From b2f75ca6104a6f93795cf7bea8cf70a2e6823a80 Mon Sep 17 00:00:00 2001 From: Bernat Puig Font Date: Mon, 5 Sep 2022 17:47:59 +0200 Subject: [PATCH] [FIX] mrp_multi_level: Manage Kits in MRP Multi Level --- mrp_multi_level/demo/mrp_bom_demo.xml | 47 +++++++++++++++- .../demo/product_mrp_area_demo.xml | 16 ++++++ mrp_multi_level/demo/product_product_demo.xml | 53 ++++++++++++++++++- .../demo/product_supplierinfo_demo.xml | 16 ++++++ mrp_multi_level/models/product_mrp_area.py | 15 ++++-- mrp_multi_level/tests/common.py | 17 ++++++ mrp_multi_level/tests/test_mrp_multi_level.py | 39 ++++++++++++++ mrp_multi_level/wizards/mrp_multi_level.py | 38 ++++++++++--- 8 files changed, 226 insertions(+), 15 deletions(-) diff --git a/mrp_multi_level/demo/mrp_bom_demo.xml b/mrp_multi_level/demo/mrp_bom_demo.xml index 21e90a822..e2639cc08 100644 --- a/mrp_multi_level/demo/mrp_bom_demo.xml +++ b/mrp_multi_level/demo/mrp_bom_demo.xml @@ -40,8 +40,30 @@ 5 - - + + + + + + 5 + + + + 2 + + 5 + + + + + 2 + + 5 + + + + + 5 + + + + + phantom + 5 + + + + 1 + + 5 + + + + + 3 + + 5 + + diff --git a/mrp_multi_level/demo/product_mrp_area_demo.xml b/mrp_multi_level/demo/product_mrp_area_demo.xml index fdb6a264c..c14a4ef80 100644 --- a/mrp_multi_level/demo/product_mrp_area_demo.xml +++ b/mrp_multi_level/demo/product_mrp_area_demo.xml @@ -8,6 +8,10 @@ + + + + @@ -32,6 +36,10 @@ + + + + @@ -40,6 +48,14 @@ + + + + + + + + diff --git a/mrp_multi_level/demo/product_product_demo.xml b/mrp_multi_level/demo/product_product_demo.xml index f99f38e11..f9b45c103 100644 --- a/mrp_multi_level/demo/product_product_demo.xml +++ b/mrp_multi_level/demo/product_product_demo.xml @@ -24,7 +24,20 @@ eval="[(6, 0, [ref('mrp.route_warehouse0_manufacture')])]" /> - + + FP-3 + + product + + + 3 + + + + + + SF-3 + + product + + + 3 + + + PP-1 @@ -77,6 +103,31 @@ eval="[(6, 0, [ref('purchase_stock.route_warehouse0_buy')])]" /> + + + PP-3 + + product + + + + + + + PP-4 + + product + + + + + AV-11 steel diff --git a/mrp_multi_level/demo/product_supplierinfo_demo.xml b/mrp_multi_level/demo/product_supplierinfo_demo.xml index c29460900..eaecfa523 100644 --- a/mrp_multi_level/demo/product_supplierinfo_demo.xml +++ b/mrp_multi_level/demo/product_supplierinfo_demo.xml @@ -42,4 +42,20 @@ 0 100 + + + + + 2 + 0 + 10 + + + + + + 3 + 0 + 80 + diff --git a/mrp_multi_level/models/product_mrp_area.py b/mrp_multi_level/models/product_mrp_area.py index 42c94bb94..c01d56421 100644 --- a/mrp_multi_level/models/product_mrp_area.py +++ b/mrp_multi_level/models/product_mrp_area.py @@ -78,6 +78,7 @@ class ProductMRPArea(models.Model): ("buy", "Buy"), ("none", "Undefined"), ("manufacture", "Produce"), + ("phantom", "Kit"), ("pull", "Pull From"), ("push", "Push To"), ("pull_push", "Pull & Push"), @@ -182,7 +183,14 @@ class ProductMRPArea(models.Model): "company_id": rec.mrp_area_id.company_id, } rule = group_obj._get_rule(rec.product_id, proc_loc, values) - rec.supply_method = rule.action if rule else "none" + if ( + rule.action == "manufacture" + and rec.product_id.product_tmpl_id.bom_ids + and rec.product_id.product_tmpl_id.bom_ids[0].type == "phantom" + ): + rec.supply_method = "phantom" + else: + rec.supply_method = rule.action if rule else "none" @api.depends("supply_method", "product_id.route_ids", "product_id.seller_ids") def _compute_main_supplier(self): @@ -263,7 +271,4 @@ class ProductMRPArea(models.Model): def _to_be_exploded(self): self.ensure_one() - if self.supply_method == "manufacture": - return True - else: - return False + return self.supply_method in ["manufacture", "phantom"] diff --git a/mrp_multi_level/tests/common.py b/mrp_multi_level/tests/common.py index aae0c0314..ca4139073 100644 --- a/mrp_multi_level/tests/common.py +++ b/mrp_multi_level/tests/common.py @@ -28,10 +28,14 @@ class TestMrpMultiLevelCommon(SavepointCase): cls.fp_1 = cls.env.ref("mrp_multi_level.product_product_fp_1") cls.fp_2 = cls.env.ref("mrp_multi_level.product_product_fp_2") + cls.fp_3 = cls.env.ref("mrp_multi_level.product_product_fp_3") cls.sf_1 = cls.env.ref("mrp_multi_level.product_product_sf_1") cls.sf_2 = cls.env.ref("mrp_multi_level.product_product_sf_2") + cls.sf_3 = cls.env.ref("mrp_multi_level.product_product_sf_3") cls.pp_1 = cls.env.ref("mrp_multi_level.product_product_pp_1") cls.pp_2 = cls.env.ref("mrp_multi_level.product_product_pp_2") + cls.pp_3 = cls.env.ref("mrp_multi_level.product_product_pp_3") + cls.pp_4 = cls.env.ref("mrp_multi_level.product_product_pp_4") cls.product_4b = cls.env.ref("product.product_product_4b") cls.av_11 = cls.env.ref("mrp_multi_level.product_product_av_11") cls.av_12 = cls.env.ref("mrp_multi_level.product_product_av_12") @@ -271,6 +275,19 @@ class TestMrpMultiLevelCommon(SavepointCase): "location_dest_id": cls.customer_location.id, }, ), + ( + 0, + 0, + { + "name": "Test move fp-3", + "product_id": cls.fp_3.id, + "date": date_move, + "product_uom": cls.fp_3.uom_id.id, + "product_uom_qty": 5, + "location_id": cls.stock_location.id, + "location_dest_id": cls.customer_location.id, + }, + ), ( 0, 0, diff --git a/mrp_multi_level/tests/test_mrp_multi_level.py b/mrp_multi_level/tests/test_mrp_multi_level.py index 62b9912f3..1f874d664 100644 --- a/mrp_multi_level/tests/test_mrp_multi_level.py +++ b/mrp_multi_level/tests/test_mrp_multi_level.py @@ -389,3 +389,42 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon): self.assertEqual(len(prod_uom_test_inventory_lines), 1) self.assertEqual(prod_uom_test_inventory_lines.supply_qty, 12.0) # Supply qty has to be 12 has a dozen of units are in a RFQ. + + def test_16_phantom_comp_planning(self): + """ + Phantom components will not appear in MRP Inventory or Planned Orders. + MRP Parameter will have 'phantom' supply method. + """ + # SF-3 + sf_3_line_1 = self.mrp_inventory_obj.search( + [("product_mrp_area_id.product_id", "=", self.sf_3.id)] + ) + self.assertEqual(len(sf_3_line_1), 0) + 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") + # 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) + 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), 2) + # PP-4 + pp_4_line_1 = self.mrp_inventory_obj.search( + [("product_mrp_area_id.product_id", "=", self.pp_4.id)] + ) + self.assertEqual(len(pp_4_line_1), 1) + self.assertEqual(pp_4_line_1.demand_qty, 30.0) + pp_4_planned_orders = self.planned_order_obj.search( + [("product_mrp_area_id.product_id", "=", self.pp_4.id)] + ) + self.assertEqual(len(pp_4_planned_orders), 1) diff --git a/mrp_multi_level/wizards/mrp_multi_level.py b/mrp_multi_level/wizards/mrp_multi_level.py index 731230d81..6f38dd7ae 100644 --- a/mrp_multi_level/wizards/mrp_multi_level.py +++ b/mrp_multi_level/wizards/mrp_multi_level.py @@ -121,7 +121,15 @@ class MultiLevelMrp(models.TransientModel): @api.model def _prepare_mrp_move_data_bom_explosion( - self, product, bomline, qty, mrp_date_demand_2, bom, name, planned_order + self, + product, + bomline, + qty, + mrp_date_demand_2, + bom, + name, + planned_order, + values=None, ): product_mrp_area = self._get_product_mrp_area_from_product_and_area( bomline.product_id, product.mrp_area_id @@ -157,7 +165,7 @@ class MultiLevelMrp(models.TransientModel): ).replace( "Demand Bom Explosion: Demand Bom Explosion: ", "Demand Bom Explosion: " ), - "origin": planned_order.origin, + "origin": planned_order.origin if planned_order else values.get("origin"), } @api.model @@ -182,7 +190,9 @@ class MultiLevelMrp(models.TransientModel): return mrp_action_date, mrp_date_supply @api.model - def explode_action(self, product_mrp_area_id, mrp_action_date, name, qty, action): + def explode_action( + self, product_mrp_area_id, mrp_action_date, name, qty, action, values=None + ): """Explode requirements.""" mrp_date_demand = mrp_action_date if mrp_date_demand < date.today(): @@ -221,6 +231,7 @@ class MultiLevelMrp(models.TransientModel): bom, name, action, + values, ) mrpmove_id2 = self.env["mrp.move"].create(move_data) if hasattr(action, "mrp_move_down_ids"): @@ -265,12 +276,20 @@ class MultiLevelMrp(models.TransientModel): order_data = self._prepare_planned_order_data( product_mrp_area_id, qty, mrp_date_supply, mrp_action_date, name, values ) - planned_order = self.env["mrp.planned.order"].create(order_data) + # Do not create planned order for products that are Kits + planned_order = False + if not product_mrp_area_id.supply_method == "phantom": + planned_order = self.env["mrp.planned.order"].create(order_data) qty_ordered = qty_ordered + qty if product_mrp_area_id._to_be_exploded(): self.explode_action( - product_mrp_area_id, mrp_action_date, name, qty, planned_order + product_mrp_area_id, + mrp_action_date, + name, + qty, + planned_order, + values, ) values["qty_ordered"] = qty_ordered @@ -768,8 +787,13 @@ class MultiLevelMrp(models.TransientModel): for product_mrp_area in product_mrp_area_ids: # Build the time-phased inventory - if self._exclude_from_mrp( - product_mrp_area.product_id, product_mrp_area.mrp_area_id + # Condition "supply_method == phantom" is not added in _exclude_from_mrp + # because this function is also used to filter the explosion. + if ( + self._exclude_from_mrp( + product_mrp_area.product_id, product_mrp_area.mrp_area_id + ) + or product_mrp_area.supply_method == "phantom" ): continue self._init_mrp_inventory(product_mrp_area)