Пример #1
0
    def kickoff(cls, user, materials=None):
        """
        Create new MRP session, extracting existing data

        :param materials - list of tuple [(stock_code, revision, size)]
        :return: MRPSession Object
        """
        # Check is_running
        if cls.running_session() is not None:
            raise err.ProhibitedError('There is MRP Session running.')

        if not isinstance(materials, (type(None), list)):
            raise err.ProhibitedError(
                'Bad value - materials must be list of tuple size of 3')

        session = MRPSession()
        session.target_materials = materials
        session.issued_by = user

        key = str(session.object_id)
        LOG.debug("Running MRP Session=%s" % key)
        t = multiprocessing.Process(target=MRPSession.run, args=(session, ))
        cls.processes[key] = t
        t.start()
        return session
Пример #2
0
 def _group_id(dt):
     if lz == MaterialMaster.LZ_DAILY:
         return dt.date()
     elif lz == MaterialMaster.LZ_MONTHLY:
         return "%04d%02d" % (dt.year, dt.month)
     elif lz == MaterialMaster.LZ_WEEKLY:
         return "%04d%02d" % (dt.year,
                              dt.date().isocalendar()[1])
     else:
         raise err.ProhibitedError('Invalid lot_size value=%s' %
                                   lot_size)
Пример #3
0
 def populate(self, path):
     if path == 'material_master' and self.material_master is None and self.material is not None:
         key = str(self.material)
         mms = MaterialMaster.manager.find(cond={'code': key}, pagesize=1)
         if len(mms) == 0:
             raise err.ProhibitedError('Material Master %s is missing' %
                                       key)
         self.material_master = mms[0]  # type: MaterialMaster
         self.mrp_type = self.material_master.mrp_type
         self.reorder_point = 0 if self.material_master == MaterialMaster.MRP else self.material_master.reorder_point
         self.lead_time = self.material_master.gr_processing_time
         self.procurement_type = self.material_master.procurement_type
     else:
         super(MRPSessionExecutionRecord, self).populate(path)
     return self
Пример #4
0
    def create_demand_groups(self):
        lot_size = self.material_master.lot_size
        if lot_size in [
                MaterialMaster.LZ_LOT_FOR_LOT,
                MaterialMaster.LZ_MAX_STOCK_LEVEL
        ]:

            def lot_for_lot():
                r = []
                for a in self.entries:
                    if a.marker.date() in [
                            MRPSessionExecutionRecordEntry.
                            initial_balance_marker(),
                            MRPSessionExecutionRecordEntry.safety_stock_marker(
                            )
                    ]:
                        r.append(a)
                    else:
                        if len(r) > 0:
                            yield None, r
                            r = []
                        yield a.marker, [a]

            return lot_for_lot()
        elif lot_size in [
                MaterialMaster.LZ_DAILY, MaterialMaster.LZ_MONTHLY,
                MaterialMaster.LZ_WEEKLY
        ]:

            def create_timed_lot_size(lz):
                def _group_id(dt):
                    if lz == MaterialMaster.LZ_DAILY:
                        return dt.date()
                    elif lz == MaterialMaster.LZ_MONTHLY:
                        return "%04d%02d" % (dt.year, dt.month)
                    elif lz == MaterialMaster.LZ_WEEKLY:
                        return "%04d%02d" % (dt.year,
                                             dt.date().isocalendar()[1])
                    else:
                        raise err.ProhibitedError('Invalid lot_size value=%s' %
                                                  lot_size)

                def timed_lot_size():
                    group_point = None
                    r = []
                    for e in self.entries:
                        if group_point is None:
                            # do not yield the automatic ones
                            group_point = _group_id(e.marker)
                            r.append(e)
                        elif group_point != _group_id(e.marker):
                            yield group_point, r
                            # renew
                            group_point = _group_id(e.marker)
                            r = [e]
                        else:
                            r.append(e)
                    if len(r) > 0:
                        yield group_point, r

                return timed_lot_size

            return create_timed_lot_size(lot_size)()
        raise err.ProhibitedError('Unknown lot_size %s' % lot_size)
Пример #5
0
    def run(self):
        self._begin()
        try:

            def report(message, group=None, level=0):
                if group is None:
                    LOG.debug("%s%s" % ("\t" * level, message))
                else:
                    LOG.debug("%s%s: %s" % ("\t" * level, group, message))

            report("\nBuilding Run Sequences")
            self._build_run_sequences()
            report("\nInitial sequence" % self.sequence)
            if len(self.sequence) == 0:
                report("(No sequence initialized)")
            else:
                for s in self.sequence:
                    report(s, "i", 1)
                report("")

            # Run sequence; Execute mrp
            for seq in self.sequence:

                # Read Material
                # => update reorder_point, lead_time, lot_size -> making 'create_supply()'
                seq.populate('material_master')

                # Extract attributes
                reorder_point = seq.reorder_point
                procurement_type = seq.material_master.procurement_type
                lot_size = seq.material_master.lot_size
                mrp_type = seq.material_master.mrp_type

                report("Started %s" % seq)
                report("reorder_point=%s" % reorder_point, "i", 1)
                report("lot_size=%s" % lot_size, "i", 1)
                report(
                    "supply_type=%s" %
                    ('PurchaseRequisition' if procurement_type
                     == MaterialMaster.EXTERNAL else 'ProductionOrder'), 'i',
                    1)

                # Type = 'MRP' or 'Reorder', if 'No MRP' = Skip
                if mrp_type not in [
                        MaterialMaster.MRP, MaterialMaster.REORDER
                ]:
                    raise err.ProhibitedError(
                        'Unable to process MRP Sequence %s - incorrect MRP type'
                        % seq.material)

                # Delete weak supply
                seq.delete_weak_supplies(verbose=report)

                # Gathering Demand/Supply
                seq.gather(verbose=report)
                report("-", "i", 1)

                # Go through Demand/Supply in chronological order
                # Try to resolve negative balance situation.
                current = 0
                for group_id, a in seq.create_demand_groups():
                    first_marker = a[0].marker
                    # print demand
                    report("marker=%s group_id=%s" % (first_marker, group_id),
                           "O", 1)
                    # e = MRPSessionExecutionRecordEntry
                    for e in a:
                        current += e.quantity
                        report("%s\t= %s" % (e, current), level=2)
                    # For Every demand group, we need to produce a supply for it.
                    # TODO: Each demand group might have a supply within that will in turn misled the calculation.
                    if current <= seq.reorder_point:
                        reorder_amount = current - seq.reorder_point

                        # MAX_STOCK_LEVEL
                        if lot_size == MaterialMaster.LZ_MAX_STOCK_LEVEL:
                            max_stock_level = seq.material_master.lot_size_arg
                            if max_stock_level <= 0 or max_stock_level is None:
                                raise err.BadParameterError(
                                    'Invalid lot_size_arg for material %s' %
                                    seq.material)
                            reorder_amount = current - max_stock_level

                        # IF supply is needed
                        if reorder_amount < 0:
                            replenished = seq.create_supply(-reorder_amount,
                                                            first_marker,
                                                            self,
                                                            ref_doc=e.ref_docs,
                                                            remark=e.remark,
                                                            verbose=report)
                            current += replenished
                            # create sequence handle this replenishing
                            # just for printing sake
                            rec = MRPSessionExecutionRecordEntry.create(
                                marker=first_marker,
                                quantity=replenished,
                                ref_docs=e.ref_docs,
                                remark=e.remark,
                                original=False)
                            seq.created_supplies.append(rec)
                            report("%s\t= %s %s" % (rec, current, rec.remark),
                                   level=2)
                    else:
                        report("no shortage found", "i", 1)
                report("DONE\n", "i", level=1)

            self._enclose()
        except Exception as e:
            LOG.error(traceback.format_exc())
            self._enclose(False, [str(e)])
Пример #6
0
    def create_supply(self,
                      shortage,
                      due,
                      session,
                      ref_doc=None,
                      remark=None,
                      verbose=None):
        """
        Modify add supply to sequence, and return replenished amount

        Replenished amount is ...
            shortage + lot_size_arg if lot_size = MaterialMaster.LZ_MAX_STOCK_LEVEL
            shortage if otherwise

        Supply Sequence can be broken into lots using lot_size_max

        Each sub_lot_size must be greater or equals to lot_size_min

        lead_time is taken into consideration as a due

        due is given as exact moment when it will be used, therefore it will be offset by 1 hours before
        such exact hours. (Please, Consider exclude OffHours as optional feature)

        :param int shortage:
        :param due:
        :param MRPSession session:
        :param ref_doc:
        :param basestring remark:
        :param verbose:
        :return: (amount replenished)
        """
        if shortage == 0:
            return 0

        if self.lead_time is None or self.reorder_point is None:
            raise err.ProhibitedError('Cannot create supply without lead_time')
        offset_due = due - timedelta(hours=1, days=self.lead_time)

        # Compute optimal lot_size
        procurement_type = self.material_master.procurement_type
        lots = []
        lot_size_max = self.material_master.lot_size_max
        lot_size_min = self.material_master.lot_size_min
        # lot_size_arg = self.material_master.lot_size_arg
        # Capped with lot_size_max
        if lot_size_max is not None and lot_size_max > 0:
            lot_count = int(shortage) / int(lot_size_max)
            supplied = 0
            for i in xrange(0, lot_count - 1):
                lots.append(lot_size_max)
                supplied += lot_size_max
            lots.append(shortage - supplied)
        else:
            lots = [shortage]

        # Push to lot_size_min
        if lot_size_min is not None and lot_size_min > 0:
            lots = map(lambda a: max(lot_size_min, a), lots)

        # Actually create supply
        references = []
        if procurement_type == MaterialMaster.INTERNAL:
            # ProductionOrder

            # Need to honour scrap percentage here
            scrap_percentage = self.material_master.scrap_percentage
            references.extend(
                map(
                    lambda a: ProductionOrder.factory(
                        self.material,
                        self.revision,
                        self.size,
                        offset_due,
                        a,
                        session.issued_by,
                        ref_doc=ref_doc,
                        remark=remark,
                        scrap_percentage=scrap_percentage,
                        mrp_session_id=session.object_id), lots))
        elif procurement_type == MaterialMaster.EXTERNAL:
            # PurchaseRequisition
            # FIXME: Consider adding remark/ref_doc to the output document.
            references.extend(
                map(
                    lambda a: PurchaseRequisition.factory(
                        self.material,
                        self.revision,
                        self.size,
                        offset_due,
                        a,
                        session.issued_by,
                        mrp_session_id=session.object_id), lots))

        verbose("due=%s lots=%s remark=%s" % (due, lots, remark), "S", 1)
        return reduce(lambda o, a: o + a, lots, 0)