def get_template(cls, verbose=True, **kwargs):
        def message(v, m):
            if v:
                print m

        tn = kwargs.pop('template_no', 0)
        raw_materials = kwargs.pop('raw_materials', cls.default_materials)
        raw_index = kwargs.pop('raw_index', None)
        mn = kwargs.pop('master_template', 0)
        master_model = kwargs.pop('master_model', cls.master_model)
        include_master = kwargs.pop('include_master', False)

        template = copy.deepcopy(cls.templates[tn])
        template.update(cls.base_template)
        if include_master:
            template['process'].update(
                copy.deepcopy(cls.master_model_template[mn]))
        template['design_number'] = kwargs.pop('design_number', None)
        if not template['design_number']:
            raise BadParameterError("Require design number")

        template['code'] = kwargs.pop('code', None)
        if not template['code']:
            raise BadParameterError("Require finished material code")

        template['rev_unique_id'] = kwargs.pop('rev_unique_id', None)
        if not template['rev_unique_id']:
            raise BadParameterError("Require rev_unique_id")

        template['plating'] = [kwargs.pop('plating', "plating-18")]
        template['finish'] = [kwargs.pop('finish', "finish-HP")]
        template['style'] = [kwargs.pop('style', "style-NL")]
        template['stone'] = [kwargs.pop('stone', "stone-2")]
        template['metal'] = [kwargs.pop('metal', "metal-SILV")]
        template['misc'] = kwargs.pop('misc', "Mock Data")
        template['sales_manifest']['due_date'] = time.mktime(
            kwargs.pop('due_date',
                       datetime.today().timetuple()))

        index = kwargs.pop('index', 0)
        raw_len = len(raw_materials)
        for k, v in template['process'].iteritems():
            for process in v:
                for mat in process['materials']:
                    if mat['code'] == "raw_material":
                        ind = index % raw_len
                        mat['code'] = raw_materials[ind][
                            0] if not raw_index else raw_materials[
                                raw_index[ind]][0]
                        mat['counter'] = raw_materials[ind][
                            1] if not raw_index else raw_materials[
                                raw_index[ind]][1]
                        index += 1
                    if mat['code'] == "master_model":
                        mat['code'] = master_model[0]
                        mat['counter'] = master_model[1]

        return template
Esempio n. 2
0
def material_staging_form(request):
    tasks = filter(lambda a: a, request.POST.get('tasks', '').split(','))
    o = prod_doc.MaterialStagingForm.factory(tasks)
    if o is None:
        raise BadParameterError(_("ERR_FAILED_TO_IDENTIFY_AUXILIARY_TASKS"))
    o.touched(request.user)
    return o.object_id
Esempio n. 3
0
def confirm_store_aux_task(request, id):
    """
    StoreAuxTask does NOT associate itself with UserActiveTask when it started.

    Therefore we can just confirm it with synthetic actual_data (NOW(), NOW(), 0)

    :param request:
    :param id:
    :return:
    """
    # extract incoming materials
    materials = JSONDecoder().decode(request.POST.get('materials'))

    # lookup for aux_task first
    try:
        aux_task = task_doc.StoreAuxTask(id)
    except ValueError:
        raise BadParameterError(_("ERR_INVALID_STORE_AUX_TASK: %(id)s") % {'id': id})

    # Build confirmation
    confirm = task_doc.AuxiliaryTaskConfirmation.factory(NOW(), NOW(), 0, request.user)

    # Extract materials for confirmation
    def convert_materials(material_dict):
        return task_doc.TaskComponent.factory(material_dict['material'],
                                              material_dict['revision'],
                                              material_dict['size'],
                                              material_dict['quantity'])
    confirm.materials = map(convert_materials, materials)

    # Submit to confirm
    aux_task.confirm(request.user, confirm)
    return True
Esempio n. 4
0
    def create_material_requisition(cls, user, doc_no_or_cost_center, material_list):
        """

        :param basestring doc_no_or_cost_center:
        :param [dict] material_list:
        :return:
        """
        # TODO: Add CostCenter Support
        if isinstance(doc_no_or_cost_center, Doc):
            doc = doc_no_or_cost_center
        else:
            doc = Docs.of_doc_no(doc_no_or_cost_center)

        # Check cases for ProductionOrderOperation
        if isinstance(doc, prod_doc.ProductionOrderOperation):
            # Validate ProductionOrderOperation status
            if not prod_doc.ProductionOrderOperation.STATUS_RELEASED <= doc.status <= prod_doc.ProductionOrderOperation.STATUS_PARTIAL_CONFIRMED:
                raise BadParameterError(_("ERR_INVALID_PRODUCTION_ORDER_OPERATION_STATUS"))

            def patch_weight(a):
                a['weight'] = 0
                return a

            # Call Bank's method
            # prepare material_list
            task_signals.task_repeat.send(cls, parent=doc, components=map(patch_weight, material_list))
Esempio n. 5
0
    def factory(cls, parent_task, components=None):
        target_task, duration, assignee = cls.default_parameters(parent_task)
        if target_task is None:
            return None

        if components:
            if not isinstance(components, list) or not reduce(
                    lambda x, y: isinstance(x, dict) and isinstance(y, dict),
                    components):
                raise BadParameterError("Component should be list of dict")

            components = map(
                lambda a: AuxiliaryTaskComponent.factory(
                    material=a['material'],
                    revision=a['revision'],
                    size=a['size'],
                    quantity=a['quantity'],
                    uom=a['uom'],
                    weight=a['weight']), components)
        else:
            components = filter(lambda a: a.quantity > 0,
                                parent_task.materials[:])
            components = map(AuxiliaryTaskComponent.transmute, components)

        t = cls()
        t.status = cls.STATUS_OPEN
        t.task = target_task
        t.parent_task = parent_task
        t.planned_duration = duration
        t.assignee = assignee
        t.materials = components
        if len(t.materials) <= 0:
            return None
        return t
Esempio n. 6
0
    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
Esempio n. 7
0
def task_endpoint(request, task_object_id, action):
    if action == "revert":
        task = TaskDoc.of('_id', ObjectId(task_object_id))
        # Sanity check
        if task is None:
            raise BadParameterError(
                _("ERR_UNKNOWN_DOCUMENT_ID: %(document_id)s") %
                {'document_id': task_object_id})

        if not isinstance(task, StoreAuxTask):
            raise BadParameterError(
                _("ERR_CANNOT_REVERT_TASK: %(task_type)s") %
                {'task_type': type(task)})

        return task.revert(request.user)

    raise BadParameterError(
        _("ERR_INVALID_ACTION: %(action)s") % {'action': action})
Esempio n. 8
0
    def get(cls, code):
        """
        Lookup material master by code. And raise error if such code is not found.

        :param basestring|codes.StockCode code:
        :return: MaterialMaster
        """
        mm = MaterialMaster.of('code', str(code))
        if mm is None:
            raise BadParameterError(
                _('ERROR_UNKNOWN_MATERIAL %(material_code)s') %
                {'material_code': code})
        return mm
Esempio n. 9
0
    def can(self, action_or_regex, arg=None, throw=False):
        """
        Module's permission validation

        :param basestring action_or_regex:
        :param basestring arg:
        :param bool|basestring throw: either boolean or 'challenge'
        :return bool:
        :raise InsufficientPermissionError:
        :raise BadParameterError:
        """
        def check():
            if self.is_bot:
                return True
            # Prepare permission available to compare.
            permission_info = self.permissions
            # If we have override_permission, add it in.
            if self.override_permission is not None:
                permission_info.extend(
                    self.override_permission.target_permissions)
            # Start comparing
            # Simple string case
            if isinstance(action_or_regex, basestring):
                action = ("%s@%s" % (action_or_regex, arg)
                          ) if arg is not None else action_or_regex
                if action not in permission_center:
                    # Check for non-existed permission will always returns True
                    return True
                return action in permission_info
            # Regex case
            elif isinstance(action_or_regex, PATTERN_TYPE):
                return any(action_or_regex.match(m) for m in permission_info)
            # Failed case
            raise BadParameterError(_("ERR_BAD_USAGE_OF_USER_CAN_API"))

        if not check():
            if throw == "challenge":
                required_permission = (
                    "%s@%s" % (action_or_regex,
                               arg)) if arg is not None else action_or_regex
                raise InsufficientPermissionError(required_permission)
            elif throw:
                raise BadParameterError(
                    _("ERR_INSUFFICIENT_PERMISSION: %(action)s arg=%(arg)s") %
                    {
                        'action': action_or_regex,
                        'arg': arg
                    })
            return False
        return True
Esempio n. 10
0
 def transmute(cls, input_component):
     if not isinstance(input_component, TaskComponent):
         raise BadParameterError(
             _("ERR_CANNOT_TRANSUMTE: %(input_class)s to %(output_class)s")
             % {
                 'input_class': type(input_component),
                 'output_class': cls
             })
     return cls.factory(material=input_component.material,
                        revision=input_component.revision,
                        size=input_component.size,
                        quantity=input_component.quantity,
                        uom=input_component.uom,
                        weight=None)
Esempio n. 11
0
    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
Esempio n. 12
0
    def factory(cls,
                code,
                uom=None,
                procurement_type=EXTERNAL,
                author=None,
                scrap_percentage=0):
        """
        Lookup by Code first,
            - if not exists,
                - if UoM is not supplied
                    - raise Exception
                - create a MaterialMaster
            - if exists,
                - return a fetched MaterialMaster

        :param codes.StockCode code:
        :param basestring|UOM uom:
        :param basestring procurement_type:
        :param IntraUser author:
        :param int scrap_percentage:
        :return MaterialMaster:
        """
        ProhibitedError.raise_if(not isinstance(code, codes.StockCode),
                                 "provided code must be StockCode instance")
        materials = cls.manager.find(1, 0, cond={'code': str(code)})
        # if exists
        if len(materials) > 0:
            return materials[0]

        if uom is None or author is None:
            raise ProhibitedError(
                "UOM and author must be supplied in case of creation")
        if not UOM.has(uom):
            raise BadParameterError("UOM \"%s\" is invalid" % uom)

        # Initialize Scale according to code
        if re.compile('^stock-[A-Z]{3}02[123]').match(str(code)) is not None:
            scale = 10
        else:
            scale = 1
        o = cls()
        o.code = code
        o.uom = uom
        o.procurement_type = procurement_type
        o.scrap_percentage = scrap_percentage
        o.scale = scale
        o.touched(author)
        return o
Esempio n. 13
0
    def confirm(self, user, confirmation, **kwargs):
        if not isinstance(confirmation, Confirmation):
            raise BadParameterError(
                _("ERROR_CONFIRMATION_MUST_BE_INSTANCE_OF_CONFIRMATION"))

        # for first confirmation, init array. otherwise, append it
        if len(self.confirmations) > 0:
            self.confirmations.append(confirmation)
        else:
            self.confirmations = [confirmation]

        # dispatch event
        signals.task_confirmed.send(self.__class__,
                                    instance=self,
                                    code=str(task.code),
                                    confirmation=confirmation)
        self.touched(user, **kwargs)
Esempio n. 14
0
def return_materials(request, doc_no, type):
    """
    Returning Materials - please see #return_candidates for more information.

    :param request:
    :param doc_no:
    :param type:
    :return:
    """
    # Required parameters
    return_list = JSONDecoder().decode(request.POST.get('return_list'))
    if return_list is None or len(return_list) == 0:
        raise BadParameterError.required('return_list')

    return ReturningAPI.return_materials(user=request.user,
                                         doc_no=doc_no,
                                         mode=type,
                                         return_list=return_list)
Esempio n. 15
0
 def check():
     if self.is_bot:
         return True
     # Prepare permission available to compare.
     permission_info = self.permissions
     # If we have override_permission, add it in.
     if self.override_permission is not None:
         permission_info.extend(
             self.override_permission.target_permissions)
     # Start comparing
     # Simple string case
     if isinstance(action_or_regex, basestring):
         action = ("%s@%s" % (action_or_regex, arg)
                   ) if arg is not None else action_or_regex
         if action not in permission_center:
             # Check for non-existed permission will always returns True
             return True
         return action in permission_info
     # Regex case
     elif isinstance(action_or_regex, PATTERN_TYPE):
         return any(action_or_regex.match(m) for m in permission_info)
     # Failed case
     raise BadParameterError(_("ERR_BAD_USAGE_OF_USER_CAN_API"))
Esempio n. 16
0
def activity_endpoint(request, identification, action=None):
    try:
        identification = ObjectId(identification)
    except:
        raise BadParameterError('id %s is not valid bson.ObjectId')

    operation_doc_id = identification
    # Check if operation exists
    try:
        op = TaskDoc.manager.factory('task', operation_doc_id)
    except ValueError:
        raise BadParameterError('Unknown operation %s' % operation_doc_id)

    if request.method == 'GET':
        # probe for status
        previous_op_count = len(op.previous_op())
        if op.get_confirmable_quantity() <= 0 or (
                previous_op_count == 0
                and op.status != ProductionOrderOperation.STATUS_RELEASED) or (
                    previous_op_count > 0
                    and op.status != ProductionOrderOperation.STATUS_READY):
            return False
        r = UserActiveTask.probe(operation=op)
        return r.as_json() if r is not None else None

    if request.method == 'POST':

        # identify intention
        action = 'start' if action is None or action == '' else action

        # really perform the actions
        if action == 'start':
            # user code is required from this point
            user_code = request.POST.get('user_code')
            if not user_code:
                raise BadParameterError(_('ERR_USER_CODE_REQUIRED'))
            try:
                assignee = IntraUser.objects.get(code=user_code)
            except:
                raise BadParameterError('Unknown user %s' % user_code)

            o = UserActiveTask.start(assignee=assignee,
                                     operation=op,
                                     author=request.user)
            return o.as_json()

        # from this point we will need the probed object
        activity = UserActiveTask.probe(operation=op)

        if activity is None:
            raise BadParameterError(_("ERR_ACTIVITY_NOT_YET_STARTED"))

        if action in ['pause', 'resume']:
            if action == 'pause':
                activity.pause()
            if action == 'resume':
                activity.resume()
            activity.touched(request.user)
            return activity.as_json()

        if action == 'stop':
            # Anything that the task should required upon its confirm method.
            confirmation_details = JSONDecoder().decode(
                request.POST.get('details'))

            # Stop task with details
            activity.stop(request.user, **confirmation_details)

            # Delete active task
            UserActiveTask.manager.delete(cond={'_id': activity.object_id})
            return action

    raise BadParameterError(_("ERR_INVALID_OPERATION"))
Esempio n. 17
0
    def return_candidates(cls, doc_no):
        """
        Identify if given doc_no is a supported document type.

        Retrieve the candidate list of materials that will be returned.

        Process:

            Storage fire an API request for ``return_candidates``

            Method interpret the ``doc_no`` and return the list of possible ``revert_content`` along with its
            interpretation types.

                CASE A: StoreAuxTask - with parent_task.status >= confirmed.

                    When to use?: Task has been confirmed and there are materials left after the process.

                    Meaning that the (parent) task has already been processed and confirmed. The returning content
                    should be all whatever in STAGED instance.

                    Upon return, the Complete list of items should be issued, and saved, no additional document reverted
                    in this regard.

                    List of: [
                        - type: "staged_inventory_content"
                        - doc_no: whatever given
                        - System gather items from ``inventory_content`` API.
                    ]

                CASE B: StoreAuxTask - with parent_task.status < confirmed.

                    When to use?: The Task has not yet been completed and is requested to return its materials
                    (pause, or anything)

                    Meaning that the (parent) task has not yet been started. The returning content should be those
                    STAGED recently staged items.

                    Upon return, ClerkAuxTask should be degraded, and so StoreAuxTask respectively.

                    List of: [
                        - type: "staged_from_clerk_aux_task"
                        - doc_no: whatever given
                        - System gather items from ``list_components`` method and returned to Caller.
                    ]

                CASE C: JOB TAG (Production Order)

                    When to use?: Same like case B - task is requested to returns its materials.

                    Meaning that the user wish to return some portion of associated JOB TAG. User should be presented
                    with choices to return all their items based on their selectable tasks.

                        List of: [
                            - type: "production_wip"
                            - doc_no: <task_doc_no>
                            - System examine the Edge Task, asking for WIP that may be created.
                        ]

        :param basestring doc_no:
        :return: [{type, doc_no, return_list: [{TaskComponent.as_json()}]},..]
        """
        # Retrieve documents based on what it is given
        if isinstance(doc_no, Doc):
            doc = doc_no
        else:
            doc = Docs.of_doc_no(doc_no)

        # Case StoreAuxTask
        if isinstance(doc, prod_doc.StoreAuxTask):
            if doc.status <= prod_doc.StoreAuxTask.STATUS_CONFIRMED:
                raise BadParameterError(_("ERR_INVALID_DOCUMENT_STATUS: %(doc_no)s") % {
                    'doc_no': doc_no
                })
            doc.populate('parent_task')
            if doc.parent_task:
                if prod_doc.UserActiveTask.probe(operation=doc.parent_task) is not None:
                    raise BadParameterError(_("ERR_INVALID_STATE_TASK_IN_PROGRESS: %(doc_no)s") % {
                        'doc_no': doc_no
                    })

                if doc.parent_task.status >= prod_doc.ProductionOrderOperation.STATUS_CONFIRMED:
                    staged_components = stock_doc.InventoryContent.query_content(ref_doc=doc.parent_task,
                                                                                 location=Location.factory('STAGING'))
                    return [{
                        "type": cls.TYPE_STAGED_IN_INVENTORY_CONTENT,
                        "object_id": str(doc.object_id),
                        "doc_no": doc.doc_no,
                        "return_list": map(lambda a: {
                            # As TaskComponent.as_json()
                            'material': str(a.material),
                            'quantity': a.quantity,
                            'uom': stock_doc.MaterialMaster.get(a.material).uom.code,
                            'revision': None,
                            'weight': a.weight,
                            'size': None
                        }, staged_components)
                    }]
                else:
                    staged_components = doc.list_components()
                    return [{
                        "type": cls.TYPE_STAGED_FROM_CLERK_AUX_TASK,
                        "object_id": str(doc.object_id),
                        "doc_no": doc.doc_no,
                        "return_list": map(lambda a: {
                            # As TaskComponent.as_json()
                            'material': str(a.material),
                            'quantity': a.quantity,
                            'uom': stock_doc.MaterialMaster.get(a.material).uom.code,
                            'revision': None,
                            'weight': a.weight,
                            'size': None
                        }, staged_components)
                    }]
            raise BadParameterError(_("ERR_UNKNOWN_PARENT_TASK_FOR: %(doc_no)s") % {
                'doc_no': doc_no
            })
        elif isinstance(doc, prod_doc.ProductionOrder):
            return map(lambda a: {
                "type": cls.TYPE_WIP_FROM_PRODUCTION_ORDER,
                "object_id": str(a.object_id),
                "doc_no": a.doc_no,
                "return_list": [{
                    'material': 'WIP-CANDIDATE',  # WIP CANDIDATE ...
                    'quantity': a.get_confirmable_quantity(),
                    'uom': 'pc',
                    'revision': doc.revision,
                    'weight': 0,
                    'size': doc.size
                }]
            }, doc.get_edge_operations())

        raise BadParameterError(_("ERR_UNSUPPORTED_DOCUMENT_TYPE: %(document_type)s" % {
            'document_type': str(type(doc))
        }))
Esempio n. 18
0
    def try_propagate(self, target_material, replacement_array):
        """
        Replace target_material (take/borrow only) with replacement_array (array of sized materials)

        :param target_material:
        :param replacement_array:
        :return:
        """
        # Sanity Check
        if not isinstance(target_material, codes.StockCode):
            raise BadParameterError.std(require_type=codes.StockCode)

        if not self.materials:
            return

        # all these materials can be ...
        #   - Borrow case
        #   - Take case <-- TODO: Should filter this case out.
        #   - Make case
        append_list = [
        ]  # to keep the loop safe, we will extend this self.materials once we complete the removal
        process_list = list(reversed(list(enumerate(self.materials))))
        print 'Process_list', process_list
        print '\tlooking for', target_material
        print '\tReplacement_array', replacement_array
        for sch_mat_index, sch_mat in process_list:
            if sch_mat.code == target_material:
                # Sanity check
                if sch_mat.is_configurable and len(
                        sch_mat.quantity) != len(replacement_array):
                    raise BadParameterError(
                        _("ERR_CANNOT_PROPAGATE_MATERIAL_SIZES: %(material)s")
                        % {'material': str(sch_mat.code)})
                # update append_list
                for size_index, replace_mat in enumerate(replacement_array):
                    quantities = [0] * len(replacement_array)
                    quantities[size_index] = sch_mat.quantity[
                        size_index] if sch_mat.is_configurable else sch_mat.quantity[
                            0]
                    append_list.append(
                        self.add_material(code=replace_mat,
                                          quantities=quantities,
                                          is_configurable=True,
                                          counter=sch_mat.counter,
                                          cost=sch_mat.cost,
                                          add=False))

                # delete original one
                del self.materials[sch_mat_index]
        print '\toutput append_list', append_list
        print '\tbefore', self.materials
        self.materials.extend(append_list)
        print '\tafter\n', self.materials

        # If appended, this task must be converted to configurable
        if len(append_list) > 0 and not self.is_configurable:
            self.is_configurable = True
            self.duration = map(lambda _: self.duration[0],
                                range(0, len(replacement_array)))
            self.staging_duration = map(lambda _: self.staging_duration[0],
                                        range(0, len(replacement_array)))
Esempio n. 19
0
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))
        }
Esempio n. 20
0
    def return_materials(cls, user, doc_no, mode, return_list):
        """
        Returning Materials - please see #return_candidates for more information.

        :param IntraUser user:
        :param basestring doc_no:
        :param basestring mode:
        :param list[dict[basestring, basestring]] return_list:
        :return:
        """

        # Retrieve documents based on what it is given
        doc = Docs.of_doc_no(doc_no)

        # Validate input,
        # Extract source to compare
        current_list = []
        ref_doc = None
        from_ref_doc = None
        if mode in [cls.TYPE_STAGED_IN_INVENTORY_CONTENT, cls.TYPE_STAGED_FROM_CLERK_AUX_TASK] and isinstance(doc, prod_doc.StoreAuxTask):
            o = cls.return_candidates(doc_no)
            current_list = list(o[0]['return_list'])
            doc.populate('parent_task')
            doc.parent_task.populate('ref_doc')
            ref_doc = doc.parent_task.ref_doc
            from_ref_doc = doc.parent_task
        elif mode == cls.TYPE_WIP_FROM_PRODUCTION_ORDER and isinstance(doc, prod_doc.ProductionOrderOperation):
            doc.populate('ref_doc')
            current_list = [{
                'material': 'WIP-CANDIDATE',
                'quantity': doc.get_confirmable_quantity(),
                'uom': doc.ref_doc.uom,
                'revision': None,
                'size': None,
                'weight': 0,
            }]
            ref_doc = doc.ref_doc
        else:
            # Unsupported case
            raise BadParameterError(_("ERR_UNSUPPORTED_DOCUMENT_TYPE: %(document_type)s") % {
                'document_type': str(type(doc))
            })

        # prepare movement based on return_list
        # Sanity check
        if len(current_list) != len(return_list):
            raise BadParameterError(_("ERR_UNEQUAL_SIZE_OF_RETURN_LIST: %(expected_size)s != $(actual_size)s") % {
                'expected_size': len(current_list),
                'actual_size': len(return_list)
            })

        # index list for ease of comparison
        current_list = dict(map(lambda a: (str(a['material']), a), current_list))
        return_list = dict(map(lambda a: (str(a['material']), a), return_list))

        movements = []
        entries = []
        lost_entries = []
        movement_type = None
        lost_movement_type = None
        # From STAGED => STORE
        # From STAGED => LOST & FOUND
        for key, value in current_list.iteritems():
            lost = value['quantity'] - return_list[key]['quantity']

            # NOTE: For WIP scenario
            if key == 'WIP-CANDIDATE':
                # init Goods receipt by-product for WIP to STORE
                # TODO: value for WIP to be fixed later with proper logic. now using static 543210.
                movement_type = stock_doc.InventoryMovement.GR_BP
                user.can(stock_doc.InventoryMovement.ACTION_WRITE(), movement_type, throw=True)

                # NOTE: Generate WIP Stock code
                wip_material = doc.ref_doc.material.create_wip(doc.task.code)

                # NOTE: Create material master from stock code
                stock_doc.MaterialMaster.factory(wip_material, uom=doc.ref_doc.uom, procurement_type=stock_doc.MaterialMaster.INTERNAL, author=user)

                current_list[key]['material'] = wip_material
                current_list[key]['uom'] = stock_doc.MaterialMaster.get(doc.ref_doc.material).uom.code
                entries.append(stock_doc.InventoryMovementEntry.factory(material=wip_material,
                                                                        quantity=return_list[key]['quantity'],
                                                                        location=Location.factory('STORE').code,
                                                                        weight=return_list[key]['weight'],
                                                                        value=543210))

                if lost > 0:
                    # if lost is found, init Goods receipt lost and found for WIP to STORE
                    lost_movement_type = stock_doc.InventoryMovement.GR_LT
                    lost_entries.append(stock_doc.InventoryMovementEntry.factory(material=wip_material,
                                                                                 quantity=return_list[key]['quantity'],
                                                                                 location=Location.factory('LOST').code,
                                                                                 weight=return_list[key]['weight'],
                                                                                 value=543210))
            # NOTE: For STAGING scenario
            else:
                # init Stock transfer production to location for STAGING to STORE
                movement_type = stock_doc.InventoryMovement.ST_PL
                user.can(stock_doc.InventoryMovement.ACTION_WRITE(), movement_type, throw=True)
                entries.extend(stock_doc.InventoryMovementEntry.transfer_pair_factory(material=value['material'],
                                                                                      quantity=return_list[key]['quantity'],
                                                                                      from_location=Location.factory('STAGING').code,
                                                                                      to_location=Location.factory('STORE').code,
                                                                                      from_ref_doc=from_ref_doc))

                if lost > 0:
                    # if lost is found, init Stock transfer lost and found for STAGING to STORE retaining ref_doc
                    lost_movement_type = stock_doc.InventoryMovement.ST_LT
                    lost_entries.extend(stock_doc.InventoryMovementEntry.transfer_pair_factory(material=value['material'],
                                                                                               quantity=return_list[key]['quantity'],
                                                                                               from_location=Location.factory('STAGING').code,
                                                                                               to_location=Location.factory('LOST').code,
                                                                                               from_ref_doc=from_ref_doc,
                                                                                               to_ref_doc=from_ref_doc))
        if not entries:
            raise ValueError("Nothing to return")

        movement = stock_doc.InventoryMovement.factory(movement_type, list(entries), ref_doc=ref_doc)
        movements.append(movement)

        if lost > 0:
            user.can(stock_doc.InventoryMovement.ACTION_WRITE(), lost_movement_type, throw='challenge')
            movement = stock_doc.InventoryMovement.factory(lost_movement_type, list(lost_entries), ref_doc=ref_doc)
            movements.append(movement)

        if movements:
            # validate first then touched
            map(lambda x: x.validate(user=user), movements)

            # NOTE: create a new pair of store & clerk with different parent depending on mode
            if mode == cls.TYPE_STAGED_FROM_CLERK_AUX_TASK:
                task_signals.task_repeat.send(cls, parent=doc.parent_task, components=[v for i, v in current_list.iteritems()])
            if mode == cls.TYPE_WIP_FROM_PRODUCTION_ORDER:
                task_signals.task_repeat.send(cls, parent=doc, components=[v for i, v in current_list.iteritems()])
                doc.ready(user)
            
            map(lambda x: x.touched(user), movements)

        return len(movements)