[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:
Touati Djamel (otd)
2023-05-10 21:18:08 +02:00
parent 6f6fb10511
commit 2a5e079ef7
2 changed files with 80 additions and 1 deletions
+1 -1
View File
@@ -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
+79
View File
@@ -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)