From cf9ff8e41a05bdaf6d46d9f6753c5f56ed19fb1e Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Tue, 12 Apr 2022 15:35:34 +0000 Subject: [PATCH] [FIX] mrp: orderpoint for manufacture not trigger twice Usecase to reproduce: With BoM structure - Product A (RR 1-20) | Product B (MTO) | Product C (RR 5-50) | Product D (Buy) Launch the RR. The MO are: Current Behavior - Product A 20 - Product B 20 - Product C 50 - Product C 70 Expected Behavior - Product A 20 - Product B 20 - Product C 50 It happens because the MO are created as draft during the `_run_manufacture` and are confirm at the end of `_procure_orderpoint_confirm` in '_post_process_scheduler`. However at the first MO for product A, it creates a MO for product B and since it consumes product c, it checks if the orderpoint should be trigger. MO for product C still "draft" and the orderpoint forecast is now -20, so it will create a new procurement for product C with 70 units To fix it we take the MO draft linked to the orderpoint for the forecasted quantity. closes odoo/odoo#89079 X-original-commit: 199a0dd0f013655a3c2345a41bc7c721e09bfc68 Signed-off-by: Arnold Moyaux Signed-off-by: William Henrotin (whe) --- addons/mrp/models/stock_orderpoint.py | 15 ++++- addons/mrp/tests/test_procurement.py | 80 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/addons/mrp/models/stock_orderpoint.py b/addons/mrp/models/stock_orderpoint.py index 89662bdf72f..583a09df5c9 100644 --- a/addons/mrp/models/stock_orderpoint.py +++ b/addons/mrp/models/stock_orderpoint.py @@ -50,7 +50,8 @@ class StockWarehouseOrderpoint(models.Model): for orderpoint in self if orderpoint.product_id in bom_kits } - res = super(StockWarehouseOrderpoint, self.filtered(lambda p: p not in bom_kit_orderpoints))._quantity_in_progress() + orderpoints_without_kit = self - self.env['stock.warehouse.orderpoint'].concat(*bom_kit_orderpoints.keys()) + res = super(StockWarehouseOrderpoint, orderpoints_without_kit)._quantity_in_progress() for orderpoint in bom_kit_orderpoints: dummy, bom_sub_lines = bom_kit_orderpoints[orderpoint].explode(orderpoint.product_id, 1) ratios_qty_available = [] @@ -73,6 +74,18 @@ class StockWarehouseOrderpoint(models.Model): # (the quantity if we have received all in-progress components) - (the quantity using only available components) product_qty = min(ratios_total or [0]) - min(ratios_qty_available or [0]) res[orderpoint.id] = orderpoint.product_id.uom_id._compute_quantity(product_qty, orderpoint.product_uom, round=False) + + bom_manufacture = self.env['mrp.bom']._bom_find(orderpoints_without_kit.product_id, bom_type='normal') + bom_manufacture = self.env['mrp.bom'].concat(*bom_manufacture.values()) + productions_group = self.env['mrp.production'].read_group( + [('bom_id', 'in', bom_manufacture.ids), ('state', '=', 'draft'), ('orderpoint_id', 'in', orderpoints_without_kit.ids)], + ['orderpoint_id', 'product_qty', 'product_uom_id'], + ['orderpoint_id', 'product_uom_id'], lazy=False) + for p in productions_group: + uom = self.env['uom.uom'].browse(p['product_uom_id'][0]) + orderpoint = self.env['stock.warehouse.orderpoint'].browse(p['orderpoint_id'][0]) + res[orderpoint.id] += uom._compute_quantity( + p['product_qty'], orderpoint.product_uom, round=False) return res def _get_qty_multiple_to_order(self): diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py index e18e03b841c..a6307701776 100644 --- a/addons/mrp/tests/test_procurement.py +++ b/addons/mrp/tests/test_procurement.py @@ -596,3 +596,83 @@ class TestProcurement(TestMrpCommon): self.assertEqual(manufacturing_order.product_qty, 10, 'The demand on the initial manufacturing order should not have been increased.') manufacturing_orders = self.env['mrp.production'].search([('product_id', '=', product.id)]) self.assertEqual(len(manufacturing_orders), 2, 'A new MO should have been created for missing demand.') + + def test_rr_with_dependance_between_bom(self): + self.warehouse = self.env.ref('stock.warehouse0') + route_mto = self.warehouse.mto_pull_id.route_id + route_mto.active = True + route_manufacture = self.warehouse.manufacture_pull_id.route_id + product_1 = self.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + 'route_ids': [(6, 0, [route_manufacture.id])] + }) + product_2 = self.env['product.product'].create({ + 'name': 'Product B', + 'type': 'product', + 'route_ids': [(6, 0, [route_manufacture.id, route_mto.id])] + }) + product_3 = self.env['product.product'].create({ + 'name': 'Product B', + 'type': 'product', + 'route_ids': [(6, 0, [route_manufacture.id])] + }) + product_4 = self.env['product.product'].create({ + 'name': 'Product C', + 'type': 'consu', + }) + + op1 = self.env['stock.warehouse.orderpoint'].create({ + 'name': 'Product A', + 'location_id': self.warehouse.lot_stock_id.id, + 'product_id': product_1.id, + 'product_min_qty': 1, + 'product_max_qty': 20, + }) + + op2 = self.env['stock.warehouse.orderpoint'].create({ + 'name': 'Product B', + 'location_id': self.warehouse.lot_stock_id.id, + 'product_id': product_3.id, + 'product_min_qty': 5, + 'product_max_qty': 50, + }) + + self.env['mrp.bom'].create({ + 'product_id': product_1.id, + 'product_tmpl_id': product_1.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 1, + 'consumption': 'flexible', + 'type': 'normal', + 'bom_line_ids': [(0, 0, {'product_id': product_2.id, 'product_qty': 1})] + }) + + self.env['mrp.bom'].create({ + 'product_id': product_2.id, + 'product_tmpl_id': product_2.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 1, + 'consumption': 'flexible', + 'type': 'normal', + 'bom_line_ids': [(0, 0, {'product_id': product_3.id, 'product_qty': 1})] + }) + + self.env['mrp.bom'].create({ + 'product_id': product_3.id, + 'product_tmpl_id': product_3.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 1, + 'consumption': 'flexible', + 'type': 'normal', + 'bom_line_ids': [(0, 0, {'product_id': product_4.id, 'product_qty': 1})] + }) + + (op1 | op2)._procure_orderpoint_confirm() + mo1 = self.env['mrp.production'].search([('product_id', '=', product_1.id)]) + mo3 = self.env['mrp.production'].search([('product_id', '=', product_3.id)]) + + self.assertEqual(len(mo1), 1) + self.assertEqual(len(mo3), 1) + self.assertEqual(mo1.product_qty, 20) + self.assertEqual(mo3.product_qty, 50)