From acd3556463fa1fbc376b6c5252254110019753ff Mon Sep 17 00:00:00 2001 From: Adrien Widart <awt@odoo.com> Date: Mon, 25 Oct 2021 10:19:53 +0000 Subject: [PATCH] [FIX] mrp_account, sale_mrp: check if BoM line is correct Suppose an automated AVCO product category. When confirming an invoice, if the associated product was a kit and if its BoM has changed during the process, a traceback can occur. To reproduce the issue: (Need sale_management) 1. Create a product category PC: - Costing Method: AVCO - Inventory Valuation: Automated 2. Create 3 products P1, P2, P3: - Product Type: Storable - Category: PC 3. Update P3's quantity > 0 4. Create two bills of materials: - BOM01: - Product: P1 - BoM Type: Kit - Components: 1 x P2 - BOM02: - Product: P2 - BoM Type: Kit - Components: 1 x P3 5. Create a sale order SO with 1 x P1 6. Confirm SO and process the delivery 7. Edit BOM02: - BoM Type: Manufacture 8. On SO, create the invoice INV 9. Confirm INV Error: an Odoo Server Error is displayed with a traceback: "[...] in _compute_average_price, bom_line_data = bom_lines[bom_line] [...] KeyError: mrp.bom.line(19,)" On step 7, when changing the BoM type, a new BoM line is created. Therefore, in `_compute_average_price`, the BoM associated to the move (i.e., `bom_line`) is not one of the lines in `bom_lines` (i.e., the new BoM lines). There is already a check in case the line has been deleted, but not if it has been changed. OPW-2610685 closes odoo/odoo#78917 Signed-off-by: William Henrotin (whe) <whe@odoo.com> --- addons/mrp_account/models/product.py | 2 +- addons/sale_mrp/tests/test_sale_mrp_flow.py | 50 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/addons/mrp_account/models/product.py b/addons/mrp_account/models/product.py index 82122b6de01c..8b975609bc8d 100644 --- a/addons/mrp_account/models/product.py +++ b/addons/mrp_account/models/product.py @@ -54,7 +54,7 @@ class ProductProduct(models.Model): if move.state == 'cancel': continue bom_line = move.bom_line_id - if bom_line: + if bom_line in bom_lines: bom_line_data = bom_lines[bom_line] line_qty = bom_line.product_uom_id._compute_quantity(bom_line_data['qty'], bom_line.product_id.uom_id) else: diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py index 4c9bde734de9..f940912c3135 100644 --- a/addons/sale_mrp/tests/test_sale_mrp_flow.py +++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py @@ -1860,3 +1860,53 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon): self.assertNotEqual(qty_del_return_validated, 1.0, "The return was validated, therefore the delivery from client to" " company was successful, and the client is left without his 1 product.") self.assertEqual(qty_del_return_validated, 0.0, "The return has processed, client doesn't have any quantity anymore") + + def test_14_change_bom_type(self): + """ This test ensures that updating a Bom type during a flow does not lead to any error """ + p1 = self._cls_create_product('Master', self.uom_unit) + p2 = self._cls_create_product('Component', self.uom_unit) + p3 = self.component_a + p1.categ_id.write({ + 'property_cost_method': 'average', + 'property_valuation': 'real_time', + }) + stock_location = self.company_data['default_warehouse'].lot_stock_id + self.env['stock.quant']._update_available_quantity(self.component_a, stock_location, 1) + + self.env['mrp.bom'].create({ + 'product_tmpl_id': p1.product_tmpl_id.id, + 'product_qty': 1.0, + 'type': 'phantom', + 'bom_line_ids': [(0, 0, { + 'product_id': p2.id, + 'product_qty': 1.0, + })] + }) + + p2_bom = self.env['mrp.bom'].create({ + 'product_tmpl_id': p2.product_tmpl_id.id, + 'product_qty': 1.0, + 'type': 'phantom', + 'bom_line_ids': [(0, 0, { + 'product_id': p3.id, + 'product_qty': 1.0, + })] + }) + + so_form = Form(self.env['sale.order']) + so_form.partner_id = self.env['res.partner'].create({'name': 'Super Partner'}) + with so_form.order_line.new() as so_line: + so_line.product_id = p1 + so = so_form.save() + so.action_confirm() + + wiz_act = so.picking_ids.button_validate() + wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() + wiz.process() + + p2_bom.type = "normal" + + so._create_invoices() + invoice = so.invoice_ids + invoice.action_post() + self.assertEqual(invoice.state, 'posted') -- GitLab