From 2f73020e0813c18205608315038514d05cebe032 Mon Sep 17 00:00:00 2001 From: clesgow Date: Thu, 20 Apr 2023 12:20:51 +0000 Subject: [PATCH] [FIX] mrp: replace workcenter times when specific capacity is defined Currently, when a specific capacity is defined for a product in a workcenter, if a setup / cleanup time is given, it will be added to the existing setup / cleanup time of the workcenter. What we want instead is to be able to fully define what the setup/cleanup time is for this specific product. So the given times in the specific capacity override the times defined in the workcenter for that specific product. To help with that, we set the workcenter's setup/cleanup time as a default for their related specific capacities, since if not changed, they will follow the workcenter times. Updated the tests so their specified capacities match the new behaviour. closes odoo/odoo#119212 Related: odoo/upgrade#4576 Signed-off-by: Tiffany Chang --- addons/mrp/models/mrp_workcenter.py | 18 ++++++---- addons/mrp/tests/test_bom.py | 6 ++-- addons/mrp/tests/test_order.py | 38 ++++++++++++++++++++++ addons/mrp/views/mrp_workcenter_views.xml | 4 +-- addons/mrp_account/tests/test_bom_price.py | 4 +-- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py index 8444985e490..809ef27301d 100644 --- a/addons/mrp/models/mrp_workcenter.py +++ b/addons/mrp/models/mrp_workcenter.py @@ -299,12 +299,10 @@ class MrpWorkcenter(models.Model): def _get_expected_duration(self, product_id): """Compute the expected duration when using this work-center - Always include workcenter startup time and clean-up time. - In case there are specific capacities defined in the workcenter - that matches the product we are producing. Add the extra-time. + Always use the startup / clean-up time from specific capacity if defined. """ capacity = self.capacity_ids.filtered(lambda p: p.product_id == product_id) - return self.time_start + self.time_stop + (capacity.time_start + capacity.time_stop if capacity else 0.0) + return capacity.time_start + capacity.time_stop if capacity else self.time_start + self.time_stop class WorkcenterTag(models.Model): @@ -478,12 +476,20 @@ class MrpWorkCenterCapacity(models.Model): _description = 'Work Center Capacity' _check_company_auto = True + def _default_time_start(self): + workcenter_id = self.workcenter_id.id or self.env.context.get('default_workcenter_id') + return self.env['mrp.workcenter'].browse(workcenter_id).time_start if workcenter_id else 0.0 + + def _default_time_stop(self): + workcenter_id = self.workcenter_id.id or self.env.context.get('default_workcenter_id') + return self.env['mrp.workcenter'].browse(workcenter_id).time_stop if workcenter_id else 0.0 + workcenter_id = fields.Many2one('mrp.workcenter', string='Work Center', required=True) product_id = fields.Many2one('product.product', string='Product', required=True) product_uom_id = fields.Many2one('uom.uom', string='Product UoM', related='product_id.uom_id') capacity = fields.Float('Capacity', default=1.0, help="Number of pieces that can be produced in parallel for this product.") - time_start = fields.Float('Setup Time (minutes)', help="Time in minutes for the setup.") - time_stop = fields.Float('Cleanup Time (minutes)', help="Time in minutes for the cleaning.") + time_start = fields.Float('Setup Time (minutes)', default=_default_time_start, help="Time in minutes for the setup.") + time_stop = fields.Float('Cleanup Time (minutes)', default=_default_time_stop, help="Time in minutes for the cleaning.") _sql_constraints = [ ('positive_capacity', 'CHECK(capacity > 0)', 'Capacity should be a positive number.'), diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index 28691a2c9b2..9685b2ed4be 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -602,8 +602,8 @@ class TestBoM(TestMrpCommon): self.env['mrp.workcenter.capacity'].create({ 'product_id': cheese_cake.id, 'workcenter_id': workcenter_2.id, - 'time_start': 2, - 'time_stop': 1, + 'time_start': 12, + 'time_stop': 16, }) with Form(bom_cheese_cake) as bom: @@ -628,7 +628,7 @@ class TestBoM(TestMrpCommon): # TEST CHEESE BOM STRUCTURE VALUE WITH BOM QUANTITY report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_cheese_cake.id, searchQty=60, searchVariant=False) - #Operation time = 15 min * 60 + time_start + time_stop + capacity_time_start + capacity_time_stop= 928 + # Operation time = 15 min * 60 + capacity_time_start + capacity_time_stop = 928 self.assertEqual(report_values['lines']['operations_time'], 928.0, 'Operation time should be the same for 1 unit or for the batch') # Operation cost is the sum of operation line : (60 * 10)/60 * 10€ + (10 + 15 + 60 * 5)/60 * 20€ + (1 + 2)/60 * 20€ = 209,33€ self.assertEqual(float_compare(report_values['lines']['operations_cost'], 209.33, precision_digits=2), 0) diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 28bdd0b2d7c..962a26b37b5 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -3239,3 +3239,41 @@ class TestMrpOrder(TestMrpCommon): mo_form.picking_type_id = warehouse02.manu_type_id mo_form.bom_id = bom_wh02 self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the default value') + + def test_workcenter_specific_capacities(self): + """ Test that the duraction expected is correctly computed when specific capacities are defined on the workcenter. + """ + # Required for `workorder_ids` to be visible in the view + self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings') + self.workcenter_2.update({ + 'time_start': 10, + 'time_stop': 20, + }) + self.env['mrp.workcenter.capacity'].create({ + 'product_id': self.product_4.id, + 'workcenter_id': self.workcenter_2.id, + 'time_start': 5, + 'time_stop': 10, + }) + + production_form = Form(self.env['mrp.production']) + production_form.product_id = self.product_5 + production = production_form.save() + + with Form(production) as mo_form: + with mo_form.workorder_ids.new() as wo: + wo.name = "OP1" + wo.workcenter_id = self.workcenter_2 + + # Since no duration was given, only duration from the workcenter setup/cleanup time should be added in : 10 + 20 = 30 + self.assertEqual(production.workorder_ids[0].duration_expected, 30.0, "Workcenter setup time (10) + workcenter cleanup time (20)") + + # Change the product so that it uses a specific capacity of that workcenter + with Form(production) as mo_form: + mo_form.product_id = self.product_4 + with mo_form.workorder_ids.new() as wo: + wo.name = "OP1" + wo.workcenter_id = self.workcenter_2 + + # Only duration from the workcenter specific capacity setup/cleanup times since there is one defined for this product. + self.assertEqual(production.workorder_ids[0].duration_expected, 15.0, "Capacity setup time (5) + capacity cleanup time (10)") diff --git a/addons/mrp/views/mrp_workcenter_views.xml b/addons/mrp/views/mrp_workcenter_views.xml index b4d50ee285d..d6ee74f820a 100644 --- a/addons/mrp/views/mrp_workcenter_views.xml +++ b/addons/mrp/views/mrp_workcenter_views.xml @@ -392,8 +392,8 @@ - - + + diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py index e27fc767066..9dac7d1f605 100644 --- a/addons/mrp_account/tests/test_bom_price.py +++ b/addons/mrp_account/tests/test_bom_price.py @@ -124,8 +124,8 @@ class TestBomPrice(TestBomPriceCommon): self.env['mrp.workcenter.capacity'].create({ 'product_id': self.dining_table.id, 'workcenter_id': workcenter_1.id, - 'time_start': 2, - 'time_stop': 1, + 'time_start': 17, + 'time_stop': 16, }) self.bom_1.write({