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 @@
+
@@ -152,11 +153,12 @@
+
-
Components
-
-
+
Components
+
+
@@ -168,9 +170,9 @@
-
Finished Products
-
-
+
Finished Products
+
+
diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py
index 27a20724746..5a0601f5ec9 100644
--- a/addons/mrp/wizard/mrp_product_produce.py
+++ b/addons/mrp/wizard/mrp_product_produce.py
@@ -109,34 +109,6 @@ class MrpProductProduce(models.TransientModel):
# Check all the product_produce line have a move id (the user can add product
# to consume directly in the wizard)
for line in self._workorder_line_ids():
- if not line.move_id:
- # Find move_id that would match
- if line.raw_product_produce_id:
- moves = self.move_raw_ids
- else:
- moves = self.move_finished_ids
- move_id = moves.filtered(lambda m: m.product_id == line.product_id and m.state not in ('done', 'cancel'))
- if not move_id:
- # create a move to assign it to the line
- if line.raw_product_produce_id:
- values = {
- 'name': self.production_id.name,
- 'reference': self.production_id.name,
- 'product_id': line.product_id.id,
- 'product_uom': line.product_uom_id.id,
- 'location_id': self.production_id.location_src_id.id,
- 'location_dest_id': line.product_id.property_stock_production.id,
- 'raw_material_production_id': self.production_id.id,
- 'group_id': self.production_id.procurement_group_id.id,
- 'origin': self.production_id.name,
- 'state': 'confirmed',
- 'company_id': self.production_id.company_id.id,
- }
- else:
- values = self.production_id._get_finished_move_value(line.product_id.id, 0, line.product_uom_id.id)
- move_id = self.env['stock.move'].create(values)
- line.move_id = move_id.id
-
line._check_line_sn_uniqueness()
# because of an ORM limitation (fields on transient models are not
# recomputed by updates in non-transient models), the related fields on