[FIX] mrp: cancelling and validating impact on planning
The commit 0679d0d introduced a new
planning mechanism for the workorder. Two issues have been instroduced
since this patch.
1. Cancelling a workorder didn't free the time slot in the workcenter
calendar
2. Validation a workorder sooner than the planned date didn't update
the workcenter calendar to plan another production sooner.
This commit update the date_planned_start and date_planned_finished
at cancellation and validation to impact the workcenters calendar.
To keep the original planning dates, they are now tracked
This commit also fix the button_start() method that always rewrited the
date_start even if the production had already been started.
Task : 2082442
closes odoo/odoo#38947
X-original-commit: 4ca6c773a00320865142e4efb3645f36c54ce9a2
Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
This commit is contained in:
@@ -69,13 +69,15 @@ class MrpWorkorder(models.Model):
|
||||
compute='_compute_dates_planned',
|
||||
inverse='_set_dates_planned',
|
||||
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
|
||||
store=True)
|
||||
store=True,
|
||||
tracking=True)
|
||||
date_planned_finished = fields.Datetime(
|
||||
'Scheduled Date Finished',
|
||||
compute='_compute_dates_planned',
|
||||
inverse='_set_dates_planned',
|
||||
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
|
||||
store=True)
|
||||
store=True,
|
||||
tracking=True)
|
||||
date_start = fields.Datetime(
|
||||
'Effective Start Date',
|
||||
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
|
||||
@@ -541,14 +543,28 @@ class MrpWorkorder(models.Model):
|
||||
'user_id': self.env.user.id, # FIXME sle: can be inconsistent with company_id
|
||||
'company_id': self.company_id.id,
|
||||
})
|
||||
return self.write({'state': 'progress',
|
||||
'date_start': datetime.now(),
|
||||
})
|
||||
if self.state == 'progress':
|
||||
return True
|
||||
else:
|
||||
start_date = datetime.now()
|
||||
vals = {
|
||||
'state': 'progress',
|
||||
'date_start': start_date,
|
||||
'date_planned_start': start_date,
|
||||
}
|
||||
if self.date_planned_finished < start_date:
|
||||
vals['date_planned_finished'] = start_date
|
||||
return self.write(vals)
|
||||
|
||||
def button_finish(self):
|
||||
self.ensure_one()
|
||||
self.end_all()
|
||||
return self.write({'state': 'done', 'date_finished': fields.Datetime.now()})
|
||||
end_date = datetime.now()
|
||||
return self.write({
|
||||
'state': 'done',
|
||||
'date_finished': end_date,
|
||||
'date_planned_finished': end_date
|
||||
})
|
||||
|
||||
def end_previous(self, doall=False):
|
||||
"""
|
||||
@@ -595,14 +611,19 @@ class MrpWorkorder(models.Model):
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
self.leave_id.unlink()
|
||||
return self.write({'state': 'cancel'})
|
||||
|
||||
def button_done(self):
|
||||
if any([x.state in ('done', 'cancel') for x in self]):
|
||||
raise UserError(_('A Manufacturing Order is already done or cancelled.'))
|
||||
self.end_all()
|
||||
return self.write({'state': 'done',
|
||||
'date_finished': datetime.now()})
|
||||
end_date = datetime.now()
|
||||
return self.write({
|
||||
'state': 'done',
|
||||
'date_finished': end_date,
|
||||
'date_planned_finished': end_date,
|
||||
})
|
||||
|
||||
def button_scrap(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -410,6 +410,7 @@ class TestWorkOrderProcess(TestMrpCommon):
|
||||
|
||||
# first operation of main bom
|
||||
finished_lot = self.env['stock.production.lot'].create({'product_id': man_order.product_id.id, 'company_id': self.env.company.id})
|
||||
door_wo_1.button_start()
|
||||
door_wo_1.write({
|
||||
'finished_lot_id': finished_lot.id,
|
||||
'qty_producing': 48
|
||||
@@ -419,6 +420,7 @@ class TestWorkOrderProcess(TestMrpCommon):
|
||||
|
||||
# second operation of main bom
|
||||
self.assertEqual(door_wo_2.state, 'ready', "Workorder should be in ready state.")
|
||||
door_wo_2.button_start()
|
||||
door_wo_2.record_production()
|
||||
self.assertEqual(door_wo_2.state, 'done', "Workorder should be in done state.")
|
||||
|
||||
@@ -751,6 +753,7 @@ class TestWorkOrderProcess(TestMrpCommon):
|
||||
self.assertEqual(workorders[0].state, 'done')
|
||||
|
||||
for workorder in workorders - workorders[0]:
|
||||
workorder.button_start()
|
||||
self.assertEqual(workorder.finished_lot_id, serial_a)
|
||||
workorder.record_production()
|
||||
self.assertEqual(workorder.finished_lot_id, serial_b)
|
||||
@@ -797,12 +800,12 @@ class TestWorkOrderProcess(TestMrpCommon):
|
||||
mo.button_plan()
|
||||
|
||||
workorder_0 = mo.workorder_ids[0]
|
||||
workorder_0.button_start()
|
||||
workorder_0.record_production()
|
||||
workorder_0.record_production()
|
||||
with self.assertRaises(UserError):
|
||||
workorder_0.record_production()
|
||||
|
||||
workorder_1 = mo.workorder_ids[1]
|
||||
workorder_1.button_start()
|
||||
with Form(workorder_1) as wo:
|
||||
wo.finished_lot_id = lot_1
|
||||
workorder_1.record_production()
|
||||
@@ -1043,6 +1046,59 @@ class TestWorkOrderProcess(TestMrpCommon):
|
||||
with self.assertRaises(UserError):
|
||||
mo.button_plan()
|
||||
|
||||
def test_planning_5(self):
|
||||
""" Cancelling a production with workorders should free all reserved slot
|
||||
in the related workcenters calendars """
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.product_4
|
||||
mo_form.bom_id = self.planning_bom
|
||||
mo_form.product_qty = 1
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
mo.button_plan()
|
||||
|
||||
mo.action_cancel()
|
||||
self.assertEqual(mo.workorder_ids.mapped('date_start'), [False])
|
||||
self.assertEqual(mo.workorder_ids.mapped('date_finished'), [False])
|
||||
|
||||
def test_planning_6(self):
|
||||
""" Marking a workorder as done before the theoretical date should update
|
||||
the reservation slot in the calendar the be able to reserve the next
|
||||
production sooner """
|
||||
self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2
|
||||
self.env['mrp.workcenter'].search([]).write({'tz': 'UTC'}) # compute all date in UTC
|
||||
self.planning_bom.routing = self.env.ref("mrp.mrp_routing_0")
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.product_4
|
||||
mo_form.bom_id = self.planning_bom
|
||||
mo_form.product_qty = 1
|
||||
mo_form.date_start_wo = datetime(2019, 5, 13, 9, 0, 0, 0)
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
mo.button_plan()
|
||||
wo = mo.workorder_ids
|
||||
self.assertAlmostEqual(wo.date_planned_start, datetime(2019, 5, 13, 9, 0, 0, 0), delta=timedelta(seconds=10))
|
||||
self.assertAlmostEqual(wo.date_planned_finished, datetime(2019, 5, 13, 9, 0, 0, 0) + timedelta(minutes=85.58), delta=timedelta(seconds=10))
|
||||
wo.button_start()
|
||||
wo.record_production()
|
||||
# Marking workorder as done should change the finished date
|
||||
self.assertAlmostEqual(wo.date_finished, datetime.now(), delta=timedelta(seconds=10))
|
||||
self.assertAlmostEqual(wo.date_planned_finished, datetime.now(), delta=timedelta(seconds=10))
|
||||
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.product_4
|
||||
mo_form.bom_id = self.planning_bom
|
||||
mo_form.product_qty = 1
|
||||
mo_form.date_start_wo = datetime(2019, 5, 13, 9, 0, 0, 0)
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
mo.button_plan()
|
||||
wo = mo.workorder_ids
|
||||
wo.button_start()
|
||||
self.assertAlmostEqual(wo.date_start, datetime.now(), delta=timedelta(seconds=10))
|
||||
self.assertAlmostEqual(wo.date_planned_start, datetime.now(), delta=timedelta(seconds=10))
|
||||
self.assertAlmostEqual(wo.date_planned_finished, datetime.now(), delta=timedelta(seconds=10))
|
||||
|
||||
def test_plan_unplan_date(self):
|
||||
""" Testing planning a workorder then cancel it and then plan it again.
|
||||
The planned date must be the same the first time and the second time the
|
||||
|
||||
Reference in New Issue
Block a user