From 4f750d8ac9b736debac55682ea84a75c11ede0e6 Mon Sep 17 00:00:00 2001 From: "Touati Djamel (otd)" Date: Thu, 14 Apr 2022 14:17:23 +0000 Subject: [PATCH] [FIX] mrp: use the fastest workcenter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce the bug: - Create the workcenter_1: - Time Efficiency: 100% - Capacity: 2 - Setup Time: 1 - Cleanup Time: 1 - Create the workcenter_2: - Time Efficiency: 100% - Capacity: 4 - Setup Time: 10 - Cleanup Time: 5 - Alternative Work centers: workcenter_1 - Create a storable product “P1” with BOM: - Add any product as component - Add operation: - Duration: 60 - Work center: Workcenter_2 - Create a MO: - select “P1” as Product - Qty to produce: 4 - Confirm and Plan Problem: The workcenter_1 is chosen while work_center_2 is faster it can manufacture the 4 units at the same time while workcenter_1 must do two cycles As the Time Efficiency = 100%, so: Total duration = (Setup Time + Cleanup Time) + duration_by_unit * nb_cycle Work_center_1 = (1 + 1) + 60 * 2 = 122 Work_center_2 = (10 + 5) + 60 = 75 The calculation is bad because we use the number of cycles necessary for the work_center defined in the BOM in the calculation for the alternative work center opw-2821749 closes odoo/odoo#88945 X-original-commit: deb87a6abf8b8ccc4328eaac21430eedc2536361 Signed-off-by: Arnold Moyaux Signed-off-by: Djamel Touati (otd) --- addons/mrp/models/mrp_workorder.py | 3 +- addons/mrp/tests/test_order.py | 77 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index e9732368212..69187efaf94 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -728,7 +728,8 @@ class MrpWorkorder(models.Model): duration_expected_working = (self.duration_expected - self.workcenter_id.time_start - self.workcenter_id.time_stop) * self.workcenter_id.time_efficiency / (100.0 * cycle_number) if duration_expected_working < 0: duration_expected_working = 0 - return alternative_workcenter.time_start + alternative_workcenter.time_stop + cycle_number * duration_expected_working * 100.0 / alternative_workcenter.time_efficiency + alternative_wc_cycle_nb = float_round(qty_production / alternative_workcenter.capacity, precision_digits=0, rounding_method='UP') + return alternative_workcenter.time_start + alternative_workcenter.time_stop + alternative_wc_cycle_nb * duration_expected_working * 100.0 / alternative_workcenter.time_efficiency time_cycle = self.operation_id.time_cycle return self.workcenter_id.time_start + self.workcenter_id.time_stop + cycle_number * time_cycle * 100.0 / self.workcenter_id.time_efficiency diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index c58488c8fb5..304e461aa88 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -2651,3 +2651,80 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(mo_3.state, 'to_close') mo_3.button_mark_done() self.assertEqual(mo_3.state, 'done') + + def test_planning_workorder(self): + """ + Check that the fastest work center is used when planning the workorder. + - create two work centers with similar production capacity + but the work_center_2 with a longer start and stop time. + 1:/ produce 2 units > work_center_1 faster because + it does not need much time to start and to finish the production. + 2/ - update the production capacity of the work_center_2 to 4 + - produce 4 units > work_center_2 faster because + it must do a single cycle while the work_center_1 have to do two cycles. + """ + workcenter_1 = self.env['mrp.workcenter'].create({ + 'name': 'wc1', + 'capacity': 2, + 'time_start': 1, + 'time_stop': 1, + 'time_efficiency': 100, + }) + + workcenter_2 = self.env['mrp.workcenter'].create({ + 'name': 'wc2', + 'capacity': 2, + 'time_start': 10, + 'time_stop': 5, + 'time_efficiency': 100, + 'alternative_workcenter_ids': [workcenter_1.id] + }) + + product_to_build = self.env['product.product'].create({ + 'name': 'final product', + 'type': 'product', + }) + + product_to_use = self.env['product.product'].create({ + 'name': 'component', + 'type': 'product', + }) + + bom = self.env['mrp.bom'].create({ + 'product_id': product_to_build.id, + 'product_tmpl_id': product_to_build.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 1.0, + 'type': 'normal', + 'consumption': 'flexible', + 'operation_ids': [ + (0, 0, {'name': 'Test', 'workcenter_id': workcenter_2.id, 'time_cycle': 60, 'sequence': 1}), + ], + 'bom_line_ids': [ + (0, 0, {'product_id': product_to_use.id, 'product_qty': 1}), + ]}) + + #MO_1 + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = product_to_build + mo_form.bom_id = bom + mo_form.product_qty = 2 + mo = mo_form.save() + mo.action_confirm() + mo.button_plan() + self.assertEqual(mo.workorder_ids[0].workcenter_id.id, workcenter_1.id, 'workcenter_1 is faster than workcenter_2 to manufacture 2 units') + # Unplan the mo to prevent the first workcenter from being busy + mo.button_unplan() + + # Update the production capcity + workcenter_2.capacity = 4 + + #MO_2 + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = product_to_build + mo_form.bom_id = bom + mo_form.product_qty = 4 + mo_2 = mo_form.save() + mo_2.action_confirm() + mo_2.button_plan() + self.assertEqual(mo_2.workorder_ids[0].workcenter_id.id, workcenter_2.id, 'workcenter_2 is faster than workcenter_1 to manufacture 4 units')