[IMP] mrp*: Merge workorder and production scheduled/effective dates

Since starting a workorder and marking it as done replaces the
planned dates with the actual ones,
there is no need to keep dates which will always end up being the same.

task: 3108291

see odoo/enterprise#36102
see odoo/upgrade#4247

closes odoo/odoo#110550

Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
This commit is contained in:
JF Aubert
2023-03-14 11:07:56 +01:00
parent d9d47166de
commit d4ed9f8a50
22 changed files with 261 additions and 271 deletions
+1 -1
View File
@@ -283,7 +283,7 @@
<field name="product_id" ref="product_product_computer_desk"/>
<field name="product_uom_id" ref="uom.product_uom_unit"/>
<field name="product_qty">1</field>
<field name="date_planned_start" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_start" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="bom_id" ref="mrp_bom_desk"/>
</record>
+63 -63
View File
@@ -24,15 +24,25 @@ class MrpProduction(models.Model):
""" Manufacturing Orders """
_name = 'mrp.production'
_description = 'Production Order'
_date_name = 'date_planned_start'
_date_name = 'date_start'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'priority desc, date_planned_start asc,id'
_order = 'priority desc, date_start asc,id'
@api.model
def _get_default_date_planned_start(self):
def _get_default_date_start(self):
if self.env.context.get('default_date_deadline'):
date_finished = fields.Datetime.to_datetime(self.env.context.get('default_date_deadline'))
date_start = date_finished - relativedelta(hours=1)
return date_start
return fields.Datetime.now()
@api.model
def _get_default_date_finished(self):
if self.env.context.get('default_date_deadline'):
return fields.Datetime.to_datetime(self.env.context.get('default_date_deadline'))
return fields.Datetime.now()
date_start = fields.Datetime.now()
date_finished = date_start + relativedelta(hours=1)
return date_finished
@api.model
def _get_default_is_locked(self):
@@ -98,23 +108,19 @@ class MrpProduction(models.Model):
readonly=False, required=True, precompute=True,
domain="[('usage','=','internal'), '|', ('company_id', '=', False), ('company_id', '=', company_id)]",
help="Location where the system will stock the finished products.")
date_planned_start = fields.Datetime(
'Scheduled Date', copy=False, default=_get_default_date_planned_start,
help="Date at which you plan to start the production.",
index=True, required=True)
date_planned_finished = fields.Datetime(
'Scheduled End Date',
compute='_compute_date_planned_finished', store=True,
help="Date at which you plan to finish the production.",
copy=False)
date_deadline = fields.Datetime(
'Deadline', copy=False, store=True, readonly=True, compute='_compute_date_deadline',
help="Informative date allowing to define when the manufacturing order should be processed at the latest to fulfill delivery on time.")
date_start = fields.Datetime('Start Date', copy=False, readonly=True)
date_finished = fields.Datetime('End Date', copy=False, readonly=True)
production_duration_expected = fields.Float("Expected Duration", help="Total expected duration (in minutes)", compute='_compute_production_duration_expected')
production_real_duration = fields.Float("Real Duration", help="Total real duration (in minutes)", compute='_compute_production_real_duration')
date_start = fields.Datetime(
'Start', copy=False, default=_get_default_date_start,
help="Date you plan to start production or date you actually started production.",
index=True, required=True)
date_finished = fields.Datetime(
'End', copy=False, default=_get_default_date_finished,
compute='_compute_date_finished', store=True,
help="Date you expect to finish production or actual date you finished production.")
duration_expected = fields.Float("Expected Duration", help="Total expected duration (in minutes)", compute='_compute_duration_expected')
duration = fields.Float("Real Duration", help="Total real duration (in minutes)", compute='_compute_duration')
bom_id = fields.Many2one(
'mrp.bom', 'Bill of Material', readonly=False,
@@ -305,7 +311,7 @@ class MrpProduction(models.Model):
production.location_src_id = production.picking_type_id.default_location_src_id.id or fallback_loc.id
production.location_dest_id = production.picking_type_id.default_location_dest_id.id or fallback_loc.id
@api.depends('state', 'reservation_state', 'date_planned_start', 'move_raw_ids', 'move_raw_ids.forecast_availability', 'move_raw_ids.forecast_expected_date')
@api.depends('state', 'reservation_state', 'date_start', 'move_raw_ids', 'move_raw_ids.forecast_availability', 'move_raw_ids.forecast_expected_date')
def _compute_components_availability(self):
productions = self.filtered(lambda mo: mo.state not in ('cancel', 'done', 'draft'))
productions.components_availability_state = 'available'
@@ -326,8 +332,8 @@ class MrpProduction(models.Model):
forecast_date = max(production.move_raw_ids.filtered('forecast_expected_date').mapped('forecast_expected_date'), default=False)
if forecast_date:
production.components_availability = _('Exp %s', format_date(self.env, forecast_date))
if production.date_planned_start:
production.components_availability_state = 'late' if forecast_date > production.date_planned_start else 'expected'
if production.date_start:
production.components_availability_state = 'late' if forecast_date > production.date_start else 'expected'
@api.depends('bom_id')
def _compute_product_id(self):
@@ -378,20 +384,20 @@ class MrpProduction(models.Model):
production.date_deadline = min(production.move_finished_ids.filtered('date_deadline').mapped('date_deadline'), default=production.date_deadline or False)
@api.depends('workorder_ids.duration_expected')
def _compute_production_duration_expected(self):
def _compute_duration_expected(self):
for production in self:
production.production_duration_expected = sum(production.workorder_ids.mapped('duration_expected'))
production.duration_expected = sum(production.workorder_ids.mapped('duration_expected'))
@api.depends('workorder_ids.duration')
def _compute_production_real_duration(self):
def _compute_duration(self):
for production in self:
production.production_real_duration = sum(production.workorder_ids.mapped('duration'))
production.duration = sum(production.workorder_ids.mapped('duration'))
@api.depends("workorder_ids.date_planned_start", "workorder_ids.date_planned_finished")
@api.depends("workorder_ids.date_start", "workorder_ids.date_finished")
def _compute_is_planned(self):
for production in self:
if production.workorder_ids:
production.is_planned = any(wo.date_planned_start and wo.date_planned_finished for wo in production.workorder_ids)
production.is_planned = any(wo.date_start and wo.date_finished for wo in production.workorder_ids)
else:
production.is_planned = False
@@ -661,13 +667,13 @@ class MrpProduction(models.Model):
('move_orig_ids', 'in', lines.ids)], limit=1):
mo.show_allocation = True
@api.depends('product_uom_qty', 'date_planned_start')
@api.depends('product_uom_qty', 'date_start')
def _compute_forecasted_issue(self):
for order in self:
warehouse = order.location_dest_id.warehouse_id
order.forecasted_issue = False
if order.product_id:
virtual_available = order.product_id.with_context(warehouse=warehouse.id, to_date=order.date_planned_start).virtual_available
virtual_available = order.product_id.with_context(warehouse=warehouse.id, to_date=order.date_start).virtual_available
if order.state == 'draft':
virtual_available += order.product_uom_qty
if virtual_available < 0:
@@ -678,18 +684,18 @@ class MrpProduction(models.Model):
late_stock_moves = self.env['stock.move'].search([('delay_alert_date', operator, value)])
return ['|', ('move_raw_ids', 'in', late_stock_moves.ids), ('move_finished_ids', 'in', late_stock_moves.ids)]
@api.depends('company_id', 'date_planned_start', 'is_planned', 'product_id')
def _compute_date_planned_finished(self):
@api.depends('company_id', 'date_start', 'is_planned', 'product_id')
def _compute_date_finished(self):
for production in self:
if not production.date_planned_start or production.is_planned:
if not production.date_start or production.is_planned:
continue
days_delay = production.bom_id.produce_delay
date_planned_finished = production.date_planned_start + relativedelta(days=days_delay)
if date_planned_finished == production.date_planned_start:
date_planned_finished = date_planned_finished + relativedelta(hours=1)
production.date_planned_finished = date_planned_finished
date_finished = production.date_start + relativedelta(days=days_delay)
if date_finished == production.date_start:
date_finished = date_finished + relativedelta(hours=1)
production.date_finished = date_finished
@api.depends('company_id', 'bom_id', 'product_id', 'product_qty', 'product_uom_id', 'location_src_id', 'date_planned_start')
@api.depends('company_id', 'bom_id', 'product_id', 'product_qty', 'product_uom_id', 'location_src_id', 'date_start')
def _compute_move_raw_ids(self):
for production in self:
if production.state != 'draft':
@@ -714,13 +720,13 @@ class MrpProduction(models.Model):
else:
production.move_raw_ids = [Command.delete(move.id) for move in production.move_raw_ids.filtered(lambda m: m.bom_line_id)]
@api.depends('product_id', 'bom_id', 'product_qty', 'product_uom_id', 'location_dest_id', 'date_planned_finished')
@api.depends('product_id', 'bom_id', 'product_qty', 'product_uom_id', 'location_dest_id', 'date_finished')
def _compute_move_finished_ids(self):
for production in self:
if production.state != 'draft':
updated_values = {}
if production.date_planned_finished:
updated_values['date'] = production.date_planned_finished
if production.date_finished:
updated_values['date'] = production.date_finished
if production.date_deadline:
updated_values['date_deadline'] = production.date_deadline
if 'date' in updated_values or 'date_deadline' in updated_values:
@@ -800,15 +806,15 @@ class MrpProduction(models.Model):
res = super(MrpProduction, self).write(vals)
for production in self:
if 'date_planned_start' in vals and not self.env.context.get('force_date', False):
if 'date_start' in vals and not self.env.context.get('force_date', False):
if production.state in ['done', 'cancel']:
raise UserError(_('You cannot move a manufacturing order once it is cancelled or done.'))
if production.is_planned:
production.button_unplan()
if vals.get('date_planned_start'):
production.move_raw_ids.write({'date': production.date_planned_start, 'date_deadline': production.date_planned_start})
if vals.get('date_planned_finished'):
production.move_finished_ids.write({'date': production.date_planned_finished})
if vals.get('date_start'):
production.move_raw_ids.write({'date': production.date_start, 'date_deadline': production.date_start})
if vals.get('date_finished'):
production.move_finished_ids.write({'date': production.date_finished})
if any(field in ['move_raw_ids', 'move_finished_ids', 'workorder_ids'] for field in vals) and production.state != 'draft':
production._autoconfirm_production()
if production in production_to_replan:
@@ -820,10 +826,10 @@ class MrpProduction(models.Model):
finished_move_lines.write({'lot_id': vals.get('lot_producing_id')})
if 'qty_producing' in vals:
finished_move_lines.write({'qty_done': vals.get('qty_producing')})
if self._has_workorders() and not production.workorder_ids.operation_id and vals.get('date_planned_start') and not vals.get('date_planned_finished'):
new_date_planned_start = fields.Datetime.to_datetime(vals.get('date_planned_start'))
if not production.date_planned_finished or new_date_planned_start >= production.date_planned_finished:
production.date_planned_finished = new_date_planned_start + datetime.timedelta(hours=1)
if self._has_workorders() and not production.workorder_ids.operation_id and vals.get('date_start') and not vals.get('date_finished'):
new_date_start = fields.Datetime.to_datetime(vals.get('date_start'))
if not production.date_finished or new_date_start >= production.date_finished:
production.date_finished = new_date_start + datetime.timedelta(hours=1)
return res
@api.model_create_multi
@@ -934,7 +940,7 @@ class MrpProduction(models.Model):
'operation_id': operation_id,
'byproduct_id': byproduct_id,
'name': _('New'),
'date': self._get_date_planned_finished(),
'date': self.date_finished,
'date_deadline': self.date_deadline,
'picking_type_id': self.picking_type_id.id,
'location_id': self.product_id.with_company(self.company_id).property_stock_production.id,
@@ -949,12 +955,6 @@ class MrpProduction(models.Model):
'cost_share': cost_share,
}
def _get_date_planned_finished(self):
date_planned_finished = self.date_planned_start + relativedelta(days=self.bom_id.produce_delay)
if date_planned_finished == self.date_planned_start:
date_planned_finished = date_planned_finished + relativedelta(hours=1)
return date_planned_finished
def _get_moves_finished_values(self):
moves = []
for production in self:
@@ -1021,8 +1021,8 @@ class MrpProduction(models.Model):
data = {
'sequence': bom_line.sequence if bom_line else 10,
'name': _('New'),
'date': self.date_planned_start,
'date_deadline': self.date_planned_start,
'date': self.date_start,
'date_deadline': self.date_start,
'bom_line_id': bom_line.id if bom_line else False,
'picking_type_id': self.picking_type_id.id,
'product_id': product_id.id,
@@ -1307,8 +1307,8 @@ class MrpProduction(models.Model):
workorders = self.workorder_ids.filtered(lambda w: w.state not in ['done', 'cancel'])
self.with_context(force_date=True).write({
'date_planned_start': min([workorder.leave_id.date_from for workorder in workorders]),
'date_planned_finished': max([workorder.leave_id.date_to for workorder in workorders])
'date_start': min([workorder.leave_id.date_from for workorder in workorders]),
'date_finished': max([workorder.leave_id.date_to for workorder in workorders])
})
def button_unplan(self):
@@ -1319,8 +1319,8 @@ class MrpProduction(models.Model):
self.workorder_ids.leave_id.unlink()
self.workorder_ids.write({
'date_planned_start': False,
'date_planned_finished': False,
'date_start': False,
'date_finished': False,
})
def _get_consumption_issues(self):
@@ -2050,7 +2050,7 @@ class MrpProduction(models.Model):
self.with_context(skip_activity=True)._action_cancel()
# set the new deadline of origin moves (stock to pre prod)
production.move_raw_ids.move_orig_ids.with_context(date_deadline_propagate_ids=set(production.move_raw_ids.ids)).write({'date_deadline': production.date_planned_start})
production.move_raw_ids.move_orig_ids.with_context(date_deadline_propagate_ids=set(production.move_raw_ids.ids)).write({'date_deadline': production.date_start})
for p in self:
p._message_log(body=_('This production has been merge in %s', production.display_name))
+2 -2
View File
@@ -82,14 +82,14 @@ class MrpWorkcenter(models.Model):
if workcenter in workcenter.alternative_workcenter_ids:
raise ValidationError(_("Workcenter %s cannot be an alternative of itself.", workcenter.name))
@api.depends('order_ids.duration_expected', 'order_ids.workcenter_id', 'order_ids.state', 'order_ids.date_planned_start')
@api.depends('order_ids.duration_expected', 'order_ids.workcenter_id', 'order_ids.state', 'order_ids.date_start')
def _compute_workorder_count(self):
MrpWorkorder = self.env['mrp.workorder']
result = {wid: {} for wid in self._ids}
result_duration_expected = {wid: 0 for wid in self._ids}
# Count Late Workorder
data = MrpWorkorder._read_group(
[('workcenter_id', 'in', self.ids), ('state', 'in', ('pending', 'waiting', 'ready')), ('date_planned_start', '<', datetime.now().strftime('%Y-%m-%d'))],
[('workcenter_id', 'in', self.ids), ('state', 'in', ('pending', 'waiting', 'ready')), ('date_start', '<', datetime.now().strftime('%Y-%m-%d'))],
['workcenter_id'], ['workcenter_id'])
count_data = dict((item['workcenter_id'][0], item['workcenter_id_count']) for item in data)
# Count All, Pending, Ready, Progress Workorder
+79 -89
View File
@@ -68,25 +68,18 @@ class MrpWorkorder(models.Model):
'resource.calendar.leaves',
help='Slot into workcenter calendar once planned',
check_company=True, copy=False)
date_planned_start = fields.Datetime(
'Scheduled Start Date',
compute='_compute_dates_planned',
inverse='_set_dates_planned',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
store=True, copy=False)
date_planned_finished = fields.Datetime(
'Scheduled End Date',
compute='_compute_dates_planned',
inverse='_set_dates_planned',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
store=True, copy=False)
date_start = fields.Datetime(
'Start Date', copy=False,
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
'Start',
compute='_compute_dates',
inverse='_set_dates',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
store=True, copy=False)
date_finished = fields.Datetime(
'End Date', copy=False,
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
'End',
compute='_compute_dates',
inverse='_set_dates',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
store=True, copy=False)
duration_expected = fields.Float(
'Expected Duration', digits=(16, 2), compute='_compute_duration_expected',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
@@ -138,7 +131,7 @@ class MrpWorkorder(models.Model):
scrap_ids = fields.One2many('stock.scrap', 'workorder_id')
scrap_count = fields.Integer(compute='_compute_scrap_move_count', string='Scrap Move')
production_date = fields.Datetime('Production Date', related='production_id.date_planned_start', store=True)
production_date = fields.Datetime('Production Date', related='production_id.date_start', store=True)
json_popover = fields.Char('Popover Data JSON', compute='_compute_json_popover')
show_json_popover = fields.Boolean('Show Popover?', compute='_compute_json_popover')
consumption = fields.Selection(related='production_id.consumption')
@@ -177,33 +170,33 @@ class MrpWorkorder(models.Model):
elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
workorder.state = 'waiting'
@api.depends('production_state', 'date_planned_start', 'date_planned_finished')
@api.depends('production_state', 'date_start', 'date_finished')
def _compute_json_popover(self):
if self.ids:
conflicted_dict = self._get_conflicted_workorder_ids()
for wo in self:
infos = []
if not wo.date_planned_start or not wo.date_planned_finished or not wo.ids:
if not wo.date_start or not wo.date_finished or not wo.ids:
wo.show_json_popover = False
wo.json_popover = False
continue
if wo.state in ('pending', 'waiting', 'ready'):
previous_wos = wo.blocked_by_workorder_ids
prev_start = min([workorder.date_planned_start for workorder in previous_wos]) if previous_wos else False
prev_finished = max([workorder.date_planned_finished for workorder in previous_wos]) if previous_wos else False
if wo.state == 'pending' and prev_start and not (prev_start > wo.date_planned_start):
prev_start = min([workorder.date_start for workorder in previous_wos]) if previous_wos else False
prev_finished = max([workorder.date_finished for workorder in previous_wos]) if previous_wos else False
if wo.state == 'pending' and prev_start and not (prev_start > wo.date_start):
infos.append({
'color': 'text-primary',
'msg': _("Waiting the previous work order, planned from %(start)s to %(end)s",
start=format_datetime(self.env, prev_start, dt_format=False),
end=format_datetime(self.env, prev_finished, dt_format=False))
})
if wo.date_planned_finished < fields.Datetime.now():
if wo.date_finished < fields.Datetime.now():
infos.append({
'color': 'text-warning',
'msg': _("The work order should have already been processed.")
})
if prev_start and prev_start > wo.date_planned_start:
if prev_start and prev_start > wo.date_start:
infos.append({
'color': 'text-danger',
'msg': _("Scheduled before the previous work order, planned from %(start)s to %(end)s",
@@ -245,26 +238,26 @@ class MrpWorkorder(models.Model):
workorder.production_id.qty_producing = workorder.qty_producing
workorder.production_id._set_qty_producing()
# Both `date_planned_start` and `date_planned_finished` are related fields on `leave_id`. Let's say
# Both `date_start` and `date_finished` are related fields on `leave_id`. Let's say
# we slide a workorder on a gantt view, a single call to write is made with both
# fields Changes. As the ORM doesn't batch the write on related fields and instead
# makes multiple call, the constraint check_dates() is raised.
# That's why the compute and set methods are needed. to ensure the dates are updated
# in the same time.
@api.depends('leave_id')
def _compute_dates_planned(self):
def _compute_dates(self):
for workorder in self:
workorder.date_planned_start = workorder.leave_id.date_from
workorder.date_planned_finished = workorder.leave_id.date_to
workorder.date_start = workorder.leave_id.date_from
workorder.date_finished = workorder.leave_id.date_to
def _set_dates_planned(self):
if not self[0].date_planned_start or not self[0].date_planned_finished:
def _set_dates(self):
if not self[0].date_start or not self[0].date_finished:
if not self.leave_id:
return
raise UserError(_("It is not possible to unplan one single Work Order. "
"You should unplan the Manufacturing Order instead in order to unplan all the linked operations."))
date_from = self[0].date_planned_start
date_to = self[0].date_planned_finished
date_from = self[0].date_start
date_to = self[0].date_finished
to_write = self.env['mrp.workorder']
for wo in self.sudo():
if wo.leave_id:
@@ -415,25 +408,25 @@ class MrpWorkorder(models.Model):
self.name = self.operation_id.name
self.workcenter_id = self.operation_id.workcenter_id.id
@api.onchange('date_planned_start', 'duration_expected', 'workcenter_id')
def _onchange_date_planned_start(self):
if self.date_planned_start and self.workcenter_id:
self.date_planned_finished = self._calculate_date_planned_finished()
@api.onchange('date_start', 'duration_expected', 'workcenter_id')
def _onchange_date_start(self):
if self.date_start and self.workcenter_id:
self.date_finished = self._calculate_date_finished()
def _calculate_date_planned_finished(self, date_planned_start=False):
def _calculate_date_finished(self, date_finished=False):
return self.workcenter_id.resource_calendar_id.plan_hours(
self.duration_expected / 60.0, date_planned_start or self.date_planned_start,
self.duration_expected / 60.0, date_finished or self.date_start,
compute_leaves=True, domain=[('time_type', 'in', ['leave', 'other'])]
)
@api.onchange('date_planned_finished')
def _onchange_date_planned_finished(self):
if self.date_planned_start and self.date_planned_finished:
@api.onchange('date_finished')
def _onchange_date_finished(self):
if self.date_start and self.date_finished:
self.duration_expected = self._calculate_duration_expected()
def _calculate_duration_expected(self, date_planned_start=False, date_planned_finished=False):
def _calculate_duration_expected(self, date_start=False, date_finished=False):
interval = self.workcenter_id.resource_calendar_id.get_work_duration_data(
date_planned_start or self.date_planned_start, date_planned_finished or self.date_planned_finished,
date_start or self.date_start, date_finished or self.date_finished,
domain=[('time_type', 'in', ['leave', 'other'])]
)
return interval['hours'] * 60
@@ -453,30 +446,30 @@ class MrpWorkorder(models.Model):
if workorder.state in ('progress', 'done', 'cancel'):
raise UserError(_('You cannot change the workcenter of a work order that is in progress or done.'))
workorder.leave_id.resource_id = self.env['mrp.workcenter'].browse(values['workcenter_id']).resource_id
if 'date_planned_start' in values or 'date_planned_finished' in values:
if 'date_start' in values or 'date_finished' in values:
for workorder in self:
start_date = fields.Datetime.to_datetime(values.get('date_planned_start', workorder.date_planned_start))
end_date = fields.Datetime.to_datetime(values.get('date_planned_finished', workorder.date_planned_finished))
if start_date and end_date and start_date > end_date:
date_start = fields.Datetime.to_datetime(values.get('date_start', workorder.date_start))
date_finished = fields.Datetime.to_datetime(values.get('date_finished', workorder.date_finished))
if date_start and date_finished and date_start > date_finished:
raise UserError(_('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.'))
if 'duration_expected' not in values and not self.env.context.get('bypass_duration_calculation'):
if values.get('date_planned_start') and values.get('date_planned_finished'):
computed_finished_time = workorder._calculate_date_planned_finished(start_date)
values['date_planned_finished'] = computed_finished_time
elif start_date and end_date:
computed_duration = workorder._calculate_duration_expected(date_planned_start=start_date, date_planned_finished=end_date)
if values.get('date_start') and values.get('date_finished'):
computed_finished_time = workorder._calculate_date_finished(date_start)
values['date_finished'] = computed_finished_time
elif date_start and date_finished:
computed_duration = workorder._calculate_duration_expected(date_start=date_start, date_finished=date_finished)
values['duration_expected'] = computed_duration
# Update MO dates if the start date of the first WO or the
# finished date of the last WO is update.
if workorder == workorder.production_id.workorder_ids[0] and 'date_planned_start' in values:
if values['date_planned_start']:
if workorder == workorder.production_id.workorder_ids[0] and 'date_start' in values:
if values['date_start']:
workorder.production_id.with_context(force_date=True).write({
'date_planned_start': fields.Datetime.to_datetime(values['date_planned_start'])
'date_start': fields.Datetime.to_datetime(values['date_start'])
})
if workorder == workorder.production_id.workorder_ids[-1] and 'date_planned_finished' in values:
if values['date_planned_finished']:
if workorder == workorder.production_id.workorder_ids[-1] and 'date_finished' in values:
if values['date_finished']:
workorder.production_id.with_context(force_date=True).write({
'date_planned_finished': fields.Datetime.to_datetime(values['date_planned_finished'])
'date_finished': fields.Datetime.to_datetime(values['date_finished'])
})
return super(MrpWorkorder, self).write(values)
@@ -503,12 +496,12 @@ class MrpWorkorder(models.Model):
def _plan_workorder(self, replan=False):
self.ensure_one()
# Plan workorder after its predecessors
start_date = max(self.production_id.date_planned_start, datetime.now())
date_start = max(self.production_id.date_start, datetime.now())
for workorder in self.blocked_by_workorder_ids:
if workorder.state in ['done', 'cancel']:
continue
workorder._plan_workorder(replan)
start_date = max(start_date, workorder.date_planned_finished)
date_start = max(date_start, workorder.date_finished)
# Plan only suitable workorders
if self.state not in ['pending', 'waiting', 'ready']:
return
@@ -519,7 +512,7 @@ class MrpWorkorder(models.Model):
return
# Consider workcenter and alternatives
workcenters = self.workcenter_id | self.workcenter_id.alternative_workcenter_ids
best_finished_date = datetime.max
best_date_finished = datetime.max
vals = {}
for workcenter in workcenters:
if not workcenter.resource_calendar_id:
@@ -529,28 +522,28 @@ class MrpWorkorder(models.Model):
duration_expected = self.duration_expected
else:
duration_expected = self._get_duration_expected(alternative_workcenter=workcenter)
from_date, to_date = workcenter._get_first_available_slot(start_date, duration_expected)
from_date, to_date = workcenter._get_first_available_slot(date_start, duration_expected)
# If the workcenter is unavailable, try planning on the next one
if not from_date:
continue
# Check if this workcenter is better than the previous ones
if to_date and to_date < best_finished_date:
best_start_date = from_date
best_finished_date = to_date
if to_date and to_date < best_date_finished:
best_date_start = from_date
best_date_finished = to_date
best_workcenter = workcenter
vals = {
'workcenter_id': workcenter.id,
'duration_expected': duration_expected,
}
# If none of the workcenter are available, raise
if best_finished_date == datetime.max:
if best_date_finished == datetime.max:
raise UserError(_('Impossible to plan the workorder. Please check the workcenter availabilities.'))
# Create leave on chosen workcenter calendar
leave = self.env['resource.calendar.leaves'].create({
'name': self.display_name,
'calendar_id': best_workcenter.resource_calendar_id.id,
'date_from': best_start_date,
'date_to': best_finished_date,
'date_from': best_date_start,
'date_to': best_date_finished,
'resource_id': best_workcenter.resource_id.id,
'time_type': 'other'
})
@@ -626,32 +619,33 @@ class MrpWorkorder(models.Model):
})
if self.state == 'progress':
return True
start_date = fields.Datetime.now()
date_start = fields.Datetime.now()
vals = {
'state': 'progress',
'date_start': start_date,
'date_start': date_start,
}
if not self.leave_id:
leave = self.env['resource.calendar.leaves'].create({
'name': self.display_name,
'calendar_id': self.workcenter_id.resource_calendar_id.id,
'date_from': start_date,
'date_to': start_date + relativedelta(minutes=self.duration_expected),
'date_from': date_start,
'date_to': date_start + relativedelta(minutes=self.duration_expected),
'resource_id': self.workcenter_id.resource_id.id,
'time_type': 'other'
})
vals['date_finished'] = leave.date_to
vals['leave_id'] = leave.id
return self.write(vals)
else:
if not self.date_planned_start or self.date_planned_start > start_date:
vals['date_planned_start'] = start_date
vals['date_planned_finished'] = self._calculate_date_planned_finished(start_date)
if self.date_planned_finished and self.date_planned_finished < start_date:
vals['date_planned_finished'] = start_date
if not self.date_start or self.date_start > date_start:
vals['date_start'] = date_start
vals['date_finished'] = self._calculate_date_finished(date_start)
if self.date_finished and self.date_finished < date_start:
vals['date_finished'] = date_start
return self.with_context(bypass_duration_calculation=True).write(vals)
def button_finish(self):
end_date = datetime.now()
date_finished = datetime.now()
for workorder in self:
if workorder.state in ('done', 'cancel'):
continue
@@ -659,14 +653,11 @@ class MrpWorkorder(models.Model):
vals = {
'qty_produced': workorder.qty_produced or workorder.qty_producing or workorder.qty_production,
'state': 'done',
'date_finished': end_date,
'date_planned_finished': end_date,
'date_finished': date_finished,
'costs_hour': workorder.workcenter_id.costs_hour
}
if not workorder.date_start:
vals['date_start'] = end_date
if not workorder.date_planned_start or end_date < workorder.date_planned_start:
vals['date_planned_start'] = end_date
if not workorder.date_start or date_finished < workorder.date_start:
vals['date_start'] = date_finished
workorder.with_context(bypass_duration_calculation=True).write(vals)
return True
@@ -717,7 +708,6 @@ class MrpWorkorder(models.Model):
return self.write({
'state': 'done',
'date_finished': end_date,
'date_planned_finished': end_date,
'costs_hour': self.workcenter_id.costs_hour
})
@@ -786,7 +776,7 @@ class MrpWorkorder(models.Model):
:return: defaultdict with key as workorder id of self and value as related conflicted workorder
"""
self.flush_model(['state', 'date_planned_start', 'date_planned_finished', 'workcenter_id'])
self.flush_model(['state', 'date_start', 'date_finished', 'workcenter_id'])
sql = """
SELECT wo1.id, wo2.id
FROM mrp_workorder wo1, mrp_workorder wo2
@@ -796,8 +786,8 @@ class MrpWorkorder(models.Model):
AND wo2.state IN ('pending', 'waiting', 'ready')
AND wo1.id != wo2.id
AND wo1.workcenter_id = wo2.workcenter_id
AND (DATE_TRUNC('second', wo2.date_planned_start), DATE_TRUNC('second', wo2.date_planned_finished))
OVERLAPS (DATE_TRUNC('second', wo1.date_planned_start), DATE_TRUNC('second', wo1.date_planned_finished))
AND (DATE_TRUNC('second', wo2.date_start), DATE_TRUNC('second', wo2.date_finished))
OVERLAPS (DATE_TRUNC('second', wo1.date_start), DATE_TRUNC('second', wo1.date_finished))
"""
self.env.cr.execute(sql, [tuple(self.ids)])
res = defaultdict(list)
+1 -1
View File
@@ -168,7 +168,7 @@ class ProductProduct(models.Model):
def _compute_mrp_product_qty(self):
date_from = fields.Datetime.to_string(fields.datetime.now() - timedelta(days=365))
#TODO: state = done?
domain = [('state', '=', 'done'), ('product_id', 'in', self.ids), ('date_planned_start', '>', date_from)]
domain = [('state', '=', 'done'), ('product_id', 'in', self.ids), ('date_start', '>', date_from)]
read_group_res = self.env['mrp.production']._read_group(domain, ['product_id', 'product_uom_qty'], ['product_id'])
mapped_data = dict([(data['product_id'][0], data['product_uom_qty']) for data in read_group_res])
for product in self:
+1 -1
View File
@@ -285,7 +285,7 @@ class StockMove(models.Model):
continue
# produced products + byproducts
values['location_id'] = mo.production_location_id.id
values['date'] = mo._get_date_planned_finished()
values['date'] = mo.date_finished
values['date_deadline'] = mo.date_deadline
if not values.get('location_dest_id'):
values['location_dest_id'] = mo.location_dest_id.id
+1 -1
View File
@@ -36,7 +36,7 @@ class StockPickingType(models.Model):
domains = {
'count_mo_waiting': [('reservation_state', '=', 'waiting')],
'count_mo_todo': ['|', ('state', 'in', ('confirmed', 'draft', 'progress', 'to_close')), ('is_planned', '=', True)],
'count_mo_late': [('date_planned_start', '<', fields.Date.today()), ('state', '=', 'confirmed')],
'count_mo_late': [('date_start', '<', fields.Date.today()), ('state', '=', 'confirmed')],
}
for field in domains:
data = self.env['mrp.production']._read_group(domains[field] +
+2 -2
View File
@@ -156,8 +156,8 @@ class StockRule(models.Model):
'location_dest_id': location_dest_id.id,
'bom_id': bom.id,
'date_deadline': date_deadline,
'date_planned_start': date_planned,
'date_planned_finished': fields.Datetime.from_string(values['date_planned']),
'date_start': date_planned,
'date_finished': fields.Datetime.from_string(values['date_planned']),
'procurement_group_id': False,
'propagate_cancel': self.propagate_cancel,
'orderpoint_id': values.get('orderpoint_id', False) and values.get('orderpoint_id').id,
+3 -3
View File
@@ -332,7 +332,7 @@ class MrpProduction(models.Model):
picking_type = self.env['stock.picking.type'].browse(values['picking_type_id'])
return picking_type.default_location_dest_id.id
def get_date_planned_start(values, counter, random):
def get_date_start(values, counter, random):
# 95.45 % of picking scheduled between (-10, 30) days and follow a gauss distribution (only +-15% picking is late)
delta = random.gauss(10, 10)
return now + timedelta(days=delta)
@@ -345,7 +345,7 @@ class MrpProduction(models.Model):
('product_uom_id', populate.compute(get_product_uom_id)),
('product_qty', populate.randint(1, 10)),
('picking_type_id', populate.compute(get_picking_type_id)),
('date_planned_start', populate.compute(get_date_planned_start)),
('date_start', populate.compute(get_date_start)),
('location_src_id', populate.compute(get_location_src_id)),
('location_dest_id', populate.compute(get_location_dest_id)),
('priority', populate.iterate(['0', '1'], [0.95, 0.05])),
@@ -399,7 +399,7 @@ class StockMove(models.Model):
values['location_dest_id'] = production.production_location_id.id
values['picking_type_id'] = production.picking_type_id.id
values['name'] = production.name
values['date'] = production.date_planned_start
values['date'] = production.date_start
values['company_id'] = production.company_id.id
yield values
+7 -7
View File
@@ -92,7 +92,7 @@ class ReportMoOverview(models.AbstractModel):
'quantity_free': product.uom_id._compute_quantity(product.free_qty, production.product_uom_id) if product.type == 'product' else False,
'quantity_on_hand': product.uom_id._compute_quantity(product.qty_available, production.product_uom_id) if product.type == 'product' else False,
'quantity_reserved': 0.0,
'receipt': self._check_planned_start(production.date_planned_start, self._get_replenishment_receipt(production, components)),
'receipt': self._check_planned_start(production.date_start, self._get_replenishment_receipt(production, components)),
'mo_cost': company.currency_id.round(mo_cost + operations.get('summary', {}).get('mo_cost', 0.0)),
'product_cost': company.currency_id.round(product.standard_price * production.product_uom_qty),
'currency_id': company.currency_id.id,
@@ -202,7 +202,7 @@ class ReportMoOverview(models.AbstractModel):
'quantity_free': product.uom_id._compute_quantity(product.free_qty, move_raw.product_uom) if product.type == 'product' else False,
'quantity_on_hand': product.uom_id._compute_quantity(product.qty_available, move_raw.product_uom) if product.type == 'product' else False,
'quantity_reserved': self._get_reserved_qty(move_raw, production.warehouse_id, replenish_data),
'receipt': self._check_planned_start(production.date_planned_start, self._get_component_receipt(product, move_raw, production.warehouse_id, replenishments, replenish_data)),
'receipt': self._check_planned_start(production.date_start, self._get_component_receipt(product, move_raw, production.warehouse_id, replenishments, replenish_data)),
'mo_cost': company.currency_id.round(mo_cost if replenishments else product_cost),
'product_cost': company.currency_id.round(product_cost),
'currency_id': company.currency_id.id,
@@ -281,7 +281,7 @@ class ReportMoOverview(models.AbstractModel):
replenishment['components'] = self._get_components_data(doc_in, replenish_data, level + 2, replenishment_index)
replenishment['operations'] = self._get_operations_data(doc_in, level + 2, replenishment_index)
replenishment['summary']['mo_cost'] = currency.round(sum(component.get('summary', {}).get('mo_cost', 0.0) for component in replenishment['components']) + replenishment['operations'].get('summary', {}).get('mo_cost', 0.0))
replenishment['summary']['receipt'] = self._check_planned_start(production.date_planned_start, self._get_replenishment_receipt(doc_in, replenishment.get('components', [])))
replenishment['summary']['receipt'] = self._check_planned_start(production.date_start, self._get_replenishment_receipt(doc_in, replenishment.get('components', [])))
replenishments.append(replenishment)
total_ordered += replenishment['summary']['quantity']
@@ -295,7 +295,7 @@ class ReportMoOverview(models.AbstractModel):
rules_delay = sum(rule.delay for rule in resupply_rules)
resupply_data = self._get_resupply_data(resupply_rules, rules_delay, missing_quantity, move_raw.product_uom, product, production.warehouse_id)
if resupply_data:
receipt = self._check_planned_start(production.date_planned_start, self._format_receipt_date('estimated', fields.datetime.today() + timedelta(days=resupply_data['delay'])))
receipt = self._check_planned_start(production.date_start, self._format_receipt_date('estimated', fields.datetime.today() + timedelta(days=resupply_data['delay'])))
else:
receipt = self._format_receipt_date('unavailable')
@@ -317,7 +317,7 @@ class ReportMoOverview(models.AbstractModel):
}}
if resupply_data:
to_order_line['mo_cost'] = currency.round(resupply_data['cost'] * move_raw.product_uom._compute_quantity(missing_quantity, product.uom_id))
to_order_line['receipt'] = self._check_planned_start(production.date_planned_start, self._format_receipt_date('estimated', fields.datetime.today() + timedelta(days=resupply_data['delay']))),
to_order_line['receipt'] = self._check_planned_start(production.date_start, self._format_receipt_date('estimated', fields.datetime.today() + timedelta(days=resupply_data['delay']))),
else:
to_order_line['mo_cost'] = currency.round(product.standard_price * move_raw.product_uom._compute_quantity(missing_quantity, product.uom_id))
to_order_line['receipt'] = self._format_receipt_date('unavailable')
@@ -330,7 +330,7 @@ class ReportMoOverview(models.AbstractModel):
def _get_replenishment_receipt(self, doc_in, components):
if doc_in._name == 'mrp.production':
max_date_start = doc_in.date_planned_start
max_date_start = doc_in.date_start
all_available = True
some_unavailable = False
some_estimated = False
@@ -344,7 +344,7 @@ class ReportMoOverview(models.AbstractModel):
if some_unavailable:
return self._format_receipt_date('unavailable')
if all_available:
return self._format_receipt_date('expected', doc_in.date_planned_finished)
return self._format_receipt_date('expected', doc_in.date_finished)
new_date = max_date_start + timedelta(days=doc_in.product_id.produce_delay)
receipt_state = 'estimated' if some_estimated else 'expected'
+1 -1
View File
@@ -33,7 +33,7 @@ class ReceptionReport(models.AbstractModel):
def _get_formatted_scheduled_date(self, source):
if source._name == 'mrp.production':
return format_date(self.env, source.date_planned_start)
return format_date(self.env, source.date_start)
return super()._get_formatted_scheduled_date(source)
def _action_assign(self, in_move, out_move):
+3 -3
View File
@@ -391,13 +391,13 @@ class TestMrpProductionBackorder(TestMrpCommon):
The MO and the backorder should be assigned according to the reservation method
defined in the default manufacturing operation type
"""
def create_mo(date_planned_start=False):
def create_mo(date_start=False):
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.bom_1.product_id
mo_form.bom_id = self.bom_1
mo_form.product_qty = 2
if date_planned_start:
mo_form.date_planned_start = date_planned_start
if date_start:
mo_form.date_start = date_start
mo = mo_form.save()
mo.action_confirm()
return mo
+35 -35
View File
@@ -57,14 +57,14 @@ class TestMrpOrder(TestMrpCommon):
'inventory_quantity': 500
}).action_apply_inventory()
test_date_planned = fields.Datetime.now() - timedelta(days=1)
date_start = fields.Datetime.now() - timedelta(days=1)
test_quantity = 3.0
man_order_form = Form(self.env['mrp.production'].with_user(self.user_mrp_user))
man_order_form.product_id = self.product_4
man_order_form.bom_id = self.bom_1
man_order_form.product_uom_id = self.product_4.uom_id
man_order_form.product_qty = test_quantity
man_order_form.date_planned_start = test_date_planned
man_order_form.date_start = date_start
man_order_form.location_src_id = self.location_1
man_order_form.location_dest_id = self.warehouse_1.wh_output_stock_loc_id
man_order = man_order_form.save()
@@ -75,7 +75,7 @@ class TestMrpOrder(TestMrpCommon):
# check production move
production_move = man_order.move_finished_ids
self.assertAlmostEqual(production_move.date, test_date_planned + timedelta(hours=1), delta=timedelta(seconds=10))
self.assertAlmostEqual(production_move.date, date_start + timedelta(hours=1), delta=timedelta(seconds=10))
self.assertEqual(production_move.product_id, self.product_4)
self.assertEqual(production_move.product_uom, man_order.product_uom_id)
self.assertEqual(production_move.product_qty, man_order.product_qty)
@@ -84,7 +84,7 @@ class TestMrpOrder(TestMrpCommon):
# check consumption moves
for move in man_order.move_raw_ids:
self.assertEqual(move.date, test_date_planned)
self.assertEqual(move.date, date_start)
first_move = man_order.move_raw_ids.filtered(lambda move: move.product_id == self.product_2)
self.assertEqual(first_move.product_qty, test_quantity / self.bom_1.product_qty * self.product_4.uom_id.factor_inv * 2)
first_move = man_order.move_raw_ids.filtered(lambda move: move.product_id == self.product_1)
@@ -485,17 +485,17 @@ class TestMrpOrder(TestMrpCommon):
def test_update_plan_date(self):
"""Editing the scheduled date after planning the MO should unplan the MO, and adjust the date on the stock moves"""
planned_date = datetime(2023, 5, 15, 9, 0)
date_start = datetime(2023, 5, 15, 9, 0)
mo_form = Form(self.env['mrp.production'])
mo_form.product_id = self.product_4
mo_form.bom_id = self.bom_1
mo_form.product_qty = 1
mo_form.date_planned_start = planned_date
mo_form.date_start = date_start
mo = mo_form.save()
self.assertEqual(mo.move_finished_ids[0].date, datetime(2023, 5, 15, 10, 0))
mo.action_confirm()
mo.button_plan()
mo.date_planned_start = datetime(2024, 5, 15, 9, 0)
mo.date_start = datetime(2024, 5, 15, 9, 0)
self.assertEqual(mo.move_finished_ids[0].date, datetime(2024, 5, 15, 10, 0))
def test_rounding(self):
@@ -1346,7 +1346,7 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(move.propagate_cancel, mo.propagate_cancel)
self.assertFalse(move.raw_material_production_id)
self.assertEqual(move.location_id, mo.production_location_id)
self.assertEqual(move.date, mo._get_date_planned_finished())
self.assertEqual(move.date, mo.date_finished)
self.assertEqual(move.date_deadline, mo.date_deadline)
mo.move_raw_ids |= move
@@ -1580,7 +1580,7 @@ class TestMrpOrder(TestMrpCommon):
now = fields.Datetime.now()
mo_form = Form(self.env['mrp.production'])
mo_form.bom_id = self.bom_3 # product_5 (2), product_4 (8), product_2 (12)
mo_form.date_planned_start = now
mo_form.date_start = now
mo = mo_form.save()
self.assertEqual(mo.components_availability, False) # no compute for draft
mo.action_confirm()
@@ -1607,7 +1607,7 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(mo.components_availability, f'Exp {format_date(self.env, tommorrow)}')
self.assertEqual(mo.components_availability_state, 'late')
mo.date_planned_start = after_tommorrow
mo.date_start = after_tommorrow
self.assertEqual(mo.components_availability, f'Exp {format_date(self.env, tommorrow)}')
self.assertEqual(mo.components_availability_state, 'expected')
@@ -1959,10 +1959,10 @@ class TestMrpOrder(TestMrpCommon):
})
# Next Monday at 6:00 am UTC
date_planned = (fields.Datetime.now() + timedelta(days=7 - fields.Datetime.now().weekday())).replace(hour=6, minute=0, second=0)
date_start = (fields.Datetime.now() + timedelta(days=7 - fields.Datetime.now().weekday())).replace(hour=6, minute=0, second=0)
mo_form = Form(self.env['mrp.production'])
mo_form.bom_id = bom
mo_form.date_planned_start = date_planned
mo_form.date_start = date_start
mo = mo_form.save()
mo.workorder_ids[0].duration_expected = 240
@@ -1974,11 +1974,11 @@ class TestMrpOrder(TestMrpCommon):
# Asia/Bangkok is UTC+7 and the start date is on Monday at 06:00 UTC (i.e., 13:00 UTC+7).
# So, in Bangkok, the first workorder uses the entire Monday afternoon slot 13:00 - 17:00 UTC+7 (i.e., 06:00 - 10:00 UTC)
# The second job uses the beginning of the Tuesday morning slot: 08:00 - 09:00 UTC+7 (i.e., 01:00 - 02:00 UTC)
self.assertEqual(mo.workorder_ids[0].date_planned_start, date_planned)
self.assertEqual(mo.workorder_ids[0].date_planned_finished, date_planned + timedelta(hours=4))
tuesday = date_planned + timedelta(days=1)
self.assertEqual(mo.workorder_ids[1].date_planned_start, tuesday.replace(hour=1))
self.assertEqual(mo.workorder_ids[1].date_planned_finished, tuesday.replace(hour=2))
self.assertEqual(mo.workorder_ids[0].date_start, date_start)
self.assertEqual(mo.workorder_ids[0].date_finished, date_start + timedelta(hours=4))
tuesday = date_start + timedelta(days=1)
self.assertEqual(mo.workorder_ids[1].date_start, tuesday.replace(hour=1))
self.assertEqual(mo.workorder_ids[1].date_finished, tuesday.replace(hour=2))
def test_backorder_with_overconsumption(self):
""" Check that the components of the backorder have the correct quantities
@@ -2838,7 +2838,7 @@ class TestMrpOrder(TestMrpCommon):
mo_form = Form(self.env['mrp.production'])
mo_form.bom_id = self.bom_4
with mo_form.workorder_ids.edit(0) as wo_line:
wo_line.date_planned_start = datetime.now()
wo_line.date_start = datetime.now()
mos += mo_form.save()
mos.action_confirm()
@@ -2853,7 +2853,7 @@ class TestMrpOrder(TestMrpCommon):
self.assertFalse(wo_01.show_json_popover)
self.assertFalse(wo_02.show_json_popover)
self.assertEqual(wo_01.date_planned_finished, wo_02.date_planned_start)
self.assertEqual(wo_01.date_finished, wo_02.date_start)
@freeze_time('2022-06-28 08:00')
def test_replan_workorders02(self):
@@ -2876,7 +2876,7 @@ class TestMrpOrder(TestMrpCommon):
for mo in mos:
with Form(mo) as mo_form:
with mo_form.workorder_ids.edit(0) as wo_line:
wo_line.date_planned_start = datetime.now()
wo_line.date_start = datetime.now()
wo_01 = mo_01.workorder_ids
wo_02 = mo_02.workorder_ids
@@ -2887,7 +2887,7 @@ class TestMrpOrder(TestMrpCommon):
self.assertFalse(wo_01.show_json_popover)
self.assertFalse(wo_02.show_json_popover)
self.assertEqual(wo_01.date_planned_finished, wo_02.date_planned_start)
self.assertEqual(wo_01.date_finished, wo_02.date_start)
@freeze_time('2022-10-05 12:00')
def test_replan_mo_without_bom(self):
@@ -2930,27 +2930,27 @@ class TestMrpOrder(TestMrpCommon):
with mo_01_form.workorder_ids.new() as workorder:
workorder.name = "OP2"
workorder.workcenter_id = self.workcenter_3
workorder.date_planned_start = datetime(2022, 10, 23, 12)
workorder.date_start = datetime(2022, 10, 23, 12)
mo_01 = mo_01_form.save()
mo_01.action_confirm()
op_1, op_2 = mo_01.workorder_ids
self.assertEqual(op_2.date_planned_start, datetime(2022, 10, 23, 12))
self.assertEqual(op_2.date_start, datetime(2022, 10, 23, 12))
with Form(mo_01) as mo_01_form:
with mo_01_form.workorder_ids.edit(0) as workorder:
workorder.date_planned_start = datetime(2022, 10, 18, 12)
workorder.date_start = datetime(2022, 10, 18, 12)
mo_01 = mo_01_form.save()
self.assertEqual(op_1.date_planned_start, datetime(2022, 10, 18, 12))
self.assertEqual(op_1.date_planned_finished, op_2.date_planned_start)
self.assertEqual(op_1.date_start, datetime(2022, 10, 18, 12))
self.assertEqual(op_1.date_finished, op_2.date_start)
#Second MO
with Form(mo_02) as mo_02_form:
with mo_02_form.workorder_ids.new() as workorder:
workorder.name = "OP1"
workorder.workcenter_id = self.workcenter_2
workorder.date_planned_start = datetime(2022, 10, 20, 12)
workorder.date_start = datetime(2022, 10, 20, 12)
mo_02 = mo_02_form.save()
mo_02.action_confirm()
@@ -2958,11 +2958,11 @@ class TestMrpOrder(TestMrpCommon):
with mo_02_form.workorder_ids.new() as workorder:
workorder.name = "OP2"
workorder.workcenter_id = self.workcenter_3
workorder.date_planned_start = datetime(2022, 10, 18, 12)
workorder.date_start = datetime(2022, 10, 18, 12)
mo_02 = mo_02_form.save()
op_1, op_2 = mo_02.workorder_ids
self.assertEqual(op_1.date_planned_start, datetime(2022, 10, 20, 12))
self.assertEqual(op_1.date_start, datetime(2022, 10, 20, 12))
self.assertTrue(op_2.show_json_popover)
@freeze_time('2023-03-01 12:00')
@@ -3014,9 +3014,9 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
mo.action_confirm()
mo.button_plan()
self.assertEqual(mo.workorder_ids[0].date_planned_start, datetime(2023, 3, 1, 12, 0))
self.assertEqual(mo.workorder_ids[1].date_planned_start, datetime(2023, 3, 1, 13, 15))
self.assertEqual(mo.workorder_ids[2].date_planned_start, datetime(2023, 3, 1, 14, 30))
self.assertEqual(mo.workorder_ids[0].date_start, datetime(2023, 3, 1, 12, 0))
self.assertEqual(mo.workorder_ids[1].date_start, datetime(2023, 3, 1, 13, 15))
self.assertEqual(mo.workorder_ids[2].date_start, datetime(2023, 3, 1, 14, 30))
# wo_1 completely finished
mo_form = Form(mo)
@@ -3041,9 +3041,9 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(mo_backorder.workorder_ids[0].state, 'cancel')
self.assertEqual(mo_backorder.workorder_ids[1].state, 'waiting')
self.assertEqual(mo_backorder.workorder_ids[2].state, 'pending')
self.assertFalse(mo_backorder.workorder_ids[0].date_planned_start)
self.assertEqual(mo_backorder.workorder_ids[1].date_planned_start, datetime(2023, 3, 1, 12, 0))
self.assertEqual(mo_backorder.workorder_ids[2].date_planned_start, datetime(2023, 3, 1, 12, 45))
self.assertFalse(mo_backorder.workorder_ids[0].date_start)
self.assertEqual(mo_backorder.workorder_ids[1].date_start, datetime(2023, 3, 1, 12, 0))
self.assertEqual(mo_backorder.workorder_ids[2].date_start, datetime(2023, 3, 1, 12, 45))
def test_compute_product_id(self):
"""
+4 -4
View File
@@ -249,11 +249,11 @@ class TestProcurement(TestMrpCommon):
self.assertEqual(len(move_orig), 1, 'the move orig is not created')
self.assertEqual(move_orig.product_qty, 10, 'the quantity to produce is not good relative to the move')
new_sheduled_date = fields.Datetime.to_datetime(mo.date_planned_start) + timedelta(days=5)
mo.date_planned_start = new_sheduled_date
new_date_start = fields.Datetime.to_datetime(mo.date_start) + timedelta(days=5)
mo.date_start = new_date_start
self.assertAlmostEqual(mo.move_raw_ids.date, mo.date_planned_start, delta=timedelta(seconds=1))
self.assertAlmostEqual(mo.move_finished_ids.date, mo.date_planned_finished, delta=timedelta(seconds=1))
self.assertAlmostEqual(mo.move_raw_ids.date, mo.date_start, delta=timedelta(seconds=1))
self.assertAlmostEqual(mo.move_finished_ids.date, mo.date_finished, delta=timedelta(seconds=1))
def test_finished_move_cancellation(self):
"""Check state of finished move on cancellation of raw moves. """
+21 -20
View File
@@ -23,7 +23,7 @@
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="message_needaction" invisible="1"/>
<field name="name" decoration-bf="1"/>
<field name="date_planned_start" optional="show" widget="remaining_days"/>
<field name="date_start" optional="show" widget="remaining_days"/>
<field name="date_deadline" widget="remaining_days" attrs="{'invisible': [('state', 'in', ['done', 'cancel'])]}" optional="hide"/>
<field name="product_id" readonly="1" optional="show"/>
<field name="lot_producing_id" optional="hide"/>
@@ -41,8 +41,8 @@
<field name="reservation_state" optional="hide" decoration-danger="reservation_state == 'confirmed'" decoration-success="reservation_state == 'assigned'"/>
<field name="product_qty" sum="Total Qty" string="Quantity" readonly="1" optional="show"/>
<field name="product_uom_id" string="UoM" readonly="1" options="{'no_open':True,'no_create':True}" groups="uom.group_uom" optional="show"/>
<field name="production_duration_expected" attrs="{'invisible': [('production_duration_expected', '=', 0)]}" groups="mrp.group_mrp_routings" widget="float_time" sum="Total expected duration" optional="show"/>
<field name="production_real_duration" attrs="{'invisible': [('production_real_duration', '=', 0)]}" groups="mrp.group_mrp_routings" widget="float_time" sum="Total real duration" optional="show"/>
<field name="duration_expected" attrs="{'invisible': [('duration_expected', '=', 0)]}" groups="mrp.group_mrp_routings" widget="float_time" sum="Total expected duration" optional="show"/>
<field name="duration" attrs="{'invisible': [('duration', '=', 0)]}" groups="mrp.group_mrp_routings" widget="float_time" sum="Total real duration" optional="show"/>
<field name="company_id" readonly="1" groups="base.group_multi_company" optional="show"/>
<field name="state"
decoration-success="state in ('done', 'to_close')"
@@ -138,7 +138,7 @@
</header>
<sheet>
<field name="reservation_state" invisible="1"/>
<field name="date_planned_finished" invisible="1"/>
<field name="date_finished" invisible="1"/>
<field name="is_locked" invisible="1"/>
<field name="qty_produced" invisible="1"/>
<field name="unreserve_visible" invisible="1"/>
@@ -239,13 +239,14 @@
</div>
</group>
<group name="group_extra_info">
<label for="date_planned_start"/>
<label for="date_start" string="Scheduled Date" attrs="{'invisible': [('state', 'in', ['progress', 'to_close', 'done', 'cancel'])]}"/>
<label for="date_start" string="Start Date" attrs="{'invisible': [('state', 'not in', ['progress', 'to_close', 'done', 'cancel'])]}"/>
<div class="o_row">
<field name="date_planned_start"
attrs="{'readonly': [('state', 'in', ['close', 'cancel'])]}"
decoration-warning="state not in ('done', 'cancel') and date_planned_start &lt; now"
decoration-danger="state not in ('done', 'cancel') and date_planned_start &lt; current_date"
decoration-bf="state not in ('done', 'cancel') and (date_planned_start &lt; current_date or date_planned_start &lt; now)"/>
<field name="date_start"
attrs="{'readonly': [('state', 'in', ['done', 'to_close', 'cancel'])]}"
decoration-warning="state not in ('done', 'cancel') and date_start &lt; now"
decoration-danger="state not in ('done', 'cancel') and date_start &lt; current_date"
decoration-bf="state not in ('done', 'cancel') and (date_start &lt; current_date or date_start &lt; now)"/>
<field name="delay_alert_date" invisible="1"/>
<field nolabel="1" name="json_popover" widget="stock_rescheduling_popover" attrs="{'invisible': [('json_popover', '=', False)]}"/>
</div>
@@ -308,7 +309,7 @@
<page string="Components" name="components">
<field name="move_raw_ids"
widget="mrp_production_components_x2many"
context="{'default_date': date_planned_start, 'default_date_deadline': date_planned_start, 'default_location_id': location_src_id, 'default_location_dest_id': production_location_id,
context="{'default_date': date_start, 'default_date_deadline': date_start, 'default_location_id': location_src_id, 'default_location_dest_id': production_location_id,
'default_warehouse_id': warehouse_id, 'default_state': 'draft', 'default_raw_material_production_id': id, 'default_picking_type_id': picking_type_id, 'default_company_id': company_id}"
attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&amp;', ('state', '=', 'done'), ('is_locked', '=', True)]}" options="{'delete': [('state', '=', 'draft')]}">
<tree default_order="is_done, manual_consumption desc, sequence" editable="bottom">
@@ -378,7 +379,7 @@
<field name="workorder_ids" attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&amp;', ('state', '=', 'done'), ('is_locked', '=', True)]}" context="{'tree_view_ref': 'mrp.mrp_production_workorder_tree_editable_view', 'default_product_uom_id': product_uom_id, 'from_manufacturing_order': True}"/>
</page>
<page string="By-Products" name="finished_products" groups="mrp.group_mrp_byproducts">
<field name="move_byproduct_ids" context="{'default_date': date_planned_finished, 'default_date_deadline': date_deadline, 'default_location_id': production_location_id, 'default_location_dest_id': location_dest_id, 'default_state': 'draft', 'default_production_id': id, 'default_picking_type_id': picking_type_id, 'default_company_id': company_id}" attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&amp;', ('state', '=', 'done'), ('is_locked', '=', True)]}" options="{'delete': [('state', '=', 'draft')]}">
<field name="move_byproduct_ids" context="{'default_date': date_finished, 'default_date_deadline': date_deadline, 'default_location_id': production_location_id, 'default_location_dest_id': location_dest_id, 'default_state': 'draft', 'default_production_id': id, 'default_picking_type_id': picking_type_id, 'default_company_id': company_id}" attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&amp;', ('state', '=', 'done'), ('is_locked', '=', True)]}" options="{'delete': [('state', '=', 'draft')]}">
<tree default_order="is_done,sequence" decoration-muted="is_done" editable="bottom">
<field name="product_id" context="{'default_detailed_type': 'product'}" domain="[('id', '!=', parent.product_id)]" required="1"/>
<field name="location_dest_id" string="To" readonly="1" force_save="1" groups="stock.group_stock_multi_locations"/>
@@ -457,7 +458,7 @@
<field name="product_id"/>
<field name="product_qty"/>
<field name="product_uom_id" options="{'no_open':True,'no_create':True}"/>
<field name="date_planned_start"/>
<field name="date_start"/>
<field name="state"/>
<field name="activity_state"/>
<progressbar field="activity_state" colors='{"planned": "success", "today": "warning", "overdue": "danger"}'/>
@@ -473,7 +474,7 @@
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left text-muted">
<span><t t-esc="record.name.value"/> <t t-esc="record.date_planned_start.value and record.date_planned_start.value.split(' ')[0] or False"/></span>
<span><t t-esc="record.name.value"/> <t t-esc="record.date_start.value and record.date_start.value.split(' ')[0] or False"/></span>
<field name="activity_ids" widget="kanban_activity"/>
<field name="delay_alert_date" invisible="1"/>
<field nolabel="1" name="json_popover" widget="stock_rescheduling_popover" attrs="{'invisible': [('json_popover', '=', False)]}"/>
@@ -494,7 +495,7 @@
<field name="model">mrp.production</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar date_start="date_planned_start" date_stop="date_planned_finished"
<calendar date_start="date_start" date_stop="date_finished"
string="Manufacturing Orders" event_limit="5" quick_add="False">
<field name="user_id" avatar_field="avatar_128"/>
<field name="product_id"/>
@@ -508,7 +509,7 @@
<field name="model">mrp.production</field>
<field name="arch" type="xml">
<pivot string="Manufacturing Orders" sample="1">
<field name="date_planned_start" type="row"/>
<field name="date_start" type="row"/>
</pivot>
</field>
</record>
@@ -518,7 +519,7 @@
<field name="model">mrp.production</field>
<field name="arch" type="xml">
<graph string="Manufacturing Orders" stacked="0" sample="1">
<field name="date_planned_finished"/>
<field name="date_finished"/>
<field name="product_uom_qty" type="measure"/>
<field name="backorder_sequence" invisible="1"/>
<field name="qty_producing" string="Quantity Produced"/>
@@ -564,8 +565,8 @@
domain="[('my_activity_date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
domain="[('my_activity_date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]"/>
<filter name="filter_date_planned_start" string="Scheduled Date" date="date_planned_start"/>
<filter name="filter_plan_date" invisible="1" string="Scheduled Date: Last 365 Days" domain="[('date_planned_start', '>', (datetime.datetime.now() + relativedelta(days=-365)).to_utc().strftime('%Y-%m-%d %H:%M:%S'))]"/>
<filter name="filter_date_start" string="Date" date="date_start"/>
<filter name="filter_plan_date" invisible="1" string="Date: Last 365 Days" domain="[('date_start', '>', (datetime.datetime.now() + relativedelta(days=-365)).to_utc().strftime('%Y-%m-%d %H:%M:%S'))]"/>
<separator/>
<filter string="Warnings" name="activities_exception"
domain="[('activity_exception_decoration', '!=', False)]"/>
@@ -574,7 +575,7 @@
<filter string="Status" name="status" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Material Availability" name="groupby_reservation_state" domain="[]" context="{'group_by': 'reservation_state'}"/>
<filter string="Procurement Group" name="procurement_group_id" domain="[]" context="{'group_by': 'procurement_group_id'}"/>
<filter string="Scheduled Date" name="scheduled_date" domain="[]" context="{'group_by': 'date_planned_start'}" help="Scheduled Date by Month"/>
<filter string="Date" name="date" domain="[]" context="{'group_by': 'date_start'}" help="Date by Month"/>
</group>
</search>
</field>
+23 -24
View File
@@ -13,7 +13,7 @@
<filter string="Pending" name="pending" domain="[('state','=','pending')]"/>
<filter string="In Progress" name="progress" domain="[('state','=','progress')]"/>
<filter string="Done" name="done" domain="[('state','=', 'done')]"/>
<filter string="Late" name="late" domain="[('date_planned_start','&lt;=',time.strftime('%Y-%m-%d'))]"/>
<filter string="Late" name="late" domain="[('date_start','&lt;=',time.strftime('%Y-%m-%d'))]"/>
<separator/>
<filter string="Start Date" name="date_start_filter" date="date_start"/>
<group expand="0" string="Group By...">
@@ -80,10 +80,8 @@
<field name="product_id" optional="show"/>
<field name="qty_remaining" optional="show" string="Quantity"/>
<field name="finished_lot_id" optional="hide" string="Lot/Serial"/>
<field name="date_planned_start" optional="show"/>
<field name="date_planned_finished" optional="hide"/>
<field name="date_start" optional="hide" readonly="1"/>
<field name="date_finished" optional="hide" readonly="1"/>
<field name="date_start" optional="show" attrs="{'readonly': [('state', 'in', ['progress', 'done', 'cancel'])]}"/>
<field name="date_finished" optional="hide" attrs="{'readonly': [('state', 'in', ['progress', 'done', 'cancel'])]}"/>
<field name="duration_expected" widget="float_time" sum="expected duration"/>
<field name="duration" widget="mrp_timer"
attrs="{'invisible': [('production_state','=', 'draft')], 'readonly': [('is_user_working', '=', True)]}" sum="real duration"/>
@@ -154,13 +152,14 @@
<field name="product_id" invisible="1"/>
<field name="finished_lot_id" invisible="1"/>
<group>
<group attrs="{'invisible': [('date_planned_start', '=', False)]}">
<label for="date_planned_start" string="Planned Date"/>
<group attrs="{'invisible': [('date_start', '=', False)]}">
<label for="date_start" string="Scheduled Date" attrs="{'invisible': [('state', 'in', ['progress', 'done', 'cancel'])]}"/>
<label for="date_start" string="Start Date" attrs="{'invisible': [('state', 'not in', ['progress', 'done', 'cancel'])]}"/>
<div class="oe_inline">
<field name="is_planned" invisible="1"/>
<field name="date_planned_start" class="mr8 oe_inline" attrs="{'required': [('is_planned', '=', True)]}"/>
<field name="date_start" class="mr8 oe_inline" attrs="{'required': [('is_planned', '=', True)]}"/>
<strong class="mr8 oe_inline">to</strong>
<field name="date_planned_finished" class="oe_inline" attrs="{'required': [('is_planned', '=', True)]}"/>
<field name="date_finished" class="oe_inline" attrs="{'required': [('is_planned', '=', True)]}"/>
<field name="show_json_popover" invisible="1"/>
<field name="json_popover" widget="mrp_workorder_popover" class="oe_inline mx-2" attrs="{'invisible': [('show_json_popover', '=', False)]}"/>
</div>
@@ -232,8 +231,8 @@
<field name="name" string="Operation"/>
<field name="company_id" optional="hide" groups="base.group_multi_company"/>
<field name="workcenter_id"/>
<field name="date_planned_start" readonly="1"/>
<field name="date_planned_finished" readonly="1"/>
<field name="date_start" readonly="1"/>
<field name="date_finished" readonly="1"/>
<field name="duration_expected" widget="float_time" sum="expected duration"/>
<field name="production_state" invisible="1"/>
<field name="state" widget="badge" decoration-warning="state == 'progress'" decoration-success="state == 'done'" decoration-info="state not in ('progress', 'done', 'cancel')"
@@ -264,13 +263,13 @@
<filter string="Draft" name="draft" domain="[('state', '=', 'pending'), ('production_state', '=', 'draft')]"/>
<filter string="Finished" name="finish" domain="[('state', '=', 'done')]"/>
<separator/>
<filter string="Late" name="late" domain="['&amp;', ('date_planned_start', '&lt;', current_date), ('state', '=', 'ready')]"
<filter string="Late" name="late" domain="['&amp;', ('date_start', '&lt;', current_date), ('state', '=', 'ready')]"
help="Production started late"/>
<group expand="0" string="Group By">
<filter string="Work Center" name="work_center" domain="[]" context="{'group_by': 'workcenter_id'}"/>
<filter string="Manufacturing Order" name="production" domain="[]" context="{'group_by': 'production_id'}"/>
<filter string="Status" name="status" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Scheduled Date" name="scheduled_month" domain="[]" context="{'group_by': 'date_planned_start'}"/>
<filter string="Date" name="scheduled_month" domain="[]" context="{'group_by': 'date_start'}"/>
</group>
</search>
</field>
@@ -280,7 +279,7 @@
<field name="name">mrp.production.work.order.calendar</field>
<field name="model">mrp.workorder</field>
<field name="arch" type="xml">
<calendar date_stop="date_planned_finished" date_start="date_planned_start" string="Operations" color="workcenter_id" event_limit="5" delete="0" create="0">
<calendar date_stop="date_finished" date_start="date_start" string="Operations" color="workcenter_id" event_limit="5" delete="0" create="0">
<field name="workcenter_id" filters="1"/>
<field name="production_id"/>
<field name="state"/>
@@ -319,7 +318,7 @@
<field name="name">mrp.production.work.order.gantt.production</field>
<field name="model">mrp.workorder</field>
<field name="arch" type="xml">
<gantt class="o_mrp_workorder_gantt" date_stop="date_planned_finished" date_start="date_planned_start" string="Operations" default_group_by="production_id" create="0" delete="0"
<gantt class="o_mrp_workorder_gantt" date_stop="date_finished" date_start="date_start" string="Operations" default_group_by="production_id" create="0" delete="0"
progress="progress" plan="0"
decoration-danger="json_popover and 'text-danger' in json_popover"
decoration-success="state == 'done'"
@@ -333,7 +332,7 @@
sample="1"
form_view_id="%(mrp_production_workorder_form_view_inherit)d">
<field name="date_planned_start"/>
<field name="date_start"/>
<field name="state"/>
<field name="workcenter_id"/>
<field name="json_popover"/>
@@ -343,8 +342,8 @@
<div class="row g-0">
<div class="col">
<ul class="ps-1 mb-0 list-unstyled">
<li><strong>Start Date: </strong> <t t-esc="date_planned_start.toFormat('F')"/></li>
<li><strong>Stop Date: </strong> <t t-esc="date_planned_finished.toFormat('F')"/></li>
<li><strong>Start Date: </strong> <t t-esc="date_start.toFormat('F')"/></li>
<li><strong>Stop Date: </strong> <t t-esc="date_finished.toFormat('F')"/></li>
<li><strong>Workcenter: </strong> <t t-esc="workcenter_id[1]"/></li>
</ul>
</div>
@@ -359,7 +358,7 @@
<field name="name">mrp.workorder.view.gantt</field>
<field name="model">mrp.workorder</field>
<field name="arch" type="xml">
<gantt class="o_mrp_workorder_gantt" date_stop="date_planned_finished" date_start="date_planned_start" string="Operations" default_group_by="workcenter_id" create="0"
<gantt class="o_mrp_workorder_gantt" date_stop="date_finished" date_start="date_start" string="Operations" default_group_by="workcenter_id" create="0"
delete="0" sample="1"
progress="progress" plan="0"
decoration-danger="json_popover and 'text-danger' in json_popover"
@@ -373,7 +372,7 @@
precision="{'day': 'hour:full', 'week': 'day:full', 'month': 'day:full', 'year': 'day:full'}"
form_view_id="mrp_production_workorder_form_view_inherit">
<field name="date_planned_start"/>
<field name="date_start"/>
<field name="state"/>
<field name="workcenter_id"/>
<field name="json_popover"/>
@@ -383,8 +382,8 @@
<div class="row g-0">
<div class="col">
<ul class="ps-1 mb-0 list-unstyled">
<li><strong>Start Date: </strong><t t-esc="date_planned_start.toFormat('F')"/></li>
<li><strong>Stop Date: </strong><t t-esc="date_planned_finished.toFormat('F')"/></li>
<li><strong>Start Date: </strong><t t-esc="date_start.toFormat('F')"/></li>
<li><strong>Stop Date: </strong><t t-esc="date_finished.toFormat('F')"/></li>
<li><strong>Workcenter: </strong> <t t-esc="workcenter_id[1]"/></li>
</ul>
</div>
@@ -411,7 +410,7 @@
<field name="product_id"/>
<field name="qty_remaining"/>
<field name="qty_production"/>
<field name="date_planned_start"/>
<field name="date_start"/>
<field name="production_date"/>
<field name="product_uom_id" force_save="1"/>
<field name="operation_id"/>
@@ -425,7 +424,7 @@
</h4>
</div>
<div class="o_kanban_workorder_date">
<h5><span class="d-flex" t-esc="record.date_planned_start.value or record.production_date.value"/></h5>
<h5><span class="d-flex" t-esc="record.date_start.value or record.production_date.value"/></h5>
</div>
<div>
<h2 class="ml8">
+3 -3
View File
@@ -48,13 +48,13 @@ class MrpProductionSplit(models.TransientModel):
commands.append(Command.create({
'quantity': quantity,
'user_id': wizard.production_id.user_id,
'date': wizard.production_id.date_planned_start,
'date': wizard.production_id.date_start,
}))
remaining_quantity = float_round(remaining_quantity - quantity, precision_rounding=wizard.product_uom_id.rounding)
commands.append(Command.create({
'quantity': remaining_quantity,
'user_id': wizard.production_id.user_id,
'date': wizard.production_id.date_planned_start,
'date': wizard.production_id.date_start,
}))
wizard.production_detailed_vals_ids = commands
@@ -69,7 +69,7 @@ class MrpProductionSplit(models.TransientModel):
productions = self.production_id._split_productions({self.production_id: [detail.quantity for detail in self.production_detailed_vals_ids]})
for production, detail in zip(productions, self.production_detailed_vals_ids):
production.user_id = detail.user_id
production.date_planned_start = detail.date
production.date_start = detail.date
if self.production_split_multi_id:
saved_production_split_multi_id = self.production_split_multi_id.id
self.production_split_multi_id.production_ids = [Command.unlink(self.id)]
+2 -2
View File
@@ -9,7 +9,7 @@
<field name="location_dest_id" ref="stock.stock_location_stock"/>
<field name="bom_id" ref="mrp.mrp_bom_drawer"/>
<field name="create_date" eval="(datetime.now() - relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="date_planned_start" eval="(datetime.now() - relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="date_start" eval="(datetime.now() - relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
</record>
<record id="mrp_production_drawer_3" model="mrp.production">
@@ -20,7 +20,7 @@
<field name="location_dest_id" ref="stock.stock_location_stock"/>
<field name="bom_id" ref="mrp.mrp_bom_drawer"/>
<field name="create_date" eval="(datetime.now() - relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="date_planned_start" eval="(datetime.now() - relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="date_start" eval="(datetime.now() - relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
</record>
<function model="mrp.production" name="action_confirm" eval="[[
@@ -79,8 +79,8 @@ class StockMove(models.Model):
if move.state in ('done', 'cancel') or not move.is_subcontract:
continue
move.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel')).write({
'date_planned_finished': move.date,
'date_planned_start': move.date,
'date_start': move.date,
'date_finished': move.date,
})
return res
@@ -139,7 +139,7 @@ class StockPicking(models.Model):
'location_dest_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'product_qty': subcontract_move.product_uom_qty,
'picking_type_id': warehouse.subcontracting_type_id.id,
'date_planned_start': subcontract_move.date - relativedelta(days=bom.produce_delay)
'date_start': subcontract_move.date - relativedelta(days=bom.produce_delay)
}
return vals
@@ -155,7 +155,7 @@ class StockPicking(models.Model):
mo = self.env['mrp.production'].with_company(move.company_id).create(self._prepare_subcontract_mo_vals(move, bom))
self.env['stock.move'].create(mo._get_moves_raw_values())
self.env['stock.move'].create(mo._get_moves_finished_values())
mo.date_planned_finished = move.date # Avoid to have the picking late depending of the MO
mo.date_finished = move.date # Avoid to have the picking late depending of the MO
mo.action_confirm()
# Link the finished to the receipt move.
@@ -80,9 +80,9 @@ class TestSaleMrpLeadTime(TestStockCommon):
)
# Check schedule date and deadline of manufacturing order
mo_scheduled = out_date - timedelta(days=manufacturing_order.bom_id.produce_delay) - timedelta(days=company.manufacturing_lead)
mo_date_start = out_date - timedelta(days=manufacturing_order.bom_id.produce_delay) - timedelta(days=company.manufacturing_lead)
self.assertAlmostEqual(
fields.Datetime.from_string(manufacturing_order.date_planned_start), mo_scheduled,
fields.Datetime.from_string(manufacturing_order.date_start), mo_date_start,
delta=timedelta(seconds=1),
msg="Schedule date of manufacturing order should be equal to: Schedule date of picking - product's Manufacturing Lead Time - company's Manufacturing Lead Time."
)
@@ -154,9 +154,9 @@ class TestSaleMrpLeadTime(TestStockCommon):
)
# Check schedule date and deadline date of manufacturing order
mo_scheduled = out_date - timedelta(days=manufacturing_order.bom_id.produce_delay) - timedelta(days=self.warehouse_1.delivery_route_id.rule_ids[0].delay) - timedelta(days=self.env.ref('base.main_company').manufacturing_lead)
mo_date_start = out_date - timedelta(days=manufacturing_order.bom_id.produce_delay) - timedelta(days=self.warehouse_1.delivery_route_id.rule_ids[0].delay) - timedelta(days=self.env.ref('base.main_company').manufacturing_lead)
self.assertAlmostEqual(
fields.Datetime.from_string(manufacturing_order.date_planned_start), mo_scheduled,
fields.Datetime.from_string(manufacturing_order.date_start), mo_date_start,
delta=timedelta(seconds=1),
msg="Schedule date of manufacturing order should be equal to: Schedule date of picking - product's Manufacturing Lead Time- delay pull_rule."
)
@@ -22,7 +22,7 @@
t-options="{'date_only': True}"/>
<span t-if="mo.state in ['draft', 'confirmed', 'progress', 'to_close']"
class="text-muted"
t-field="mo.date_planned_finished"
t-field="mo.date_finished"
t-options="{'date_only': True}"/>
</div>
</div>