[IMP] mrp: Add or update components in confirmed state
This commit let the manufacturing user to add new components or update the quantity to consume of existing ones in a manufacturing order even if the state confirmed. The BoM should be in flexible consumption. In case of reserved quantity, updating the quantity on a raw move will unreserve it entirely. We apply the same logic than the stock.picking one Task : 2121714
This commit is contained in:
@@ -222,6 +222,13 @@ class MrpProduction(models.Model):
|
||||
picking_ids = fields.Many2many('stock.picking', compute='_compute_picking_ids', string='Picking associated to this manufacturing order')
|
||||
delivery_count = fields.Integer(string='Delivery Orders', compute='_compute_picking_ids')
|
||||
confirm_cancel = fields.Boolean(compute='_compute_confirm_cancel')
|
||||
consumption = fields.Selection([
|
||||
('strict', 'Strict'),
|
||||
('flexible', 'Flexible')],
|
||||
required=True,
|
||||
readonly=True,
|
||||
default='strict',
|
||||
)
|
||||
|
||||
mrp_production_child_count = fields.Integer("Number of generated MO", compute='_compute_mrp_production_child_count')
|
||||
mrp_production_source_count = fields.Integer("Number of source MO", compute='_compute_mrp_production_source_count')
|
||||
@@ -529,7 +536,7 @@ class MrpProduction(models.Model):
|
||||
if production.workorder_ids and not self.env.context.get('force_date', False):
|
||||
raise UserError(_('You cannot move a planned manufacturing order.'))
|
||||
if 'move_raw_ids' in vals and production.state != 'draft':
|
||||
production.move_raw_ids.filtered(lambda m: m.state == 'draft')._action_confirm()
|
||||
production._autoconfirm_production()
|
||||
if not production.routing_id and vals.get('date_planned_start') and not vals.get('date_planned_finished'):
|
||||
new_date_planned_start = fields.Datetime.to_datetime(vals.get('date_planned_start'))
|
||||
if not production.date_planned_finished or new_date_planned_start >= production.date_planned_finished:
|
||||
@@ -711,6 +718,22 @@ class MrpProduction(models.Model):
|
||||
return 'assigned'
|
||||
return 'confirmed'
|
||||
|
||||
def _autoconfirm_production(self):
|
||||
"""Automatically run `action_confirm` on `self`.
|
||||
|
||||
If the production has one of its move was added after the initial call
|
||||
to `action_confirm`.
|
||||
"""
|
||||
moves_to_confirm = self.env['stock.move']
|
||||
for production in self:
|
||||
if production.state in ('done', 'cancel'):
|
||||
continue
|
||||
moves_to_confirm |= (production.move_raw_ids | production.move_finished_ids).filtered(
|
||||
lambda move: move.state == 'draft' and move.additional
|
||||
)
|
||||
if moves_to_confirm:
|
||||
moves_to_confirm._action_confirm()
|
||||
|
||||
def action_view_mrp_production_childs(self):
|
||||
self.ensure_one()
|
||||
mrp_production_ids = self.procurement_group_id.stock_move_ids.created_production_id.ids
|
||||
@@ -754,6 +777,7 @@ class MrpProduction(models.Model):
|
||||
def action_confirm(self):
|
||||
self._check_company()
|
||||
for production in self:
|
||||
production.consumption = production.bom_id.consumption
|
||||
if not production.move_raw_ids:
|
||||
raise UserError(_("Add some materials to consume before marking this MO as to do."))
|
||||
for move_raw in production.move_raw_ids:
|
||||
|
||||
@@ -151,8 +151,11 @@ class StockMove(models.Model):
|
||||
defaults = super(StockMove, self).default_get(fields_list)
|
||||
if self.env.context.get('default_raw_material_production_id'):
|
||||
production_id = self.env['mrp.production'].browse(self.env.context['default_raw_material_production_id'])
|
||||
if production_id.state == 'done':
|
||||
defaults['state'] = 'done'
|
||||
if production_id.state in ('confirmed', 'done'):
|
||||
if production_id.state == 'confirmed':
|
||||
defaults['state'] = 'draft'
|
||||
else:
|
||||
defaults['state'] = 'done'
|
||||
defaults['product_uom_qty'] = 0.0
|
||||
defaults['additional'] = True
|
||||
return defaults
|
||||
|
||||
@@ -1791,3 +1791,37 @@ class TestRoutingAndKits(SavepointCase):
|
||||
wo1 = mo.workorder_ids[0]
|
||||
lines = wo1.raw_workorder_line_ids
|
||||
self.assertEqual(lines.product_id, add_product)
|
||||
|
||||
def test_add_move_2(self):
|
||||
""" Make a production using multi step routing. Add an additional move
|
||||
on a specific operation and check that the produce is consumed into the
|
||||
right workorder. """
|
||||
self.bom_finished1.consumption = 'flexible'
|
||||
add_product = self.env['product.product'].create({
|
||||
'name': 'Additional',
|
||||
})
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.finished1
|
||||
mo_form.bom_id = self.bom_finished1
|
||||
mo_form.product_qty = 10.0
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
mo_form = Form(mo)
|
||||
with mo_form.move_raw_ids.new() as move:
|
||||
move.name = mo.name
|
||||
move.product_id = add_product
|
||||
move.product_uom = add_product.uom_id
|
||||
move.location_id = mo.location_src_id
|
||||
move.location_dest_id = mo.production_location_id
|
||||
move.product_uom_qty = 2
|
||||
move.operation_id = mo.routing_id.operation_ids[0]
|
||||
mo = mo_form.save()
|
||||
new_move = mo.move_raw_ids.filtered(lambda move: move.additional)
|
||||
self.assertEqual(len(mo.move_raw_ids), 3)
|
||||
self.assertEqual(len(new_move), 1)
|
||||
self.assertEqual(mo.move_raw_ids.mapped('state'), ['confirmed'] * 3)
|
||||
mo.button_plan()
|
||||
self.assertEqual(len(mo.workorder_ids), 3)
|
||||
wo1 = mo.workorder_ids[0]
|
||||
lines = wo1.raw_workorder_line_ids
|
||||
self.assertEqual(lines.product_id, add_product)
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
<field name="is_locked" invisible="1"/>
|
||||
<field name="post_visible" invisible="1"/>
|
||||
<field name="unreserve_visible" invisible="1"/>
|
||||
<field name="consumption" invisible="1"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(stock.action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Traceability" type="action" states="done" groups="stock.group_production_lot"/>
|
||||
<button name="%(action_mrp_workorder_production_specific)d" type="action" attrs="{'invisible': [('workorder_count', '=', 0)]}" class="oe_stat_button" icon="fa-play-circle-o">
|
||||
@@ -184,7 +185,7 @@
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Components" name="components">
|
||||
<field name="move_raw_ids" context="{'final_lots': show_final_lots, 'tree_view_ref': 'mrp.view_stock_move_raw_tree', 'form_view_ref': 'mrp.view_stock_move_lots', 'default_location_id': location_src_id, 'default_location_dest_id': production_location_id, 'default_state': 'draft', 'default_raw_material_production_id': id, 'default_picking_type_id': picking_type_id}" attrs="{'readonly': ['&', ('state', '!=', 'draft'), ('is_locked', '=', True)]}"/>
|
||||
<field name="move_raw_ids" context="{'final_lots': show_final_lots, 'tree_view_ref': 'mrp.view_stock_move_raw_tree', 'form_view_ref': 'mrp.view_stock_move_lots', 'default_location_id': location_src_id, 'default_location_dest_id': production_location_id, 'default_state': 'draft', 'default_raw_material_production_id': id, 'default_picking_type_id': picking_type_id}" attrs="{'readonly': ['&', '&', ('state', '!=', 'draft'), ('is_locked', '=', True), '|', ('state', '!=', 'confirmed'), ('consumption', '=', 'strict')]}"/>
|
||||
</page>
|
||||
<page string="Finished Products" name="finished_products">
|
||||
<field name="move_finished_ids" invisible="1"/>
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
<field name="product_id" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||
<label for="product_uom_qty"/>
|
||||
<div class="o_row">
|
||||
<span><field name="product_uom_qty" attrs="{'readonly': [('parent.state', '!=', 'draft')]}" nolabel="1"/></span>
|
||||
<span><field name="product_uom" readonly="1" force_save="1" nolabel="1"/></span>
|
||||
<span><field name="product_uom_qty" attrs="{'readonly': ['&', ('parent.state', '!=', 'draft'), '|', ('parent.state', '!=', 'confirmed'), ('parent.consumption', '=', 'strict')]}" nolabel="1"/></span>
|
||||
<span>
|
||||
<field name="product_uom" attrs="{'readonly': [('id', '!=', False)]}" force_save="1" nolabel="1" groups="uom.group_uom"/>
|
||||
</span>
|
||||
</div>
|
||||
<label for="quantity_done" attrs="{'invisible': [('parent.state', '=', 'draft')]}"/>
|
||||
<div class="o_row" attrs="{'invisible': [('parent.state', '=', 'draft')]}">
|
||||
<label for="quantity_done" attrs="{'invisible': ['|', ('parent.state', '=', 'draft'), ('id', '=', False)]}"/>
|
||||
<div class="o_row" attrs="{'invisible': ['|', ('parent.state', '=', 'draft'), ('id', '=', False)]}">
|
||||
<span><field name="quantity_done" attrs="{'readonly': ['|', '|', ('move_line_ids', '!=', False), ('is_locked', '=', True), '|', ('finished_lots_exist', '=', True), ('has_tracking', '!=', 'none')]}" nolabel="1"/></span>
|
||||
<span> / </span>
|
||||
<span><field name="reserved_availability" nolabel="1"/></span>
|
||||
@@ -32,6 +34,7 @@
|
||||
<field name="operation_id" attrs="{'invisible': ['|', ('parent.routing_id', '=', False), ('id', '!=', False)]}" groups="mrp.group_mrp_routings"/>
|
||||
<field name="routing_id" invisible="1"/>
|
||||
<field name="unit_factor" invisible="1"/>
|
||||
<field name="additional" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="is_done" invisible="1"/>
|
||||
<field name="workorder_id" invisible="1"/>
|
||||
@@ -50,9 +53,10 @@
|
||||
<field name="has_tracking" invisible="1"/>
|
||||
<field name="order_finished_lot_ids" invisible="1"/>
|
||||
<field name="finished_lots_exist" invisible="1"/>
|
||||
<field name="product_uom_category_id" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="move_line_ids" attrs="{'invisible': [('parent.state', '=', 'draft')], 'readonly': ['|', ('is_locked', '=', True), ('state', '=', 'cancel')]}" context="{'default_workorder_id': workorder_id, 'default_product_uom_id': product_uom, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_production_id': production_id or raw_material_production_id}">
|
||||
<field name="move_line_ids" attrs="{'invisible': ['|', ('parent.state', '=', 'draft'), ('id', '=', False)], 'readonly': ['|', ('is_locked', '=', True), ('state', '=', 'cancel')]}" context="{'default_workorder_id': workorder_id, 'default_product_uom_id': product_uom, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_production_id': production_id or raw_material_production_id}">
|
||||
<tree editable="bottom" decoration-success="product_uom_qty==qty_done" decoration-danger="(product_uom_qty > 0) and (qty_done>product_uom_qty)">
|
||||
<field name="lot_id" attrs="{'column_invisible': [('parent.has_tracking', '=', 'none')]}" context="{'default_product_id': parent.product_id}"/>
|
||||
<field name="lot_produced_ids" widget="many2many_tags" options="{'no_open': True, 'no_create': True}" domain="[('id', 'in', parent.order_finished_lot_ids)]" invisible="not context.get('final_lots')"/>
|
||||
|
||||
Reference in New Issue
Block a user