summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Ledoux <dle@odoo.com>2021-02-08 10:44:24 +0000
committerDenis Ledoux <dle@odoo.com>2021-02-08 17:39:45 +0000
commit62c5b8d95c1ba5634e201c0c8a3e2d91fb2305a7 (patch)
tree0f8ee19d17fb616a24b6b6e33d0e350526b13563
parentec05004acb2a3283920e3d7730a971814ccc2ac1 (diff)
[FIX] mrp: `qty_available` computation of a kit and its component
When `qty_available` was being called for a kit and its components at the same time, e.g.: - Kit product - Product 1 - Product 2 The kit product available qty was always computed to `0.0`, because when computing its component available quantity, they were computed to `0.0` because they were marked as `protected` by the ORM when calling the compute method for these records at the first call. When the `qty_available` compute method for the kit then called recursively the `qty_available` compute method for the component, as the component record were marked as protected, the ORM filled their value with the Falsy value `0.0`. This issue occured particularly in the upgrade integrity unit test which makes sure the available quantities of the products remain unchanged after upgrade. This test computes the available quantities for a bunch of products at the same time, and its therefore likely a kit product gets its available quantity computed as the same time than its components. upg-3185 upg-4215 upg-4228 upg-5558 upg-5745 upg-5856 upg-6012 upg-6064 upg-6076 upg-6306 upg-6514 upg-6588 upg-6729 closes odoo/odoo#65751 X-original-commit: 8e152a567a6003dae4abb3b537b963eec4142d6d Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com> Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
-rw-r--r--addons/mrp/models/product.py17
-rw-r--r--addons/mrp/tests/test_bom.py32
2 files changed, 44 insertions, 5 deletions
diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py
index aca579e9272..d7a5c12f496 100644
--- a/addons/mrp/models/product.py
+++ b/addons/mrp/models/product.py
@@ -150,11 +150,18 @@ class ProductProduct(models.Model):
qty_per_kit = bom_line.product_uom_id._compute_quantity(uom_qty_per_kit, bom_line.product_id.uom_id, raise_if_failure=False)
if not qty_per_kit:
continue
- ratios_virtual_available.append(component.virtual_available / qty_per_kit)
- ratios_qty_available.append(component.qty_available / qty_per_kit)
- ratios_incoming_qty.append(component.incoming_qty / qty_per_kit)
- ratios_outgoing_qty.append(component.outgoing_qty / qty_per_kit)
- ratios_free_qty.append(component.free_qty / qty_per_kit)
+ component_res = res.get(component.id, {
+ "virtual_available": component.virtual_available,
+ "qty_available": component.qty_available,
+ "incoming_qty": component.incoming_qty,
+ "outgoing_qty": component.outgoing_qty,
+ "free_qty": component.free_qty,
+ })
+ ratios_virtual_available.append(component_res["virtual_available"] / qty_per_kit)
+ ratios_qty_available.append(component_res["qty_available"] / qty_per_kit)
+ ratios_incoming_qty.append(component_res["incoming_qty"] / qty_per_kit)
+ ratios_outgoing_qty.append(component_res["outgoing_qty"] / qty_per_kit)
+ ratios_free_qty.append(component_res["free_qty"] / qty_per_kit)
if bom_sub_lines and ratios_virtual_available: # Guard against all cnsumable bom: at least one ratio should be present.
res[product.id] = {
'virtual_available': min(ratios_virtual_available) // 1,
diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py
index 9ec7c2bee42..bfa47231ca3 100644
--- a/addons/mrp/tests/test_bom.py
+++ b/addons/mrp/tests/test_bom.py
@@ -262,6 +262,38 @@ class TestBoM(TestMrpCommon):
# Check consumed materials in production order.
self.assertEqual(mrp_order.move_raw_ids.product_id, consumed_products)
+ def test_13_bom_kit_qty(self):
+ self.env['mrp.bom'].create({
+ 'product_id': self.product_7_3.id,
+ 'product_tmpl_id': self.product_7_template.id,
+ 'product_uom_id': self.uom_unit.id,
+ 'product_qty': 4.0,
+ 'type': 'phantom',
+ 'bom_line_ids': [
+ (0, 0, {
+ 'product_id': self.product_2.id,
+ 'product_qty': 2,
+ }),
+ (0, 0, {
+ 'product_id': self.product_3.id,
+ 'product_qty': 2,
+ })
+ ]
+ })
+ location = self.env.ref('stock.stock_location_stock')
+ self.env['stock.quant']._update_available_quantity(self.product_2, location, 4.0)
+ self.env['stock.quant']._update_available_quantity(self.product_3, location, 8.0)
+ # Force the kit product available qty to be computed at the same time than its component quantities
+ # Because `qty_available` of a bom kit "recurse" on `qty_available` of its component,
+ # and this is a tricky thing for the ORM:
+ # `qty_available` gets called for `product_7_3`, `product_2` and `product_3`
+ # which then recurse on calling `qty_available` for `product_2` and `product_3` to compute the quantity of
+ # the kit `product_7_3`. `product_2` and `product_3` gets protected at the first call of the compute method,
+ # ending the recurse call to not call the compute method and just left the Falsy value `0.0`
+ # for the components available qty.
+ kit_product_qty, _, _ = (self.product_7_3 + self.product_2 + self.product_3).mapped("qty_available")
+ self.assertEqual(kit_product_qty, 2)
+
def test_20_bom_report(self):
""" Simulate a crumble receipt with mrp and open the bom structure
report and check that data insde are correct.