diff options
| author | Denis Ledoux <dle@odoo.com> | 2021-02-08 10:44:24 +0000 |
|---|---|---|
| committer | Denis Ledoux <dle@odoo.com> | 2021-02-08 17:39:45 +0000 |
| commit | 62c5b8d95c1ba5634e201c0c8a3e2d91fb2305a7 (patch) | |
| tree | 0f8ee19d17fb616a24b6b6e33d0e350526b13563 | |
| parent | ec05004acb2a3283920e3d7730a971814ccc2ac1 (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.py | 17 | ||||
| -rw-r--r-- | addons/mrp/tests/test_bom.py | 32 |
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. |
