From e1f518bb83ccaa2abf08bc870bc1c7345e85971c Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Tue, 22 Oct 2019 14:46:39 +0000 Subject: [PATCH] [IMP] mrp: add components in workorders This commit extend the flexible consumption. On the workorder form view. It's now possible to add directly new workorder line with any product (even one not in any BoM line). For those new wo line. A confirmed stock move is create in the record_production step. Task : 2082448 --- addons/mrp/models/mrp_abstract_workorder.py | 27 +++++++++++++++ addons/mrp/models/mrp_production.py | 37 +++++++++++++-------- addons/mrp/models/mrp_workorder.py | 21 ++++++++++++ addons/mrp/views/mrp_workorder_views.xml | 14 ++++---- addons/mrp/wizard/mrp_product_produce.py | 28 ---------------- 5 files changed, 80 insertions(+), 47 deletions(-) diff --git a/addons/mrp/models/mrp_abstract_workorder.py b/addons/mrp/models/mrp_abstract_workorder.py index 07fb61a4336..3e535d3d2a6 100644 --- a/addons/mrp/models/mrp_abstract_workorder.py +++ b/addons/mrp/models/mrp_abstract_workorder.py @@ -277,6 +277,10 @@ class MrpAbstractWorkorder(models.AbstractModel): """ Once the production is done. Modify the workorder lines into stock move line with the registered lot and quantity done. """ + # add missing move for extra component/byproduct + for line in self._workorder_line_ids(): + if not line.move_id: + line._set_move_id() # Before writting produce quantities, we ensure they respect the bom strictness self._strict_consumption_check() vals_list = [] @@ -500,6 +504,29 @@ class MrpAbstractWorkorderLine(models.AbstractModel): if duplicates: raise UserError(message) + def _set_move_id(self): + """ Check the line has a stock_move on which on the quantity will be + transfered at the end of the production """ + self.ensure_one() + # Find move_id that would match + mo = self._get_production() + if self[self._get_raw_workorder_inverse_name()]: + moves = mo.move_raw_ids + else: + moves = mo.move_finished_ids + move_id = moves.filtered(lambda m: m.product_id == self.product_id and m.state not in ('done', 'cancel')) + if not move_id: + # create a move to assign it to the line + if self[self._get_raw_workorder_inverse_name()]: + values = mo._get_move_raw_values(self.product_id, self.qty_done, self.product_uom_id) + elif self.product_id != mo.product_id: + values = mo._get_finished_move_value(self.product_id.id, self.qty_done, self.product_uom_id.id) + else: + # The line is neither a component nor a byproduct + return + move_id = self.env['stock.move'].create(values) + self.move_id = move_id.id + def _unreserve_order(self): """ Unreserve line with lower reserved quantity first """ self.ensure_one() diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 1acbe32a3dc..e7547f22e34 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -574,31 +574,35 @@ class MrpProduction(models.Model): if bom_line.child_bom_id and bom_line.child_bom_id.type == 'phantom' or\ bom_line.product_id.type not in ['product', 'consu']: continue - moves.append(production._get_move_raw_values(bom_line, line_data)) + operation = bom_line.operation_id.id or line_data['parent_line'] and line_data['parent_line'].operation_id.id + moves.append(production._get_move_raw_values( + bom_line.product_id, + line_data['qty'], + bom_line.product_uom_id, + operation, + bom_line + )) return moves - def _get_move_raw_values(self, bom_line, line_data): - quantity = line_data['qty'] - # alt_op needed for the case when you explode phantom bom and all the lines will be consumed in the operation given by the parent bom line - alt_op = line_data['parent_line'] and line_data['parent_line'].operation_id.id or False + def _get_move_raw_values(self, product_id, product_uom_qty, product_uom, operation_id=False, bom_line=False): source_location = self.location_src_id data = { - 'sequence': bom_line.sequence, + 'sequence': bom_line.sequence if bom_line else 10, 'name': self.name, 'reference': self.name, 'date': self.date_planned_start, 'date_expected': self.date_planned_start, - 'bom_line_id': bom_line.id, + 'bom_line_id': bom_line.id if bom_line else False, 'picking_type_id': self.picking_type_id.id, - 'product_id': bom_line.product_id.id, - 'product_uom_qty': quantity, - 'product_uom': bom_line.product_uom_id.id, + 'product_id': product_id.id, + 'product_uom_qty': product_uom_qty, + 'product_uom': product_uom.id, 'location_id': source_location.id, 'location_dest_id': self.product_id.with_company(self.company_id).property_stock_production.id, 'raw_material_production_id': self.id, 'company_id': self.company_id.id, - 'operation_id': bom_line.operation_id.id or alt_op, - 'price_unit': bom_line.product_id.standard_price, + 'operation_id': operation_id, + 'price_unit': product_id.standard_price, 'procure_method': 'make_to_stock', 'origin': self.name, 'state': 'draft', @@ -630,7 +634,14 @@ class MrpProduction(models.Model): move[0].unlink() return self.env['stock.move'], old_qty, quantity else: - move_values = self._get_move_raw_values(bom_line, line_data) + operation = bom_line.operation_id.id or line_data['parent_line'] and line_data['parent_line'].operation_id.id + move_values = self._get_move_raw_values( + bom_line.product_id, + line_data['qty'], + bom_line.product_uom_id, + operation, + bom_line + ) move = self.env['stock.move'].create(move_values) return move, 0, quantity diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index af018139036..b2fac788733 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -143,6 +143,7 @@ class MrpWorkorder(models.Model): finished_workorder_line_ids = fields.One2many('mrp.workorder.line', 'finished_workorder_id', string='By-products') allowed_lots_domain = fields.One2many(comodel_name='stock.production.lot', compute="_compute_allowed_lots_domain") + is_finished_lines_editable = fields.Boolean(compute='_compute_is_finished_lines_editable') # Both `date_planned_start` and `date_planned_finished` are related fields on `leave_id`. Let's say # we slide a workorder on a gantt view, a single call to write is made with both @@ -164,6 +165,14 @@ class MrpWorkorder(models.Model): 'date_to': date_to, }) + @api.depends('state') + def _compute_is_finished_lines_editable(self): + for workorder in self: + if self.user_has_groups('mrp.group_mrp_byproducts') and workorder.state not in ('cancel', 'done'): + workorder.is_finished_lines_editable = True + else: + workorder.is_finished_lines_editable = False + @api.onchange('finished_lot_id') def _onchange_finished_lot_id(self): """When the user changes the lot being currently produced, suggest @@ -663,6 +672,18 @@ class MrpWorkorderLine(models.Model): finished_workorder_id = fields.Many2one('mrp.workorder', 'Finished Product for Workorder', ondelete='cascade') + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + for line in res: + wo = line.raw_workorder_id + if wo and\ + wo.consumption == 'strict' and\ + wo.state == 'progress' and\ + line.product_id.id not in wo.production_id.bom_id.bom_line_ids.product_id.ids: + raise UserError(_('You cannot consume additional component as the consumption defined on the Bill of Material is set to "strict"')) + return res + @api.model def _get_raw_workorder_inverse_name(self): return 'raw_workorder_id' diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml index f63282751d1..da281ed27cf 100644 --- a/addons/mrp/views/mrp_workorder_views.xml +++ b/addons/mrp/views/mrp_workorder_views.xml @@ -105,6 +105,7 @@ +