From cd0eeb90430de1d3e91cfb6d541b57291ec5f1c1 Mon Sep 17 00:00:00 2001 From: Daniel Duque Date: Mon, 9 Dec 2024 12:26:42 +0100 Subject: [PATCH] [FIX] stock_available_mrp: variant BoMs When a BoM is linked just to one variant through field `product_id`, it is taken in account when computing stock of the rest of variants of the same template. --- stock_available_mrp/models/product_product.py | 22 +++++- .../tests/test_potential_qty.py | 67 +++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/stock_available_mrp/models/product_product.py b/stock_available_mrp/models/product_product.py index 658b9d49..f66d6822 100644 --- a/stock_available_mrp/models/product_product.py +++ b/stock_available_mrp/models/product_product.py @@ -11,7 +11,13 @@ class ProductProduct(models.Model): _inherit = "product.product" - @api.depends("virtual_available", "bom_ids", "bom_ids.product_qty") + @api.depends( + "virtual_available", + "bom_ids", + "bom_ids.product_qty", + "variant_bom_ids", + "variant_bom_ids.product_qty", + ) def _compute_available_quantities(self): res = super()._compute_available_quantities() return res @@ -19,7 +25,11 @@ def _compute_available_quantities(self): def _compute_available_quantities_dict(self): res, stock_dict = super()._compute_available_quantities_dict() # compute qty for product with bom - product_with_bom = self.filtered("bom_ids") + product_with_bom = self.filtered("variant_bom_ids") + product_with_bom |= (self - product_with_bom).filtered( + lambda p: p.bom_ids + and any(not bom.product_id or bom.product_id == p for bom in p.bom_ids) + ) if not product_with_bom: return res, stock_dict @@ -53,7 +63,13 @@ def _compute_available_quantities_dict(self): for product in product_with_bom: # Need by product (same product can be in many BOM lines/levels) - bom_id = first(product.bom_ids) + bom_id = first( + product.variant_bom_ids + or product.bom_ids.filtered( + lambda b, product=product: not b.product_id + or b.product_id == product + ) + ) exploded_components = exploded_boms[product.id] component_needs = product._get_components_needs(exploded_components) if not component_needs: diff --git a/stock_available_mrp/tests/test_potential_qty.py b/stock_available_mrp/tests/test_potential_qty.py index 6492d318..12247b23 100644 --- a/stock_available_mrp/tests/test_potential_qty.py +++ b/stock_available_mrp/tests/test_potential_qty.py @@ -451,3 +451,70 @@ def test_product_phantom(self): product.invalidate_model() self.assertEqual(product.immediately_usable_qty, 0.0) + + def test_06_potential_qty(self): + # BoM only applies to white variant. + bom = self.env["mrp.bom"].search([("product_tmpl_id", "=", self.tmpl.id)]) + bom.bom_line_ids.write({"bom_product_template_attribute_value_ids": False}) + bom.product_id = self.var2.id + for i in [self.tmpl, self.var1, self.var2]: + self.assertPotentialQty(i, 0.0, "The potential quantity should start at 0") + + # Receive 1000x Wood Panel + self.create_inventory( + product=self.env.ref("mrp.product_product_wood_panel"), + qty=1000.0, + location=self.wh_main.lot_stock_id, + ) + for i in [self.tmpl, self.var1, self.var2]: + self.assertPotentialQty( + i, + 0.0, + "Receiving a single component should not change the " + "potential of %s" % i, + ) + + # Receive second component + self.create_inventory( + product=self.env.ref("mrp.product_product_computer_desk_bolt"), + qty=1000.0, + location=self.wh_main.lot_stock_id, + ) + self.assertPotentialQty( + self.tmpl, + 0, + "Template potential changed after receiving partial variant 2 components", + ) + self.assertPotentialQty( + self.var1, + 0, + "Variant 1 potential changed after receiving partial variant 2 components", + ) + self.assertPotentialQty( + self.var2, + 0.0, + "Variant 2 potential changed after receiving partial components", + ) + + # Receive enough components to make 250 the 1st variant + self.create_inventory( + product=self.env.ref( + "stock_available_mrp.product_computer_desk_bolt_white" + ), + qty=1000.0, + location=self.wh_main.lot_stock_id, + ) + self.var1.invalidate_model() + self.assertPotentialQty( + self.tmpl, + 250.0, + "Wrong template potential after receiving variant 2 components", + ) + self.assertPotentialQty( + self.var1, + 0, + "Wrong variant 1 potential after receiving variant 2 components", + ) + self.assertPotentialQty( + self.var2, 250.0, "Wrong variant 2 potential after receiving its components" + )