From bf65cd83ab6309b5d013122a12ae8ec2bc112316 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 29 Mar 2018 09:40:05 +0200 Subject: [PATCH] [IMP] mrp: add check for unproduced finished lots - Create a Finished Product tracked by SN - Add a BoM with routing (in several steps) to that product: component A should be consumed in 1st step and component B in 2nd step - Create a MO, plan it, go on the first WO and produce the finished product with SN 0001 - Then go on the second WO and produce the finished product with SN 0002 - Back on your MO, you'll see that Finished Product = SN 0002 Component A has been consumed for SN 0001 Component B has been consumed for SN 0002 - Mark the MO as done No blocking error is raised so I'm consuming a product for a finished product I'm not really producing. Traceability report will be wrong too. We raise a blocking error to avoid this use case. opw-1832098 Co-authored-by: Nicolas Martinelli --- addons/mrp/models/mrp_production.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index ea729f7b214..5d2bb40f5ad 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -532,6 +532,22 @@ class MrpProduction(models.Model): workorder._generate_lot_ids() return workorders + def _check_lots(self): + # Check that the raw materials were consumed for lots that we have produced. + if self.product_id.tracking != 'none': + finished_lots = set(self.finished_move_line_ids.mapped('lot_id')) + raw_finished_lots = set(self.move_raw_ids.mapped('move_line_ids.lot_produced_id')) + if not (raw_finished_lots <= finished_lots): + lots_short = raw_finished_lots - finished_lots + error_msg = _( + 'Some raw materials were produced for a lot without finished product. ' + 'You can correct the following components by unlocking:\n' + ) + move_lines = self.move_raw_ids.mapped('move_line_ids').filtered(lambda x: x.lot_produced_id in lots_short) + for ml in move_lines: + error_msg += ml.product_id.display_name + ' (' + ml.lot_produced_id.name +')\n' + raise UserError(error_msg) + @api.multi def action_cancel(self): """ Cancels production order, unfinished stock moves and set procurement @@ -584,6 +600,7 @@ class MrpProduction(models.Model): for wo in self.workorder_ids: if wo.time_ids.filtered(lambda x: (not x.date_end) and (x.loss_type in ('productive', 'performance'))): raise UserError(_('Work order %s is still running') % wo.name) + self._check_lots() self.post_inventory() moves_to_cancel = (self.move_raw_ids | self.move_finished_ids).filtered(lambda x: x.state not in ('done', 'cancel')) moves_to_cancel._action_cancel()