[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
This commit is contained in:
William Henrotin
2019-11-22 12:39:51 +00:00
parent 9412b74986
commit e1f518bb83
5 changed files with 80 additions and 47 deletions
@@ -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()
+24 -13
View File
@@ -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
+21
View File
@@ -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'
+8 -6
View File
@@ -105,6 +105,7 @@
<field name="working_state" invisible="1"/>
<field name="production_state" invisible="1"/>
<field name="allowed_lots_domain" invisible="1"/>
<field name="is_finished_lines_editable" invisible="1"/>
<header>
<button name="button_finish" type="object" string="Finish Order" attrs="{'invisible': ['|', ('state', '!=', 'progress'), ('is_produced', '=', False)]}" class="btn-info"/>
<button name="button_start" type="object" string="Start Working" attrs="{'invisible': ['|', ('working_state', '=', 'blocked'), ('state', '!=', 'pending')]}"/>
@@ -152,11 +153,12 @@
<field name="company_id" invisible="1"/>
<field name="finished_lot_id" context="{'default_product_id': product_id, 'default_company_id': company_id}" attrs="{'invisible': [('product_tracking', '=', 'none')]}" groups="stock.group_production_lot"/>
<field name="product_tracking" invisible="1"/>
<field name="consumption" invisible="1"/>
</group>
</group>
<h4 attrs="{'invisible': [('raw_workorder_line_ids', '=', [])]}">Components</h4>
<field name="raw_workorder_line_ids" attrs="{'invisible': [('raw_workorder_line_ids', '=', [])], 'readonly': [('state', 'in', ('cancel', 'done'))]}">
<tree editable="bottom" create="0" delete="0">
<h4 attrs="{'invisible': ['&amp;', ('consumption', '=', 'strict'), ('raw_workorder_line_ids', '=', [])]}">Components</h4>
<field name="raw_workorder_line_ids" attrs="{'invisible': ['&amp;', ('consumption', '=', 'strict'), ('raw_workorder_line_ids', '=', [])], 'readonly': [('state', 'in', ('cancel', 'done'))]}">
<tree editable="bottom" create="1" delete="0">
<field name="product_id"/>
<field name="product_tracking" invisible="1"/>
<field name="company_id" invisible="1"/>
@@ -168,9 +170,9 @@
<field name="move_id" invisible="1"/>
</tree>
</field>
<h4 attrs="{'invisible': [('finished_workorder_line_ids', '=', [])]}">Finished Products</h4>
<field name="finished_workorder_line_ids" attrs="{'invisible': [('finished_workorder_line_ids', '=', [])], 'readonly': [('state', 'in', ('cancel', 'done'))]}">
<tree editable="bottom" create="0" delete="0">
<h4>Finished Products</h4>
<field name="finished_workorder_line_ids" attrs="{'readonly': [('is_finished_lines_editable', '=', False)]}">
<tree editable="bottom" create="1" delete="0">
<field name="product_id"/>
<field name="product_tracking" invisible="1"/>
<field name="company_id" invisible="1"/>
-28
View File
@@ -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