diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 2973dc353ee..428e8ff5cba 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -228,7 +228,7 @@ class MrpProduction(models.Model): 'procurement.group', 'Procurement Group', copy=False) product_description_variants = fields.Char('Custom Description') - orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Orderpoint') + orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Orderpoint', copy=False) propagate_cancel = fields.Boolean( 'Propagate cancel and split', help='If checked, when the previous move of the move (which was generated by a next procurement) is cancelled or split, the move generated by this move will too') @@ -767,20 +767,6 @@ class MrpProduction(models.Model): if vals.get('date_planned_finished'): production.move_finished_ids.write({'date': production.date_planned_finished}) if any(field in ['move_raw_ids', 'move_finished_ids', 'workorder_ids'] for field in vals) and production.state != 'draft': - if production.state == 'done': - # for some reason moves added after state = 'done' won't save group_id, reference if added in - # "stock_move.default_get()" - production.move_raw_ids.filtered(lambda move: move.additional and move.date > production.date_planned_start).write({ - 'group_id': production.procurement_group_id.id, - 'reference': production.name, - 'date': production.date_planned_start, - 'date_deadline': production.date_planned_start - }) - production.move_finished_ids.filtered(lambda move: move.additional and move.date > production.date_planned_finished).write({ - 'reference': production.name, - 'date': production.date_planned_finished, - 'date_deadline': production.date_deadline - }) production._autoconfirm_production() if production in production_to_replan: production._plan_workorders(replan=True) @@ -820,12 +806,6 @@ class MrpProduction(models.Model): vals['procurement_group_id'] = self.env["procurement.group"].create(procurement_group_vals).id productions = super().create(vals_list) for production in productions: - (production.move_raw_ids | production.move_finished_ids).write({ - 'group_id': production.procurement_group_id.id, - 'origin': production.name - }) - production.move_raw_ids.write({'date': production.date_planned_start}) - production.move_finished_ids.write({'date': production.date_planned_finished}) # Trigger move_raw creation when importing a file if 'import_file' in self.env.context: production._onchange_move_raw() @@ -912,10 +892,6 @@ class MrpProduction(models.Model): move_dest_ids = self.move_dest_ids if len(group_orders) > 1: move_dest_ids |= group_orders[0].move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_dest_ids - date_planned_finished = self.date_planned_start + relativedelta(days=self.product_id.produce_delay) - date_planned_finished = date_planned_finished + relativedelta(days=self.company_id.manufacturing_lead) - if date_planned_finished == self.date_planned_start: - date_planned_finished = date_planned_finished + relativedelta(hours=1) return { 'product_id': product_id, 'product_uom_qty': product_uom_qty, @@ -923,7 +899,7 @@ class MrpProduction(models.Model): 'operation_id': operation_id, 'byproduct_id': byproduct_id, 'name': self.name, - 'date': date_planned_finished, + 'date': self._get_date_planned_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, @@ -938,6 +914,13 @@ class MrpProduction(models.Model): 'cost_share': cost_share, } + def _get_date_planned_finished(self): + date_planned_finished = self.date_planned_start + relativedelta(days=self.product_id.produce_delay) + date_planned_finished = date_planned_finished + relativedelta(days=self.company_id.manufacturing_lead) + 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: @@ -999,12 +982,11 @@ class MrpProduction(models.Model): return moves def _get_move_raw_values(self, product_id, product_uom_qty, product_uom, operation_id=False, bom_line=False): + """ Warning, any changes done to this method will need to be repeated for consistency in: + - Manually added components, i.e. "default_" values in view + - Moves from a copied MO, i.e. move.create + - Existing moves during backorder creation """ source_location = self.location_src_id - origin = self.name - if self.orderpoint_id: - origin = self.origin.replace( - '%s - ' % (self.orderpoint_id.display_name), '') - origin = '%s,%s' % (origin, self.name) data = { 'sequence': bom_line.sequence if bom_line else 10, 'name': self.name, @@ -1022,7 +1004,7 @@ class MrpProduction(models.Model): 'operation_id': operation_id, 'price_unit': product_id.standard_price, 'procure_method': 'make_to_stock', - 'origin': origin, + 'origin': self._get_origin(), 'state': 'draft', 'warehouse_id': source_location.warehouse_id.id, 'group_id': self.procurement_group_id.id, @@ -1030,6 +1012,14 @@ class MrpProduction(models.Model): } return data + def _get_origin(self): + origin = self.name + if self.orderpoint_id and self.origin: + origin = self.origin.replace( + '%s - ' % (self.orderpoint_id.display_name), '') + origin = '%s,%s' % (origin, self.name) + return origin + def _set_qty_producing(self): if self.product_id.tracking == 'serial': qty_producing_uom = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP') @@ -1095,9 +1085,6 @@ class MrpProduction(models.Model): additional_moves = production.move_raw_ids.filtered( lambda move: move.state == 'draft' ) - additional_moves.write({ - 'group_id': production.procurement_group_id.id, - }) additional_moves._adjust_procure_method() moves_to_confirm |= additional_moves additional_byproducts = production.move_finished_ids.filtered( @@ -1496,6 +1483,8 @@ class MrpProduction(models.Model): 'lot_producing_id': False, 'origin': self.origin, 'state': 'confirmed', + 'date_deadline': self.date_deadline, + 'orderpoint_id': self.orderpoint_id.id, } def _split_productions(self, amounts=False, cancel_remaning_qty=False): @@ -1534,6 +1523,8 @@ class MrpProduction(models.Model): if production.backorder_sequence == 0: # Activate backorder naming production.backorder_sequence = 1 production.name = self._get_name_backorder(production.name, production.backorder_sequence) + (production.move_raw_ids | production.move_finished_ids).name = production.name + (production.move_raw_ids | production.move_finished_ids).origin = production._get_origin() production.product_qty = amounts[production][0] backorder_vals = production.copy_data(default=production._get_backorder_mo_vals())[0] backorder_qtys = amounts[production][1:] diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 65c48ce1483..726b60fffd8 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from collections import defaultdict from odoo import api, Command, fields, models from odoo.osv import expression from odoo.tools import float_compare, float_round, float_is_zero, OrderedSet @@ -242,6 +243,40 @@ class StockMove(models.Model): defaults['reference'] = production_id.name return defaults + @api.model_create_multi + def create(self, vals_list): + """ Enforce consistent values (i.e. match _get_move_raw_values/_get_move_finished_values) for: + - Manually added components/byproducts specifically values we can't set via view with "default_" + - Moves from a copied MO + - Backorders + """ + mo_id_to_mo = defaultdict(lambda: self.env['mrp.production']) + product_id_to_product = defaultdict(lambda: self.env['product.product']) + for values in vals_list: + mo_id = values.get('raw_material_production_id', False) or values.get('production_id', False) + if mo_id: + mo = mo_id_to_mo[mo_id] + if not mo: + mo = mo.browse(mo_id) + mo_id_to_mo[mo_id] = mo + values['name'] = mo.name + values['origin'] = mo._get_origin() + values['group_id'] = mo.procurement_group_id.id + values['propagate_cancel'] = mo.propagate_cancel + if values.get('raw_material_production_id', False): + product = product_id_to_product[values['product_id']] + if not product: + product = product.browse(values['product_id']) + product_id_to_product[values['product_id']] = product + values['location_dest_id'] = mo.production_location_id.id + values['price_unit'] = product.standard_price + continue + # produced products + byproducts + values['location_id'] = mo.production_location_id.id + values['date'] = mo._get_date_planned_finished() + values['date_deadline'] = mo.date_deadline + return super().create(vals_list) + def write(self, vals): if 'product_uom_qty' in vals and 'move_line_ids' in vals: # first update lines then product_uom_qty as the later will unreserve @@ -490,3 +525,8 @@ class StockMove(models.Model): self.move_line_ids = self._set_quantity_done_prepare_vals(quantity_done) else: super()._multi_line_quantity_done_set(quantity_done) + + def _prepare_extra_move_vals(self, qty): + vals = super()._prepare_extra_move_vals(qty) + vals['date_deadline'] = self.date_deadline + return vals diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml index 0592513303d..f5bc9847ec0 100644 --- a/addons/mrp/views/mrp_production_views.xml +++ b/addons/mrp/views/mrp_production_views.xml @@ -263,7 +263,7 @@ @@ -285,7 +285,6 @@ - @@ -344,7 +343,7 @@ - + @@ -374,6 +373,7 @@ + diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 12a60569086..745b8276dcc 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -34,7 +34,7 @@ class StockMove(models.Model): 'Date Scheduled', default=fields.Datetime.now, index=True, required=True, help="Scheduled date until move is done, then date of actual move processing") date_deadline = fields.Datetime( - "Deadline", readonly=True, + "Deadline", readonly=True, copy=False, help="Date Promise to the customer on the top level document (SO/PO)") company_id = fields.Many2one( 'res.company', 'Company', @@ -1691,6 +1691,7 @@ class StockMove(models.Model): 'move_orig_ids': [(4, x.id) for x in self.move_orig_ids], 'origin_returned_move_id': self.origin_returned_move_id.id, 'price_unit': self.price_unit, + 'date_deadline': self.date_deadline, } if self.env.context.get('force_split_uom_id'): vals['product_uom'] = self.env.context['force_split_uom_id']