def validate(self, user=None): if user: self.created_by = user super(InventoryMovement, self).validate() # make sure all children has batch number if our doc_type is NOT IN GR if not self.is_good_received() or self.cancel is not None: if any(i.batch is None for i in self.items): raise ValidationError(_("ERROR_BATCH_IS_REQUIRED")) if self.is_good_issued(): if any(i.quantity > 0 for i in self.items) and self.cancel is None: raise ValidationError( _("ERROR_GOOD_ISSUE_QUANTITY_MUST_BE_NEGATIVE")) if any(i.quantity < 0 for i in self.items) and self.cancel is not None: raise ValidationError( _("ERROR_CANCELLED_GOOD_ISSUE_QUANTITY_MUST_BE_POSITIVE")) if self.is_transfer(): if len(self.items) % 2 != 0: raise ValidationError( _("ERROR_TRANSFER_MUST_HAVE_EVEN_NUMBER_OF_ITEMS")) # Validate based on Good Received, Good Issued, SA, etc. logic InventoryContent.apply(self, True)
def confirm(self, user, confirmation, **kwargs): """ :param IntraUser user: :param AuxiliaryTaskConfirmation confirmation: :param dict kwargs: materials :return: """ if self.status >= self.STATUS_CONFIRMED: raise ValidationError( _("ERROR_TASK_STATUS_CANT_CONFIRM: %(status)s") % {'status': self.status}) if not confirmation: raise ValidationError(_("ERROR_CONFIRMATION_IS_REQUIRED")) if not isinstance(confirmation, AuxiliaryTaskConfirmation): raise ValidationError( _("ERROR_CONFIRMATION_MUST_BE_AUX_TASK_CONFIRMATION")) if not confirmation.materials: raise ValidationError(_("ERROR_MATERIAL_IS_REQUIRED")) if not self.confirmations: self.actual_start = confirmation.actual_start self.actual_duration += confirmation.actual_duration # if confirmation is marked completed, set status to confirmed if confirmation.completed: self.actual_end = confirmation.actual_end self.status = self.STATUS_CONFIRMED super(AuxiliaryTask, self).confirm(user, confirmation, **kwargs)
def create(cls, requester, authenticated_by, authentication_challenge, target_permissions): """ :param IntraUser requester: :param IntraUser authenticated_by: :param basestring authentication_challenge: :param basestring|list target_permissions: :raise ValidationError: :return Authentication: """ # sanitize input if isinstance(target_permissions, basestring): target_permissions = [target_permissions] # Validate if target user has enough permission allow such task to happen? if any(not authenticated_by.can(p) for p in target_permissions): raise ValidationError( _("ERR_AUTHENTICATE_USER_DOES_NOT_HAVE_TARGET_PERMISSION: %(target_permissions)s" ) % {'target_permissions': ",".join(target_permissions)}) # Validate password challenge if not authenticated_by.check_password(authentication_challenge): raise ValidationError(_("ERR_AUTHENTICATION_FAILED")) # Validated, success create the token o = cls() o.target_permissions = target_permissions o.requester = requester o.authorizer = authenticated_by o.created = NOW() o.save() return o
def validate(self): # FIXME: Check UOM for index, item in enumerate(self.items): material = MaterialMaster.factory(item.material) revisions = material.revisions() if revisions: revision_list = [rev.rev_id for rev in revisions] if item.revision not in revision_list: raise ValidationError( _("ERROR_REVISION_DOES_NOT_EXIST: %(material)s in item %(itemnumber)s only has revision %(revisionlist)s" ) % { 'material': item.material.code, 'itemnumber': index + 1, 'revisionlist': revision_list }) schematic = filter(lambda r: item.revision is r.rev_id, revisions)[0] if item.size and item.size not in schematic.conf_size: raise ValidationError( _("ERROR_SIZE_DOES_NOT_EXIST: %(material)s revision %(materialrevision)s in item %(itemnumber)s only has size %(sizelist)s" ) % { 'material': item.material.code, 'materialrevision': schematic.rev_id, 'itemnumber': index + 1, 'sizelist': schematic.conf_size })
def translate(input, output='label', allow_incomplete=False): if output not in ['label', 'object', 'extract']: raise BadParameterError("output format '%s' is invalid" % output) # assume default type = 'stock' (type, code) = input.split('-', 1) if '-' in input else ('stock', input) c = typed_code_factory(type, code) if c is None: raise ValidationError("Unable to parse '%s'" % code) if not allow_incomplete and output not in ['extract' ] and len(c.leftover) > 0: raise ValidationError( "Unable to parse partial of code near '%s' of %s" % (c.leftover, str(c))) if 'label' == output: return c.tail().label if 'extract' == output: return c.trail return c
def normalize(self, for_size_index): o = self.__class__() # simple stuff o.id = self.id o.process = self.process o.source = self.source[:] o.is_configurable = False o.labor_cost = self.labor_cost o.markup = self.markup o.remark = self.remark # nested stuff o.materials = map(lambda a: a.normalized(for_size_index), self.materials) # size sensitive stuff if not self.is_configurable: o.duration = self.duration[:] o.staging_duration = self.staging_duration[:] else: if not (0 <= for_size_index < len(self.duration)): raise ValidationError( _("ERR_NORMALIZE_SCHEMATIC_ENTRY_FAILED_DURATION_SIZE_INDEX_OUT_OF_RANGE" )) if not (0 <= for_size_index < len(self.staging_duration)): raise ValidationError( _("ERR_NORMALIZE_SCHEMATIC_ENTRY_FAILED_STAGING_DURATION_SIZE_INDEX_OUT_OF_RANGE" )) o.duration = [self.duration[for_size_index]] o.staging_duration = [self.staging_duration[for_size_index]] return o
def validate_pair(design_uid, code): """ Validate design_uid + code - Validate design_uid is not obligated to something else but "code" - then Validate "code" is not yet taken :param design_uid: :param code: :return: true, is already paired, false if not yet paired, throw if error """ try: o = DesignUID(design_uid) if o.code is None: # design_uid is NEW # your design_uid is not yet bind, meaning your status is not Approved yet. pass # design_uid already exists, validate if value is correct if o.code != code: raise ValidationError("%s: check_code=%s, uid_based_code=%s" % (_("ERROR_DESIGN_UID_MISMATCHED"), code, o.code)) return True except ValueError: # design_uid not exist, let's check if code is already taken or not? if DesignUID.lookup(code) is not None: raise ValidationError("%s: %s" % (_("ERROR_DESIGN_UID_ALREADY_IN_USED"), code)) return False
def append(self, code_, label_, **kwargs): if not len(code_) == self.length: raise ValidationError('Supplied code "%s" must have size of %d' % (code_, self.length)) # check if code_ already exists queried = models.LOV.objects.filter(self._get_filter() & Q(code=code_)) if queried.count() > 0: raise ValidationError('Code "%s" already exists.' % code_) models.LOV(group=self.groupValue(), code=code_, label=label_).save() return True
def cancel(self, user, **kwargs): if self.status == self.STATUS_CLOSED: raise ValidationError(_("ERROR_CANNOT_CANCEL_CLOSED_PR")) if self.status == self.STATUS_CANCELLED: raise ValidationError(_("ERROR_PR_ALREADY_CANCELLED")) self.status = self.STATUS_CANCELLED self.cancelled = doc.Event.create(doc.Event.CANCELLED, user, against=self) self.touched(user, **kwargs)
def set_delivered(self, user): if self.status < self.STATUS_CONFIRMED: raise ValidationError( _("ERROR_STORE_TASK_MUST_BE_CONFIRMED: %(status)s") % {'status': self.status}) if self.status >= self.STATUS_DELIVERED: raise ValidationError( _("ERROR_TASK_STATUS_CANT_DELIVERED: %(status)s") % {'status': self.status}) self.status = self.STATUS_DELIVERED self.touched(user)
def append(self, code_, title_, **kwargs): if not len(code_) == self.length: raise ValidationError('Supplied code "%s" must have size of %d' % (code_, self.length)) # check if code_ already exists queried = Company.objects.filter(code=code_) if queried.count() > 0: raise ValidationError('Code "%s" already exists.' % code_) comp = Company(code=code_, title=title_) if 'author' in kwargs: comp.author = kwargs['author'] comp.save() return True
def factory(cls, type, items, ref_doc=None): """ Create InventoryMovement :param type: :param items: array of tuple or list of (material_code, quantity) :param ref_doc: :return: """ # Sanitize 'items' based on 'type' if type in [cls.GI_CC, cls.GI_PD, cls.GI_SC, cls.GI_SO]: # Convert incoming tuple to InventoryMovementEntry pass elif type in [cls.GR_PD, cls.GR_PR, cls.GR_BP, cls.GR_LT]: # Convert incoming tuple to InventoryMovementEntry pass elif type in [cls.ST_LL, cls.ST_LP, cls.ST_PL, cls.ST_MM, cls.ST_LT]: # Let it go ~~ pass else: raise ValidationError( 'Factory method cannot handle document type of %s.' % type) o = cls() o.type = type o.items = items o.ref_doc = ref_doc return o
def revert(self, user): """ Revert StoreAuxTask while cancelling all the existing Confirmations. :param IntraUser user: :return: """ if self.status == self.STATUS_CONFIRMED: # Cancel all existing confirmations for conf in filter(lambda c: not c.is_cancelled(), self.confirmations): conf.cancel(cancelled_by=user) # Revert the values to original state. self.actual_duration = 0 self.actual_start = None self.actual_end = None self.status = self.STATUS_OPEN # Save it. self.touched(user) signals.task_reverted.send(self.__class__, instance=self, reverted_cotnent=[]) return True raise ValidationError( _("ERR_CANNOT_REVERT_DELIVERED_TASK: %(task_doc_no)s") % {'task_doc_no': self.doc_no})
def __init__(self, group_, length_, parent_=None): super(HierarchyLovComponent, self).__init__(group_, length_) if parent_ is not None and not isinstance(parent_, models.LOV): raise ValidationError( 'Parent provided must be models.LOV class, received %s instead' % type(parent_)) self.parent = parent_
def cancel(self, user, **kwargs): if self.cancelled: raise ValidationError(_("ERROR_TASK_IS_ALREADY_CANCELLED")) self.cancelled = doc.Event.create(doc.Event.CANCELLED, user, against=self) self.touched(user, **kwargs)
def assert_assignee(self, assignee): """ Validate if assignee as IntraUser is applicable for the task. :param assignee: :return: """ if isinstance(assignee, IntraUser): if not assignee.can("write", self.task.code): raise ValidationError( _("ERR_INVALID_ASSIGNEE: %(user)s %(task_code)s") % { 'user': self.assignee, 'task_code': self.task.code }) elif isinstance(assignee, TaskGroup): if self.task.default_assignee() is not self.assignee: raise ValidationError( _("ERR_INVALID_ASSIGNEE: %(group)s %(task_code)s") % { 'task_code': self.task.code, 'group': self.assignee })
def query_content(cls, material=None, location=None, batch=None, ref_doc=None, **kwargs): """ A thin wrapper to make a inventory content query easier :param material: :param location: :param batch: :param ref_doc: :param kwargs: :return: """ cond = { 'material': material, 'location': location, 'batch': batch, 'ref_doc': ref_doc } # sanitize # (1) remove all none attributes del_keys = [] for k in cond: if cond[k] is None: del_keys.append(k) for k in del_keys: del cond[k] # (2) String attributes for k in ['material', 'location', 'batch']: if k in cond: cond[k] = str(cond[k]) # (3) take care of ref_doc field if 'ref_doc' in cond: if isinstance(cond['ref_doc'], doc.Doc): cond['ref_doc.0'] = cond['ref_doc'].object_id cond['ref_doc.1'] = cond['ref_doc'].manager.collection_name del cond['ref_doc'] elif isinstance(cond['ref_doc'], ObjectId): cond['ref_doc.0'] = cond['ref_doc'] del cond['ref_doc'] else: raise ValidationError( _("ERR_QUERY_CONTENT_CANNOT_INTERPRET_PARAMETER: %(parameter)s %(type)s" ) % { 'parameter': 'ref_doc', 'type': type(cond['ref_doc']) }) # query return cls.manager.find(cond=cond)
def normalize(size_index, size_code): # update code # sanity check if not isinstance(master_model_code, codes.StockCode): raise ValidationError(_("ERR_CANNOT_NORMALIZE_MASTER_MODEL_CODE_SHOULD_BE_STOCK_CODE")) # update code new_code_str = master_model_code.code + size_code new_code = codes.StockCode(new_code_str) # map normalize process return new_code, map(lambda p: p.normalize(size_index), processes)
def add(self, quantity, value, weight): new_quantity = self.quantity + quantity new_value = self.value + value if new_quantity < 0: raise ValidationError( _("ERROR_INSUFFICIENT_QUANTITY: %(content_signature)s %(content_quantity)s + %(additional_quantity)s < 0" ) % { 'content_quantity': self.quantity, 'additional_quantity': quantity, 'content_signature': str(self) }) new_weight = None if weight is not None or self.weight is not None: new_weight = (self.weight or 0) + (weight or 0) if new_weight < 0: raise ValidationError( "%s: %s + %s < 0" % (_("ERROR_UNBALANCE_WEIGHT"), self.weight, weight)) self.quantity = new_quantity self.value = new_value self.weight = new_weight
def compare(pattern_, code_): if isinstance(pattern_, TypedCode): pattern_ = str(pattern_) elif isinstance(pattern_, basestring): pattern_ = pattern_ else: raise ValidationError('Unable to handle %s' % type(pattern_)) p = re.compile( '^' + re.sub(r'#+', lambda x: ('[#0-9a-z]{' + str(len(x.group(0))) + '}'), pattern_), re.IGNORECASE) return p.match( str(code_.code) if isinstance(code_, TypedCode) else code_ ) is not None
def normalized(self, for_size_index): o = self.__class__() # easy stuff o.code = self.code o.is_configurable = False o.counter = self.counter o.cost = self.cost # size sensitive stuff if not self.is_configurable: o.quantity = self.quantity[:] else: if len(self.quantity) <= for_size_index or for_size_index < 0: raise ValidationError( _("ERR_NORMALIZE_SCHEMATIC_ENTRY_FAILED_MATERIAL_QUANTITY_SIZE_INDEX_OUT_OF_RANGE" )) o.quantity = [self.quantity[for_size_index]] return o
def touched(self, user, **kwargs): """ :param IntraUser user: :param kwargs: :return: """ # Check permission if not kwargs.pop("automated", False): self.assert_permission(user, self.PERM_W, self.type) # initialisation of conditional default value if self.doc_no is not None or not self.is_new(): raise ValidationError(_("MATERIAL_MOVEMENT_IS_NOT_EDITABLE")) super(InventoryMovement, self).touched(user, **kwargs) # Post the changes to InventoryContent InventoryContent.apply(self)
def lookup(cls, design_code_with_revision, single=False): """ :param design_code_with_revision: :param single: return only one result or None :return: Array of Design object matched design_code """ if design_code_with_revision is None: raise BadParameterError("Required non-None design code string") # validate input first matches = re.compile(r'^([A-Z0-9-]+)(r(\d+))?').match(design_code_with_revision) if not matches: raise BadParameterError("Invalid design code %s" % design_code_with_revision) groups = matches.groups() design_uid = groups[0] design_rev = groups[2] design_uid = DesignUID.lookup(design_uid) if design_uid is None: raise ValidationError("Design UID %s not found." % design_uid) cond = { 'rev_unique_id': design_uid.object_id } if design_rev is not None: cond['rev_id'] = int(design_rev) r = cls.manager.find(cond=cond) if single: return r[len(r)-1] if len(r) > 0 else None else: return r
def room_dashboard(request, room_code, doc_no, action): try: room = Room.factory(room_code) except KeyError: raise BadParameterError(_("ERR_UNABLE_TO_IDENTIFY_ROOM")) if request.method == 'POST': if action is None: raise BadParameterError.required('action') if action == 'deliverable': """ POST Method, action="deliverable" Probe Job Tags for delivery candidates If condition met: all(previous_op.status == DELIVERED) and ClerkAuxTask.is_confirmed() then adjust operation status to READY. :returns [{}] deliverable """ if doc_no is None or len(doc_no) == 0: raise BadParameterError.required('production_order_doc_no') production_order = ProductionOrder.of('doc_no', doc_no) # User requested to update these task as "Ready" if production_order is None: raise BadParameterError( _("ERR_UNKNOWN_PRODUCTION_ORDER: %(doc_no)s") % {'doc_no': doc_no}) ready_operations = production_order.pending_tasks( lambda operation: operation.task in room.tasks) # Only previous operation of ready_operations where by ... # => prev_op.status == STATUS_CONFIRMED def traverse(): for ready_op in ready_operations: for prev_op in ready_op.previous_op(): if prev_op.status == ProductionOrderOperation.STATUS_CONFIRMED: yield prev_op return map( lambda a: { 'doc_no': a.doc_no, 'object_id': str(a.object_id), 'task': a.task.code }, traverse()) elif action == "delivered": """ POST Method, action="delivered" Assign Delivered status to provided doc_no, and set next_op to ready if possible... :raises BadParameter if doc_no is not ``TaskDocNo`` :returns Boolean """ if doc_no is None or len(doc_no) == 0: raise BadParameterError.required('task_doc_no') production_order_operation = ProductionOrderOperation.of( 'doc_no', doc_no) if production_order_operation is None: raise BadParameterError( _("ERR_UNKNOWN_PRODUCTION_ORDER_OPERATION_DOC_NO: %(doc_no)s" ) % {'doc_no': doc_no}) # Check if we can set this doc status to ready or not? if production_order_operation.status != ProductionOrderOperation.STATUS_CONFIRMED: raise ValidationError( _("ERR_UNABLE_TO_UPDATE_UNCONFIRMED_OPERATION_TO_DELIVERED: %(doc_no)s" ) % {'doc_no': doc_no}) # Update the status production_order_operation.status = ProductionOrderOperation.STATUS_DELIVERED production_order_operation.touched(request.user) production_order_operation.ready_next_operation_if_possible( author=request.user, context_room=room) return True raise BadParameterError( _("ERR_UNSUPPORTED_ACTION: %(action)s") % {'action': action}) if request.method == 'GET': # Query all parent task within the room results = ProductionOrderOperation.manager.find( cond={ 'planned_start': { '$lt': datetime.today().replace( hour=23, minute=59, second=59) }, '$and': [{ 'status': { '$gte': ProductionOrderOperation.STATUS_RELEASED } }, { 'status': { '$lt': ProductionOrderOperation.STATUS_CONFIRMED } }], 'task': { '$in': room.tasks } }) ref_docs = list(set(o.ref_doc[0] for o in results)) orders = ProductionOrder.manager.project({'_id': { '$in': ref_docs }}, project=['_id', 'doc_no']) # query clerk's auxiliary task status aux_tasks = ClerkAuxTask.manager.aggregate([{ "$group": { "_id": "$parent_task", "status": { "$min": "$status" } } }]) aux_tasks = dict((a['_id'], a['status']) for a in aux_tasks['result']) def process_task_output(a): r = a.serialized() # patch with previous_op statues previous_ops = [] for prev_op in a.previous_op(): previous_ops.append({ 'object_id': str(prev_op.object_id), 'task': prev_op.task.code, 'doc_no': prev_op.doc_no, 'status': prev_op.status }) r['previous_ops'] = previous_ops # patch with aux_task status key = a.object_id r['clerk_confirmed'] = aux_tasks[key] if key in aux_tasks else 0 return r return { 'orders': dict(map(lambda a: (str(a['_id']), a['doc_no']), orders)), 'tasks': map(process_task_output, results), 'activities': map(lambda a: a.as_json(), UserActiveTask.probe_all(results)) }
def extract_master_models(self, verbose=False): """ Extract all task stoppers and its first make that matched: stock-###021 (mold) :return: list of tuple(StockCode, uom, [SchematicEntry]) """ output = [] if len(self.master_modeling) <= 0: return output if verbose: print("Extraction Begun") for p in self.master_modeling: print "\t I: ", p indices = dict((p.id, p) for p in self.master_modeling) last_process = self.master_modeling[-1] ValidationError.raise_if(not is_task_stopper(last_process), _("ERROR_TASK_STOPPER_MUST_BE_LAST_PROCESS")) for final_process in reversed(filter(is_task_stopper, self.master_modeling)): if verbose: print("\t=> For %s" % final_process) # populate it buffer = [final_process] processes = [] # populate until buffer depleted while len(buffer) > 0: p = buffer.pop() processes.append(copy.deepcopy(p)) for proc_id in p.source: buffer.append(indices[proc_id]) # Clean up result # 1. Identify the final material_code (make of sprue) # 2. Place the schematic with correct placement of process. _pttrn = _TASK_STOPPER_PAIRS[str(final_process.process)] def is_target_make(m): return codes.TypedCode.compare(_pttrn, m.code) and m.quantity[0] < 0 # List all makes makes = dict(map(lambda m: (m.code, m), filter(is_target_make, processes[0].materials))) if verbose: print "\t=> output!", makes if len(makes) > 1: # if we have more than 1 make, then get only the one without pair borrows = dict(map(lambda m: (m.code, m), filter(lambda m: m.code in makes and m.quantity[0] > 0, processes[0].materials))) diff_key = set(makes) - set(borrows) map(makes.__delitem__, diff_key) # Delete all borrows items from make list # Sanity check ValidationError.raise_if(len(makes) != 1, _("ERROR_UNABLE_TO_IDENTIFY_MASTER_MODEL")) # material to create from the final process material_to_create = makes.popitem()[1] # Remove the material from the process del processes[0].materials[processes[0].materials.index(material_to_create)] # Make sure the order of process is correct processes.reverse() # TODO: Normalize existing ID/Source binding # Append to output array output.append((material_to_create.code, material_to_create.counter, processes)) if verbose: print("Done") return output
def transfer_pair_factory(cls, material, quantity, from_location, to_location, from_ref_doc=None, to_ref_doc=None): """ :param material: :param quantity: :param from_location: :param to_location: :param from_ref_doc: :param to_ref_doc: :return: cursor of InventoryMovementEntry """ # Query inventory content, FIFO batch_candidate = InventoryContent.manager.project( cond={ 'material': str(material), 'location': from_location, 'quantity': { '$gt': 0 } }, project=['_id', 'batch', 'quantity', 'value', 'weight'], sort=[("batch", 1)]) leftover = quantity usage_tuples = [] # batch, quantity, value, weight for a in batch_candidate: batch_quantity = float(a['quantity']) used = min(batch_quantity, leftover) consumption_ratio = used / batch_quantity delta_value = float(a['value']) * consumption_ratio delta_weight = float(a['weight']) * consumption_ratio leftover -= used usage_tuples.append((a['batch'], used, delta_value, delta_weight)) if leftover <= 0: break if leftover > 0: raise ValidationError( _('ERR_FAILED_TO_ALLOCATE_MATERIAL_TRANSFER_PAIR: %(material)s %(from_location)s %(to_location)s' ) % { 'material': material, 'from_location': from_location, 'to_location': to_location }) def create(): for p in usage_tuples: o = cls() o.material = material o.quantity = -p[1] o.batch = p[0] o.value = -p[2] o.weight = -p[3] o.ref_doc = from_ref_doc o.location = from_location yield o i = cls() i.material = material i.quantity = p[1] i.batch = p[0] i.value = p[2] i.weight = p[3] i.ref_doc = to_ref_doc i.location = to_location yield i return create()
def validate(self): for item in self.items: if not item.open_quantity: item.open_quantity = item.quantity elif item.open_quantity > item.quantity: raise ValidationError(_("ERROR_OPEN_QTY_MORE_THAN_QTY"))
def typed_code_factory(type, code): if type in TYPED_CODES: return TYPED_CODES[type](code) raise ValidationError('Unknown type_code type: "%s"' % type)
def validate(self): if not self.task: raise ValidationError(_("ERROR_TASK_IS_REQUIRED")) if self.assignee is None: self.assignee = self.task.default_assignee()
def check(code_): if '-' not in code_: raise ValidationError('%s is invalid' % code_)