From 963ad5eada35f22cc074b4becf4e55ac5edad63b Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Mon, 27 Jul 2020 13:17:46 +0000 Subject: [PATCH] [FIX] mrp: keep duration ratio Updating the quantity to produce on a production order will recompute the expected production duration. This can be counter productive on prototyping production when no BoM are given. The default expected batch duration is set to 60 minutes. Setting the actual production time then updating the quantity to produce will naively change the duration to 60 x the new quantity. This commit compute the duration pro rata in case of 'no BoM' production and recompute everything in case of BoM production. Task : 2278147 --- addons/mrp/models/mrp_production.py | 5 +- addons/mrp/models/mrp_workorder.py | 7 ++- addons/mrp/tests/test_order.py | 57 ++++++++++++++++++++++ addons/mrp/wizard/change_production_qty.py | 2 +- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 8594263bc64..5afd01bd5cc 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -574,7 +574,10 @@ class MrpProduction(models.Model): def _onchange_product_qty(self): for workorder in self.workorder_ids: workorder.product_uom_id = self.product_uom_id - workorder.duration_expected = workorder._get_duration_expected() + if self._origin.product_qty: + workorder.duration_expected = workorder._get_duration_expected(ratio=self.product_qty / self._origin.product_qty) + else: + workorder.duration_expected = workorder._get_duration_expected() if workorder.date_planned_start and workorder.duration_expected: workorder.date_planned_finished = workorder.date_planned_start + relativedelta(minutes=workorder.duration_expected) diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index f100c361984..5ffb7ffc860 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -671,10 +671,15 @@ class MrpWorkorder(models.Model): for wo in self: wo.qty_remaining = float_round(wo.qty_production - wo.qty_produced, precision_rounding=wo.production_id.product_uom_id.rounding) - def _get_duration_expected(self, alternative_workcenter=False): + def _get_duration_expected(self, alternative_workcenter=False, ratio=1): self.ensure_one() if not self.workcenter_id: return False + if not self.operation_id: + duration_expected_working = (self.duration_expected - self.workcenter_id.time_start - self.workcenter_id.time_stop) * self.workcenter_id.time_efficiency / 100.0 + if duration_expected_working < 0: + duration_expected_working = 0 + return self.workcenter_id.time_start + self.workcenter_id.time_stop + duration_expected_working * ratio * 100.0 / self.workcenter_id.time_efficiency qty_production = self.production_id.product_uom_id._compute_quantity(self.qty_production, self.production_id.product_id.uom_id) cycle_number = float_round(qty_production / self.workcenter_id.capacity, precision_digits=0, rounding_method='UP') if alternative_workcenter: diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index cd7015a90ce..3dfa1500625 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -259,6 +259,63 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).mapped('quantity_done')), 20) self.assertEqual(sum(mo.move_finished_ids.mapped('quantity_done')), 5) + def test_update_quantity_3(self): + bom = self.env['mrp.bom'].create({ + 'product_id': self.product_6.id, + 'product_tmpl_id': self.product_6.product_tmpl_id.id, + 'product_qty': 1, + 'product_uom_id': self.product_6.uom_id.id, + 'type': 'normal', + 'bom_line_ids': [ + (0, 0, {'product_id': self.product_2.id, 'product_qty': 2.03}), + (0, 0, {'product_id': self.product_8.id, 'product_qty': 4.16}) + ], + 'operation_ids': [ + (0, 0, {'name': 'Gift Wrap Maching', 'workcenter_id': self.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}), + ] + }) + production_form = Form(self.env['mrp.production']) + production_form.product_id = self.product_6 + production_form.bom_id = bom + production_form.product_qty = 1 + production_form.product_uom_id = self.product_6.uom_id + production = production_form.save() + self.assertEqual(production.workorder_ids.duration_expected, 90) + mo_form = Form(production) + mo_form.product_qty = 3 + production = mo_form.save() + self.assertEqual(production.workorder_ids.duration_expected, 165) + + def test_update_quantity_4(self): + bom = self.env['mrp.bom'].create({ + 'product_id': self.product_6.id, + 'product_tmpl_id': self.product_6.product_tmpl_id.id, + 'product_qty': 1, + 'product_uom_id': self.product_6.uom_id.id, + 'type': 'normal', + 'bom_line_ids': [ + (0, 0, {'product_id': self.product_2.id, 'product_qty': 2.03}), + (0, 0, {'product_id': self.product_8.id, 'product_qty': 4.16}) + ], + }) + production_form = Form(self.env['mrp.production']) + production_form.product_id = self.product_6 + production_form.bom_id = bom + production_form.product_qty = 1 + production_form.product_uom_id = self.product_6.uom_id + production = production_form.save() + production_form = Form(production) + with production_form.workorder_ids.new() as wo: + wo.name = 'OP1' + wo.workcenter_id = self.workcenter_1 + wo.duration_expected = 40 + production = production_form.save() + self.assertEqual(production.workorder_ids.duration_expected, 40) + mo_form = Form(production) + mo_form.product_qty = 3 + production = mo_form.save() + self.assertEqual(production.workorder_ids.duration_expected, 90) + def test_rounding(self): """ Checks we round up when bringing goods to produce and round half-up when producing. This implementation allows to implement an efficiency notion (see rev 347f140fe63612ee05e). diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index cd82ca389f6..9170d3eff2d 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -78,7 +78,7 @@ class ChangeProductionQty(models.TransientModel): for wo in production.workorder_ids: operation = wo.operation_id - wo._onchange_expected_duration() + wo.duration_expected = wo._get_duration_expected(ratio=new_production_qty / old_production_qty) quantity = wo.qty_production - wo.qty_produced if production.product_id.tracking == 'serial': quantity = 1.0 if not float_is_zero(quantity, precision_digits=precision) else 0.0