From e40553723b893bcd2244e9b9fcc5c1a7e1bf27d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Wed, 29 Jul 2020 14:19:26 +0000 Subject: [PATCH] [IMP] stock,mrp,purchase: refactor priority - MO/picking/PO/stock.move have now the "same" `priority` fields (Boolean logic: Normal or Urgent). These models are now sort by priority first. Note that the priority field of stock move is a technical one. - For MO and picking, the set of `priority` field change priority of the related stock move. The prioriry field on stock move is used by the scheduler for reservation ('Urgent' before 'Normal') - The Stock Replenishment Report also take in account the priority to be consistent with the reservation. task-2246665 --- addons/mrp/models/mrp_production.py | 11 ++++++---- addons/mrp/models/stock_move.py | 6 ++++++ addons/mrp/views/mrp_production_views.xml | 12 ++++++++--- addons/purchase/models/purchase.py | 6 ++++-- addons/purchase/views/purchase_views.xml | 9 ++++++++- addons/stock/models/stock_move.py | 13 +++++++++--- addons/stock/models/stock_picking.py | 20 ++----------------- addons/stock/models/stock_rule.py | 4 ++-- .../stock/report/report_stock_forecasted.py | 4 ++-- addons/stock/views/stock_picking_views.xml | 16 +++++++++------ .../data/stock_picking_batch_demo.xml | 6 +++--- 11 files changed, 63 insertions(+), 44 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index da06b8f9db6..ca6f098bd11 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -14,6 +14,8 @@ from odoo import api, fields, models, _ from odoo.exceptions import AccessError, UserError from odoo.tools import float_compare, float_round, float_is_zero, format_datetime +from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES + SIZE_BACK_ORDER_NUMERING = 3 @@ -23,7 +25,7 @@ class MrpProduction(models.Model): _description = 'Production Order' _date_name = 'date_planned_start' _inherit = ['mail.thread', 'mail.activity.mixin'] - _order = 'date_planned_start asc,id' + _order = 'priority desc, date_planned_start asc,id' @api.model def _get_default_picking_type(self): @@ -71,6 +73,9 @@ class MrpProduction(models.Model): name = fields.Char( 'Reference', copy=False, readonly=True, default=lambda x: _('New')) + priority = fields.Selection( + PROCUREMENT_PRIORITIES, string='Priority', default='0', index=True, + help="Components will be reserved first for the MO with the highest priorities.") backorder_sequence = fields.Integer("Backorder Sequence", default=0, copy=False, help="Backorder sequence, if equals to 0 means there is not related backorder") origin = fields.Char( 'Source', copy=False, @@ -229,8 +234,6 @@ class MrpProduction(models.Model): help='The change must be higher than this value to be propagated') scrap_ids = fields.One2many('stock.scrap', 'production_id', 'Scraps') scrap_count = fields.Integer(compute='_compute_scrap_move_count', string='Scrap Move') - priority = fields.Selection([('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')], 'Priority', - readonly=True, states={'draft': [('readonly', False)]}, default='1') is_locked = fields.Boolean('Is Locked', default=_get_default_is_locked, copy=False) is_planned = fields.Boolean('Its Operations are Planned', compute="_compute_is_planned") is_partially_planned = fields.Boolean('One operation is Planned', compute="_compute_is_planned") @@ -1471,7 +1474,7 @@ class MrpProduction(models.Model): }) for production in self: - production.write({'date_finished': fields.Datetime.now(), 'product_qty': production.qty_produced}) + production.write({'date_finished': fields.Datetime.now(), 'product_qty': production.qty_produced, 'priority': '0'}) for workorder in self.workorder_ids.filtered(lambda w: w.state not in ('done', 'cancel')): workorder.duration_expected = workorder._get_duration_expected() diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 294b2feeee9..35cd2b073d3 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -119,6 +119,12 @@ class StockMove(models.Model): .filtered(lambda ml: ml.qty_done == 0.0)\ .write({'move_id': new_move, 'product_uom_qty': 0}) + @api.depends('raw_material_production_id.priority') + def _compute_priority(self): + super()._compute_priority() + for move in self: + move.priority = move.raw_material_production_id.priority or move.priority or '0' + @api.depends('raw_material_production_id.move_finished_ids.move_line_ids.lot_id') def _compute_order_finished_lot_ids(self): for move in self: diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml index 84443ff5251..f62ea932cd8 100644 --- a/addons/mrp/views/mrp_production_views.xml +++ b/addons/mrp/views/mrp_production_views.xml @@ -6,11 +6,12 @@ mrp.production.tree mrp.production - +
+
-

+

+ + +

@@ -366,7 +370,8 @@
-
+ +
@@ -444,6 +449,7 @@ + diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 4cf42269166..137357dd7af 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -18,7 +18,7 @@ class PurchaseOrder(models.Model): _name = "purchase.order" _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin'] _description = "Purchase Order" - _order = 'date_order desc, id desc' + _order = 'priority desc, date_order desc, id desc' @api.depends('order_line.price_total') def _amount_all(self): @@ -71,6 +71,8 @@ class PurchaseOrder(models.Model): } name = fields.Char('Order Reference', required=True, index=True, copy=False, default='New') + priority = fields.Selection( + [('0', 'Normal'), ('1', 'Urgent')], 'Priority', default='0', index=True) origin = fields.Char('Source Document', copy=False, help="Reference of the document that generated this purchase order " "request (e.g. a sales order)") @@ -406,7 +408,7 @@ class PurchaseOrder(models.Model): self.write({'state': 'purchase'}) def button_done(self): - self.write({'state': 'done'}) + self.write({'state': 'done', 'priority': '0'}) def _add_supplier_to_product(self): # Add the partner in the supplier list of the product if the supplier is not registered for diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml index 6faeb1b1218..a1bede8765b 100644 --- a/addons/purchase/views/purchase_views.xml +++ b/addons/purchase/views/purchase_views.xml @@ -164,6 +164,7 @@ Request for Quotation Purchase Order

+

@@ -393,6 +394,7 @@ + @@ -434,6 +436,7 @@ + @@ -479,7 +482,8 @@
-
+ +
@@ -507,6 +511,7 @@ + @@ -540,6 +545,7 @@
+ @@ -576,6 +582,7 @@
+ diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 5709abe0f9e..5b478120e54 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -16,7 +16,7 @@ from odoo.exceptions import UserError from odoo.tools.float_utils import float_compare, float_is_zero, float_repr, float_round from odoo.tools.misc import format_date -PROCUREMENT_PRIORITIES = [('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')] +PROCUREMENT_PRIORITIES = [('0', 'Normal'), ('1', 'Urgent')] class StockMove(models.Model): @@ -31,7 +31,9 @@ class StockMove(models.Model): name = fields.Char('Description', index=True, required=True) sequence = fields.Integer('Sequence', default=10) - priority = fields.Selection(PROCUREMENT_PRIORITIES, 'Priority', default='1') + priority = fields.Selection( + PROCUREMENT_PRIORITIES, 'Priority', default='0', + compute="_compute_priority", store=True, index=True) create_date = fields.Datetime('Creation Date', index=True, readonly=True) date = fields.Datetime( 'Date', default=fields.Datetime.now, index=True, required=True, @@ -198,6 +200,11 @@ class StockMove(models.Model): not move.picking_type_id.use_existing_lots ) + @api.depends('picking_id.priority') + def _compute_priority(self): + for move in self: + move.priority = move.picking_id.priority or '0' + @api.depends('picking_id.is_locked') def _compute_is_locked(self): for move in self: @@ -390,7 +397,7 @@ class StockMove(models.Model): total_availability = self.env['stock.quant']._get_available_quantity(move.product_id, move.location_id) if move.product_id else 0.0 move.availability = min(move.product_qty, total_availability) - @api.depends('product_id', 'picking_type_id', 'picking_id', 'reserved_availability') + @api.depends('product_id', 'picking_type_id', 'picking_id', 'reserved_availability', 'priority') def _compute_json_forecast(self): self.json_forecast = False if not any(self._ids): diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index a9b50f93168..d28dde6de9b 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -280,10 +280,7 @@ class Picking(models.Model): 'procurement.group', 'Procurement Group', readonly=True, related='move_lines.group_id', store=True) priority = fields.Selection( - PROCUREMENT_PRIORITIES, string='Priority', - compute='_compute_priority', inverse='_set_priority', store=True, - index=True, tracking=True, - states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, + PROCUREMENT_PRIORITIES, string='Priority', default='0', index=True, help="Products will be reserved first for the transfers with the highest priorities.") scheduled_date = fields.Datetime( 'Scheduled Date', compute='_compute_scheduled_date', inverse='_set_scheduled_date', store=True, @@ -483,19 +480,6 @@ class Picking(models.Model): else: picking.state = relevant_move_state - @api.depends('move_lines.priority') - def _compute_priority(self): - for picking in self: - if picking.mapped('move_lines'): - priorities = [priority for priority in picking.mapped('move_lines.priority') if priority] or ['1'] - picking.priority = max(priorities) - else: - picking.priority = '1' - - def _set_priority(self): - for picking in self: - picking.move_lines.write({'priority': picking.priority}) - @api.depends('move_lines.date_expected') def _compute_scheduled_date(self): for picking in self: @@ -767,7 +751,7 @@ class Picking(models.Model): new_move._action_confirm() todo_moves |= new_move todo_moves._action_done(cancel_backorder=self.env.context.get('cancel_backorder')) - self.write({'date_done': fields.Datetime.now()}) + self.write({'date_done': fields.Datetime.now(), 'priority': '0'}) # if incoming moves make other confirmed/partially_available moves available, assign them done_incoming_moves = self.filtered(lambda p: p.picking_type_id.code == 'incoming').move_lines.filtered(lambda m: m.state == 'done') diff --git a/addons/stock/models/stock_rule.py b/addons/stock/models/stock_rule.py index 624ba668947..a4d10b153ad 100644 --- a/addons/stock/models/stock_rule.py +++ b/addons/stock/models/stock_rule.py @@ -320,7 +320,7 @@ class StockRule(models.Model): 'propagate_date': self.propagate_date, 'propagate_date_minimum_delta': self.propagate_date_minimum_delta, 'description_picking': picking_description, - 'priority': values.get('priority', "1"), + 'priority': values.get('priority', "0"), 'delay_alert': self.delay_alert, 'orderpoint_id': values.get('orderpoint_id') and values['orderpoint_id'].id, } @@ -411,7 +411,7 @@ class ProcurementGroup(models.Model): procurement_errors = [] for procurement in procurements: procurement.values.setdefault('company_id', self.env.company) - procurement.values.setdefault('priority', '1') + procurement.values.setdefault('priority', '0') procurement.values.setdefault('date_planned', fields.Datetime.now()) if ( procurement.product_id.type not in ('consu', 'product') or diff --git a/addons/stock/report/report_stock_forecasted.py b/addons/stock/report/report_stock_forecasted.py index 581dd9204be..9d4ae670fa3 100644 --- a/addons/stock/report/report_stock_forecasted.py +++ b/addons/stock/report/report_stock_forecasted.py @@ -136,11 +136,11 @@ class ReplenishmentReport(models.AbstractModel): in_domain, out_domain = self._move_confirmed_domain( product_template_ids, product_variant_ids, wh_location_ids ) - outs = self.env['stock.move'].search(out_domain, order='date_expected, id') + outs = self.env['stock.move'].search(out_domain, order='priority desc, date_expected, id') outs_per_product = defaultdict(lambda: []) for out in outs: outs_per_product[out.product_id.id].append(out) - ins = self.env['stock.move'].search(in_domain, order='date_expected, id') + ins = self.env['stock.move'].search(in_domain, order='priority desc, date_expected, id') ins_per_product = defaultdict(lambda: []) for in_ in ins: ins_per_product[in_.product_id.id].append([in_.product_qty, in_]) diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml index 30d75b9e29e..646082c222d 100644 --- a/addons/stock/views/stock_picking_views.xml +++ b/addons/stock/views/stock_picking_views.xml @@ -160,7 +160,8 @@
-
+ +
@@ -193,6 +194,7 @@
+ @@ -208,7 +210,6 @@ - @@ -285,9 +286,12 @@
-

- -

+
+

+ + +

+
@@ -413,7 +417,6 @@ - @@ -445,6 +448,7 @@ + diff --git a/addons/stock_picking_batch/data/stock_picking_batch_demo.xml b/addons/stock_picking_batch/data/stock_picking_batch_demo.xml index fe2d8bfe191..f465feb5a00 100644 --- a/addons/stock_picking_batch/data/stock_picking_batch_demo.xml +++ b/addons/stock_picking_batch/data/stock_picking_batch_demo.xml @@ -53,7 +53,7 @@ one - 2 + 1 @@ -63,7 +63,7 @@ one - 1 + 0 @@ -73,7 +73,7 @@ one - 1 + 0