[FIX] mrp: unbuild a manufacturing order with a tracked product
Steps to reproduce the bug:
- Create a storable product “P1”:
- Tracked by SN
- BoM:
- Component: 1 unit of “C1”
- Create a Mo to produce one unit of “P1”:
- Confirm the MO
- Set a new serial number “SN1” and qty done for “C1”
- Mark as done
- Click on “unbuild” button
Problem:
An user error is triggered: “This lot ‘SN1’ is incompatible with this
product ‘C1’
When you click on the "Unbuild" button, the `lot_id` of the finished
product is added in the context with the key `'default_lot_id'`:
https://github.com/odoo/odoo/blob/dd60647ce41547ecd00bbde45ddf564a7ada91c7/addons/mrp/models/mrp_production.py#L1977
Therefore, when creating the `stock.move.line` for the component,
even if the “lot_id” is not in the vals, we will get the “lot_id” from the context:
https://github.com/odoo/odoo/blob/d04a5b5c8c7dc13e4e911a29d1944e90587e2883/odoo/models.py#L4136
https://github.com/odoo/odoo/blob/d04a5b5c8c7dc13e4e911a29d1944e90587e2883/odoo/models.py#L1961
Then, we do a field validation via a constraint, but as the product "C1"
is not compatible with the product in lot "P1", an error is triggered:
https://github.com/odoo/odoo/blob/b9334c53b84228c00ab100ffd28620b3c923c4e6/addons/stock/models/stock_move_line.py#L94-L95
opw-3284525
closes odoo/odoo#120981
X-original-commit: 2ee59a523e12b79c1338467bcf7577a1c9b63d11
Signed-off-by: Tiffany Chang <tic@odoo.com>
This commit is contained in:
@@ -143,7 +143,7 @@ class MrpUnbuild(models.Model):
|
||||
consume_moves = self._generate_consume_moves()
|
||||
consume_moves._action_confirm()
|
||||
produce_moves = self._generate_produce_moves()
|
||||
produce_moves._action_confirm()
|
||||
produce_moves.with_context(default_lot_id=False)._action_confirm()
|
||||
|
||||
finished_moves = consume_moves.filtered(lambda m: m.product_id == self.product_id)
|
||||
consume_moves -= finished_moves
|
||||
|
||||
@@ -821,3 +821,82 @@ class TestUnbuild(TestMrpCommon):
|
||||
mo2 = mo_form.save()
|
||||
mo2.button_mark_done()
|
||||
self.assertEqual(mo2.state, 'done', "Production order should be in done state.")
|
||||
|
||||
def test_unbuild_mo_with_tracked_product_and_component(self):
|
||||
"""
|
||||
Test that the unbuild order is correctly created when the finished product
|
||||
and the component is tracked by serial number
|
||||
"""
|
||||
finished_product = self.env['product.product'].create({
|
||||
'name': 'Product tracked by sn',
|
||||
'type': 'product',
|
||||
'tracking': 'serial',
|
||||
})
|
||||
finished_product_sn = self.env['stock.lot'].create({
|
||||
'name': 'sn1',
|
||||
'product_id': finished_product.id,
|
||||
'company_id': self.env.company.id
|
||||
})
|
||||
component = self.env['product.product'].create({
|
||||
'name': 'Product component',
|
||||
'type': 'product',
|
||||
})
|
||||
bom_1 = self.env['mrp.bom'].create({
|
||||
'product_id': finished_product.id,
|
||||
'product_tmpl_id': finished_product.product_tmpl_id.id,
|
||||
'product_uom_id': self.env.ref('uom.product_uom_unit').id,
|
||||
'product_qty': 1.0,
|
||||
'type': 'normal',
|
||||
'bom_line_ids': [
|
||||
(0, 0, {'product_id': component.id, 'product_qty': 1}),
|
||||
],
|
||||
})
|
||||
# mo_1
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = finished_product
|
||||
mo_form.bom_id = bom_1
|
||||
mo_form.product_qty = 1.0
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
mo.qty_producing = 1.0
|
||||
mo.lot_producing_id = finished_product_sn
|
||||
mo.move_raw_ids.quantity_done = 1
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
|
||||
# unbuild order mo_1
|
||||
action = mo.button_unbuild()
|
||||
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
|
||||
wizard.action_validate()
|
||||
self.assertEqual(mo.unbuild_ids.produce_line_ids[0].product_id, finished_product)
|
||||
self.assertEqual(mo.unbuild_ids.produce_line_ids[0].lot_ids, finished_product_sn)
|
||||
self.assertEqual(mo.unbuild_ids.produce_line_ids[1].product_id, component)
|
||||
self.assertEqual(mo.unbuild_ids.produce_line_ids[1].lot_ids.id, False)
|
||||
|
||||
# set the component as tracked
|
||||
component.tracking = 'serial'
|
||||
component_sn = self.env['stock.lot'].create({
|
||||
'name': 'component-sn1',
|
||||
'product_id': component.id,
|
||||
'company_id': self.env.company.id
|
||||
})
|
||||
self.env['stock.quant']._update_available_quantity(component, self.stock_location, 1, lot_id=component_sn)
|
||||
#mo2 with tracked component
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = finished_product
|
||||
mo_form.bom_id = bom_1
|
||||
mo_form.product_qty = 1.0
|
||||
mo_2 = mo_form.save()
|
||||
mo_2.action_confirm()
|
||||
mo_2.qty_producing = 1.0
|
||||
mo_2.lot_producing_id = finished_product_sn
|
||||
mo_2.move_raw_ids.quantity_done = 1
|
||||
mo_2.button_mark_done()
|
||||
self.assertEqual(mo_2.state, 'done', "Production order should be in done state.")
|
||||
# unbuild mo_2
|
||||
action = mo_2.button_unbuild()
|
||||
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
|
||||
wizard.action_validate()
|
||||
self.assertEqual(mo_2.unbuild_ids.produce_line_ids[0].product_id, finished_product)
|
||||
self.assertEqual(mo_2.unbuild_ids.produce_line_ids[0].lot_ids, finished_product_sn)
|
||||
self.assertEqual(mo_2.unbuild_ids.produce_line_ids[1].product_id, component)
|
||||
self.assertEqual(mo_2.unbuild_ids.produce_line_ids[1].lot_ids, component_sn)
|
||||
|
||||
Reference in New Issue
Block a user