[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
This commit is contained in:
William Henrotin
2020-08-10 09:40:24 +00:00
parent 68f06c7acc
commit 963ad5eada
4 changed files with 68 additions and 3 deletions
+4 -1
View File
@@ -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)
+6 -1
View File
@@ -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:
+57
View File
@@ -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).
+1 -1
View File
@@ -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