From 6eaa4a2ae3b12b244f4c4277ef9cbc172f492f0a Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Thu, 10 Jun 2021 07:55:27 +0000 Subject: [PATCH] [IMP] (purchase_)stock: suppliers information in replenishment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Infobox to help the user with the replenishment. Allow those actions: - See the different supplier with their lead time, price and quantity - Be able to select a supplier and set it on the orderpoint ⁻ See the last delivery by mmonth for the product - Last purchase date for each supplier Technicaly it use a wizard and a fields char with JSON used by a widget to display a static template. Also it creates directly the wizard in backend instead of just let the view manage the new object since we need a button on a one2many and if the records do not have an id, it's not possilbe to use it. closes odoo/odoo#72039 Task: 2519761 Related: odoo/upgrade#2596 Signed-off-by: Arnold Moyaux --- addons/mrp/models/stock_rule.py | 4 +- addons/purchase_stock/__init__.py | 1 + addons/purchase_stock/__manifest__.py | 1 + addons/purchase_stock/models/product.py | 46 +++++++++++ addons/purchase_stock/models/stock.py | 10 +++ addons/purchase_stock/models/stock_rule.py | 6 +- addons/purchase_stock/views/stock_views.xml | 6 +- addons/purchase_stock/wizard/__init__.py | 4 + .../wizard/stock_replenishment_info.py | 19 +++++ .../wizard/stock_replenishment_info.xml | 43 ++++++++++ addons/stock/__manifest__.py | 1 + addons/stock/models/stock_orderpoint.py | 54 +++++++------ addons/stock/models/stock_rule.py | 2 +- addons/stock/security/ir.model.access.csv | 1 + addons/stock/static/src/js/forecast_widget.js | 14 ++++ .../js/stock_orderpoint_list_controller.js | 3 +- .../stock/static/src/xml/stock_orderpoint.xml | 31 +++++--- addons/stock/views/stock_orderpoint_views.xml | 7 +- addons/stock/wizard/__init__.py | 1 + .../stock/wizard/stock_replenishment_info.py | 79 +++++++++++++++++++ .../stock/wizard/stock_replenishment_info.xml | 34 ++++++++ 21 files changed, 318 insertions(+), 49 deletions(-) create mode 100644 addons/purchase_stock/wizard/__init__.py create mode 100644 addons/purchase_stock/wizard/stock_replenishment_info.py create mode 100644 addons/purchase_stock/wizard/stock_replenishment_info.xml create mode 100644 addons/stock/wizard/stock_replenishment_info.py create mode 100644 addons/stock/wizard/stock_replenishment_info.xml diff --git a/addons/mrp/models/stock_rule.py b/addons/mrp/models/stock_rule.py index d50e8a9b1c8..7ca58c1363a 100644 --- a/addons/mrp/models/stock_rule.py +++ b/addons/mrp/models/stock_rule.py @@ -111,11 +111,11 @@ class StockRule(models.Model): date_planned = date_planned - relativedelta(hours=1) return date_planned - def _get_lead_days(self, product): + def _get_lead_days(self, product, **values): """Add the product and company manufacture delay to the cumulative delay and cumulative description. """ - delay, delay_description = super()._get_lead_days(product) + delay, delay_description = super()._get_lead_days(product, **values) bypass_delay_description = self.env.context.get('bypass_delay_description') manufacture_rule = self.filtered(lambda r: r.action == 'manufacture') if not manufacture_rule: diff --git a/addons/purchase_stock/__init__.py b/addons/purchase_stock/__init__.py index cc821960bef..9931f706a6e 100644 --- a/addons/purchase_stock/__init__.py +++ b/addons/purchase_stock/__init__.py @@ -4,6 +4,7 @@ from . import models from . import report from . import populate +from . import wizard from odoo import api, SUPERUSER_ID diff --git a/addons/purchase_stock/__manifest__.py b/addons/purchase_stock/__manifest__.py index b3e884348d1..09a3c9c8d73 100644 --- a/addons/purchase_stock/__manifest__.py +++ b/addons/purchase_stock/__manifest__.py @@ -25,6 +25,7 @@ 'report/purchase_report_templates.xml', 'report/report_stock_forecasted.xml', 'report/report_stock_rule.xml', + 'wizard/stock_replenishment_info.xml' ], 'demo': [ 'data/purchase_stock_demo.xml', diff --git a/addons/purchase_stock/models/product.py b/addons/purchase_stock/models/product.py index 4c201c07ba0..461d84d65c3 100644 --- a/addons/purchase_stock/models/product.py +++ b/addons/purchase_stock/models/product.py @@ -69,3 +69,49 @@ class ProductProduct(models.Model): qty_by_product_location[(product.id, location.id)] += product_qty qty_by_product_wh[(product.id, location.warehouse_id.id)] += product_qty return qty_by_product_location, qty_by_product_wh + + +class SupplierInfo(models.Model): + _inherit = 'product.supplierinfo' + + last_purchase_date = fields.Date('Last Purchase', compute='_compute_last_purchase_date') + show_set_supplier_button = fields.Boolean( + 'Show Set Supplier Button', compute='_compute_show_set_supplier_button') + + def _compute_last_purchase_date(self): + self.last_purchase_date = False + purchases = self.env['purchase.order'].search([ + ('state', 'in', ('purchase', 'done')), + ('order_line.product_id', 'in', + self.product_tmpl_id.product_variant_ids.ids), + ('partner_id', 'in', self.name.ids), + ], order='date_order') + for supplier in self: + products = supplier.product_tmpl_id.product_variant_ids + for purchase in purchases: + if purchase.partner_id != supplier.name: + continue + if not (products & purchase.order_line.product_id): + continue + supplier.last_purchase_date = purchase.date_order + break + + def _compute_show_set_supplier_button(self): + self.show_set_supplier_button = True + orderpoint_id = self.env.context.get('default_orderpoint_id') + orderpoint = self.env['stock.warehouse.orderpoint'].browse(orderpoint_id) + if orderpoint_id: + self.filtered( + lambda s: s.id == orderpoint.supplier_id.id + ).show_set_supplier_button = False + + def action_set_supplier(self): + self.ensure_one() + orderpoint_id = self.env.context.get('orderpoint_id') + orderpoint = self.env['stock.warehouse.orderpoint'].browse(orderpoint_id) + if not orderpoint: + return + orderpoint.route_id = self.env['stock.rule'].search([('action', '=', 'buy')], limit=1).route_id.id + orderpoint.supplier_id = self + if orderpoint.qty_to_order < self.min_qty: + orderpoint.qty_to_order = self.min_qty diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py index 937b7cad6bc..7a406a198c4 100644 --- a/addons/purchase_stock/models/stock.py +++ b/addons/purchase_stock/models/stock.py @@ -193,6 +193,10 @@ class Orderpoint(models.Model): """ Extend to add more depends values """ return super()._compute_qty() + @api.depends('supplier_id') + def _compute_lead_days(self): + return super()._compute_lead_days() + @api.depends('route_id') def _compute_show_suppplier(self): buy_route = [] @@ -216,6 +220,12 @@ class Orderpoint(models.Model): return result + def _get_lead_days_values(self): + values = super()._get_lead_days_values() + if self.supplier_id: + values['supplierinfo'] = self.supplier_id + return values + def _get_replenishment_order_notification(self): self.ensure_one() order = self.env['purchase.order.line'].search([ diff --git a/addons/purchase_stock/models/stock_rule.py b/addons/purchase_stock/models/stock_rule.py index db8b008612d..ccbda968748 100644 --- a/addons/purchase_stock/models/stock_rule.py +++ b/addons/purchase_stock/models/stock_rule.py @@ -141,16 +141,16 @@ class StockRule(models.Model): procurement.values, po)) self.env['purchase.order.line'].sudo().create(po_line_values) - def _get_lead_days(self, product): + def _get_lead_days(self, product, **values): """Add the company security lead time, days to purchase and the supplier delay to the cumulative delay and cumulative description. The days to purchase and company lead time are always displayed for onboarding purpose in order to indicate that those options are available. """ - delay, delay_description = super()._get_lead_days(product) + delay, delay_description = super()._get_lead_days(product, **values) bypass_delay_description = self.env.context.get('bypass_delay_description') buy_rule = self.filtered(lambda r: r.action == 'buy') - seller = product.with_company(buy_rule.company_id)._select_seller() + seller = 'supplierinfo' in values and values['supplierinfo'] or product.with_company(buy_rule.company_id)._select_seller() if not buy_rule or not seller: return delay, delay_description buy_rule.ensure_one() diff --git a/addons/purchase_stock/views/stock_views.xml b/addons/purchase_stock/views/stock_views.xml index 84642291c7d..0e439d24556 100644 --- a/addons/purchase_stock/views/stock_views.xml +++ b/addons/purchase_stock/views/stock_views.xml @@ -30,10 +30,10 @@ stock.warehouse.orderpoint - + - - + + diff --git a/addons/purchase_stock/wizard/__init__.py b/addons/purchase_stock/wizard/__init__.py new file mode 100644 index 00000000000..28595d05268 --- /dev/null +++ b/addons/purchase_stock/wizard/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import stock_replenishment_info diff --git a/addons/purchase_stock/wizard/stock_replenishment_info.py b/addons/purchase_stock/wizard/stock_replenishment_info.py new file mode 100644 index 00000000000..0c6b2db3857 --- /dev/null +++ b/addons/purchase_stock/wizard/stock_replenishment_info.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class StockReplenishmentInfo(models.TransientModel): + _inherit = 'stock.replenishment.info' + _description = 'Stock supplier replenishment information' + + supplierinfo_id = fields.Many2one(related='orderpoint_id.supplier_id') + supplierinfo_ids = fields.Many2many( + 'product.supplierinfo', compute='_compute_supplierinfo_ids', + store=True) + + @api.depends('orderpoint_id') + def _compute_supplierinfo_ids(self): + for replenishment_info in self: + replenishment_info.supplierinfo_ids = replenishment_info.product_id.seller_ids diff --git a/addons/purchase_stock/wizard/stock_replenishment_info.xml b/addons/purchase_stock/wizard/stock_replenishment_info.xml new file mode 100644 index 00000000000..bad5fdbd5b2 --- /dev/null +++ b/addons/purchase_stock/wizard/stock_replenishment_info.xml @@ -0,0 +1,43 @@ + + + + + product.supplierinfo.replenishment.tree.view + product.supplierinfo + + primary + + + + + @@ -47,4 +44,18 @@ +
+

Sales History

+
+ + + + + + + + +
+
+
diff --git a/addons/stock/views/stock_orderpoint_views.xml b/addons/stock/views/stock_orderpoint_views.xml index d4c6b763c88..27ef1cab803 100644 --- a/addons/stock/views/stock_orderpoint_views.xml +++ b/addons/stock/views/stock_orderpoint_views.xml @@ -48,8 +48,9 @@ - +