From 8ad7e65d4ef8da7d5315623dbefa1489b1a3f08f Mon Sep 17 00:00:00 2001 From: BernatPForgeFlow Date: Thu, 25 May 2023 08:11:54 +0200 Subject: [PATCH] [IMP] mrp_multi_level_estimate: Stock moves from MO should always be considered When we consider forecasts as the 'Indirect' demand, we should always keep considering the demand coming from Manufacturing Orders, as they are 'Direct' demand. --- .../tests/test_mrp_multi_level_estimate.py | 107 ++++++++++++++++++ .../wizards/mrp_multi_level.py | 7 +- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/mrp_multi_level_estimate/tests/test_mrp_multi_level_estimate.py b/mrp_multi_level_estimate/tests/test_mrp_multi_level_estimate.py index a06126550..344baa799 100644 --- a/mrp_multi_level_estimate/tests/test_mrp_multi_level_estimate.py +++ b/mrp_multi_level_estimate/tests/test_mrp_multi_level_estimate.py @@ -3,6 +3,8 @@ from datetime import datetime, timedelta +from odoo.tests import Form + from odoo.addons.mrp_multi_level.tests.common import TestMrpMultiLevelCommon @@ -333,3 +335,108 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon): self.assertEqual( demand_from_other_sources.mrp_date, self.date_without_ranges.date() ) + + def test_06_estimate_and_other_sources_strat_with_mo(self): + """ + Tests demand estimates and other sources strategies with MOs. + Components demand from MOs is always indirect demand, so even if we + have estimates, we should consider that demand. + """ + # Get manufactured product, component and bom + fp_1 = self.env.ref("mrp_multi_level.product_product_fp_1") + pp_1 = self.env.ref("mrp_multi_level.product_product_pp_1") + fp_1_bom = self.env.ref("mrp_multi_level.mrp_bom_fp_1") + self.product_mrp_area_obj.create( + {"product_id": fp_1.id, "mrp_area_id": self.estimate_area.id} + ) + self.product_mrp_area_obj.create( + {"product_id": pp_1.id, "mrp_area_id": self.estimate_area.id} + ) + # Create 1 estimate of 1 week length for the component. + date_start = datetime.today().replace(hour=0) + date_end = date_start + timedelta(days=6) + self._create_demand_estimate(pp_1, self.estimate_loc, date_start, date_end, 7) + date_mo = date_start + timedelta(days=1) + # Create 1 MO for fp_1 that has two pp_1 in its components + mo_form = Form(self.mo_obj) + mo_form.product_id = fp_1 + mo_form.bom_id = fp_1_bom + mo_form.product_qty = 10 + mo_form.date_planned_start = date_mo + mo_form.location_src_id = self.estimate_loc + mo = mo_form.save() + mo.action_confirm() + # Create 1 picking out that represents a Delivery Order from a sale + self._create_picking_out(pp_1, 5, date_mo, location=self.estimate_loc) + # 1. "all" + # Expected result: Consider all sources of demand + self.estimate_area.estimate_demand_and_other_sources_strat = "all" + self.mrp_multi_level_wiz.create( + {"mrp_area_ids": [(6, 0, self.estimate_area.ids)]} + ).run_mrp_multi_level() + moves = self.mrp_move_obj.search( + [ + ("product_id", "=", pp_1.id), + ("mrp_area_id", "=", self.estimate_area.id), + ] + ) + demand_from_estimates = moves.filtered( + lambda m: m.mrp_type == "d" and m.mrp_origin == "fc" + ) + demand_from_other_sources = moves.filtered( + lambda m: m.mrp_type == "d" and m.mrp_origin != "fc" + ) + self.assertEqual(len(demand_from_estimates), 7) + self.assertEqual(sum(demand_from_estimates.mapped("mrp_qty")), -7) + self.assertEqual(len(demand_from_other_sources), 2) + self.assertEqual(sum(demand_from_other_sources.mapped("mrp_qty")), -25) + + # 2. "ignore_others_if_estimates" + # Expected result: Consider estimates and demand from MO + self.estimate_area.estimate_demand_and_other_sources_strat = ( + "ignore_others_if_estimates" + ) + self.mrp_multi_level_wiz.create( + {"mrp_area_ids": [(6, 0, self.estimate_area.ids)]} + ).run_mrp_multi_level() + moves = self.mrp_move_obj.search( + [ + ("product_id", "=", pp_1.id), + ("mrp_area_id", "=", self.estimate_area.id), + ] + ) + demand_from_estimates = moves.filtered( + lambda m: m.mrp_type == "d" and m.mrp_origin == "fc" + ) + demand_from_other_sources = moves.filtered( + lambda m: m.mrp_type == "d" and m.mrp_origin != "fc" + ) + self.assertEqual(len(demand_from_estimates), 7) + self.assertEqual(sum(demand_from_estimates.mapped("mrp_qty")), -7) + self.assertEqual(len(demand_from_other_sources), 1) + self.assertEqual(sum(demand_from_other_sources.mapped("mrp_qty")), -20) + + # 3. "ignore_overlapping" + # Expected result: Consider estimates and demand from MO + self.estimate_area.estimate_demand_and_other_sources_strat = ( + "ignore_overlapping" + ) + self.mrp_multi_level_wiz.create( + {"mrp_area_ids": [(6, 0, self.estimate_area.ids)]} + ).run_mrp_multi_level() + moves = self.mrp_move_obj.search( + [ + ("product_id", "=", pp_1.id), + ("mrp_area_id", "=", self.estimate_area.id), + ] + ) + demand_from_estimates = moves.filtered( + lambda m: m.mrp_type == "d" and m.mrp_origin == "fc" + ) + demand_from_other_sources = moves.filtered( + lambda m: m.mrp_type == "d" and m.mrp_origin != "fc" + ) + self.assertEqual(len(demand_from_estimates), 7) + self.assertEqual(sum(demand_from_estimates.mapped("mrp_qty")), -7) + self.assertEqual(len(demand_from_other_sources), 1) + self.assertEqual(sum(demand_from_other_sources.mapped("mrp_qty")), -20) diff --git a/mrp_multi_level_estimate/wizards/mrp_multi_level.py b/mrp_multi_level_estimate/wizards/mrp_multi_level.py index a57a2d3ed..09a3c5966 100644 --- a/mrp_multi_level_estimate/wizards/mrp_multi_level.py +++ b/mrp_multi_level_estimate/wizards/mrp_multi_level.py @@ -122,8 +122,11 @@ class MultiLevelMrp(models.TransientModel): ) if direction == "out": mrp_date = res.get("mrp_date") - if self._exclude_considering_estimate_demand_and_other_sources_strat( - product_mrp_area, mrp_date + if ( + self._exclude_considering_estimate_demand_and_other_sources_strat( + product_mrp_area, mrp_date + ) + and not res["production_id"] ): return False return res