[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:
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user