From fe508c6b2cb1b92d7b063cc194a3dad37f4eb255 Mon Sep 17 00:00:00 2001 From: Djamel Touati Date: Wed, 6 Dec 2023 08:10:37 +0100 Subject: [PATCH] [FIX] mrp: validate a MO with manual consumption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce the bug: - Create a storable produce P1 with BoM: - select any component, qty: 1 unit - Create a MO to produce one unit of P1 - Confirm the MO - Update the qty of the component to 2 - Validate the MO Problem: The quantity of the component is overwritten and reset to 1 Because, when the new qty of the component is set, only the “manual_consumption” field is updated to “True” thanks to their compute: https://github.com/odoo/odoo/blob/17.0/addons/mrp/models/stock_move.py#L138-L145 But the field ‘picked’, is not updated So when we check if we have to set a quantity, the condition is false and the quantity is reset to the original value: https://github.com/odoo/odoo/blob/26193fdb707a631d498cbc37d68c08497e32a31d/addons/mrp/models/mrp_production.py#L1201-L1202 opw-3616628 closes odoo/odoo#145157 Signed-off-by: William Henrotin (whe) --- addons/mrp/models/stock_move.py | 2 + addons/mrp/static/src/widgets/mrp_consumed.js | 2 +- .../tests/tours/mrp_manual_consumption.js | 34 ++++++++++++++ addons/mrp/tests/test_manual_consumption.py | 44 +++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index cc4aae3e419..ff474a117e5 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -287,6 +287,8 @@ class StockMove(models.Model): def write(self, vals): if self.env.context.get('force_manual_consumption'): vals['manual_consumption'] = True + if vals.get('manual_consumption'): + self.picked = True if 'product_uom_qty' in vals and 'move_line_ids' in vals: # first update lines then product_uom_qty as the later will unreserve # so possibly unlink lines diff --git a/addons/mrp/static/src/widgets/mrp_consumed.js b/addons/mrp/static/src/widgets/mrp_consumed.js index 2419c84f401..854ca5d0647 100644 --- a/addons/mrp/static/src/widgets/mrp_consumed.js +++ b/addons/mrp/static/src/widgets/mrp_consumed.js @@ -23,7 +23,7 @@ export class MrpConsumed extends FloatField { } onInput(ev) { - return this.props.record.update({ manual_consumption: true }); + return this.props.record.update({ manual_consumption: true, picked: true }); } } diff --git a/addons/mrp/static/tests/tours/mrp_manual_consumption.js b/addons/mrp/static/tests/tours/mrp_manual_consumption.js index 6021040c20e..02b58eda0e2 100644 --- a/addons/mrp/static/tests/tours/mrp_manual_consumption.js +++ b/addons/mrp/static/tests/tours/mrp_manual_consumption.js @@ -70,3 +70,37 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', { }, ...stepUtils.saveForm(), ]}); + +registry.category("web_tour.tours").add('test_mrp_manual_consumption_02', { + test: true, + steps: () => [ + { + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("0.00")', + run: () => {}, + }, + { + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last', + run: 'click', + }, + { + trigger: 'div[name="quantity"] input', + run: 'text 16.0' + }, + { + content: "Click Pager", + trigger: ".o_pager_value:first()", + }, + { + trigger: "input[id='qty_producing_0']", + run: 'text 8.0', + }, + { + content: "Click Pager", + trigger: ".o_pager_value:first()", + }, + { + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("16.00")', + run: () => {}, + }, + ...stepUtils.saveForm(), +]}); diff --git a/addons/mrp/tests/test_manual_consumption.py b/addons/mrp/tests/test_manual_consumption.py index 5d5188b00d4..02e8e20afcf 100644 --- a/addons/mrp/tests/test_manual_consumption.py +++ b/addons/mrp/tests/test_manual_consumption.py @@ -86,6 +86,50 @@ class TestTourManualConsumption(HttpCase): self.assertEqual(move_sn.manual_consumption, True) self.assertEqual(move_lot.manual_consumption, True) + def test_mrp_manual_consumption_02(self): + """ + test that when a new quantity is manually set for a component, + and the MO is marked as done, the component quantity is not overwritten. + """ + Product = self.env['product.product'] + product_finish = Product.create({ + 'name': 'finish', + 'type': 'product', + 'tracking': 'none',}) + product_nt = Product.create({ + 'name': 'No tracking', + 'type': 'product', + 'tracking': 'none',}) + bom = self.env['mrp.bom'].create({ + 'product_id': product_finish.id, + 'product_tmpl_id': product_finish.product_tmpl_id.id, + 'product_qty': 1, + 'type': 'normal', + 'bom_line_ids': [ + (0, 0, {'product_id': product_nt.id, 'product_qty': 1}), + ], + }) + + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = product_finish + mo_form.bom_id = bom + mo_form.product_qty = 10 + mo = mo_form.save() + mo.action_confirm() + + self.assertEqual(mo.state, 'confirmed') + move_nt = mo.move_raw_ids + self.assertEqual(move_nt.manual_consumption, False) + self.assertEqual(move_nt.quantity, 0) + self.assertFalse(move_nt.picked) + + action_id = self.env.ref('mrp.menu_mrp_production_action').action + url = "/web#model=mrp.production&view_type=form&action=%s&id=%s" % (str(action_id.id), str(mo.id)) + self.start_tour(url, "test_mrp_manual_consumption_02", login="admin", timeout=100) + + self.assertEqual(move_nt.manual_consumption, True) + self.assertEqual(move_nt.picked, True) + self.assertEqual(move_nt.quantity, 16.0) class TestManualConsumption(TestMrpCommon): @classmethod