[FIX] mrp_multi_level: fix kit/phantom planning

fixes #1362

Ignoring qty_available for phantom products prevents double counting the
qty_available of components.

Creating planned orders for phantom products is simpler than recursively
exploding phantom BOMs. This also makes it easier to analyze the planning data
generated by the MRP calculation.
This commit is contained in:
Matt Taylor
2024-10-17 09:44:06 -06:00
committed by JasminSForgeFlow
parent 34cc2cccd7
commit c850eb0bb5
7 changed files with 83 additions and 20 deletions

View File

@@ -401,11 +401,8 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
sf_3_planned_order_1 = self.planned_order_obj.search(
[("product_mrp_area_id.product_id", "=", self.sf_3.id)]
)
self.assertEqual(len(sf_3_planned_order_1), 0)
sf_3_mrp_parameter = self.product_mrp_area_obj.search(
[("product_id", "=", self.sf_3.id)]
)
self.assertEqual(sf_3_mrp_parameter.supply_method, "phantom")
self.assertEqual(sf_3_planned_order_1.mrp_action, "phantom")
self.assertEqual(sf_3_planned_order_1.mrp_qty, 10.0)
# PP-3
pp_3_line_1 = self.mrp_inventory_obj.search(
[("product_mrp_area_id.product_id", "=", self.pp_3.id)]
@@ -854,3 +851,53 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
f"unexpected value for {key}: {inv[key]} "
f"(expected {test_vals[key]} on {inv.date})",
)
def test_25_phantom_comp_on_hand(self):
"""
A phantom product with positive qty_available (which is computed from the
availability of its components) should not satisfy demand, because this leads
to double counting qty_available of its component products.
"""
quant = self.quant_obj.sudo().create(
{
"product_id": self.pp_3.id,
"inventory_quantity": 10.0,
"location_id": self.stock_location.id,
}
)
quant.action_apply_inventory()
quant = self.quant_obj.sudo().create(
{
"product_id": self.pp_4.id,
"inventory_quantity": 30.0,
"location_id": self.stock_location.id,
}
)
quant.action_apply_inventory()
self.assertEqual(self.sf_3.qty_available, 10.0)
self.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
# PP-3
pp_3_line_1 = self.mrp_inventory_obj.search(
[("product_mrp_area_id.product_id", "=", self.pp_3.id)]
)
self.assertEqual(len(pp_3_line_1), 1)
self.assertEqual(pp_3_line_1.demand_qty, 20.0)
self.assertEqual(pp_3_line_1.to_procure, 10.0)
pp_3_planned_orders = self.planned_order_obj.search(
[("product_mrp_area_id.product_id", "=", self.pp_3.id)]
)
self.assertEqual(len(pp_3_planned_orders), 1)
self.assertEqual(pp_3_planned_orders.mrp_qty, 10)
sf3_planned_orders = self.env["mrp.planned.order"].search(
[("product_id", "=", self.sf_3.id)]
)
self.assertEqual(len(sf3_planned_orders), 1)
# Trying to procure a kit planned order will have no effect.
procure_wizard = (
self.env["mrp.inventory.procure"]
.with_context(
active_model="mrp.planned.order", active_ids=sf3_planned_orders.ids
)
.create({})
)
self.assertEqual(len(procure_wizard.item_ids), 0)