[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#65758 X-original-commit: 8e152a567a6003dae4abb3b537b963eec4142d6d Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com> Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user