From b311d9ccba6eb1dd58fd952fb142404b043aefff Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Tue, 16 Jul 2019 08:09:53 +0000 Subject: [PATCH] [ADD] stock: report forecast quantity Create a new report in order to see quantity in the past and in the future. Also display the planned in and out moves at their schedule date. The view available for this report is a graph view even if it could be display with other views. Technically it's an SQL views based on stock.quant and stock.move Currently the interval for the report is -3 months, +3 months because the forecast quantity is based on quants - moves during this interval in order to keep a linear performance that's not growing with the data. task: 2000948 closes odoo/odoo#34898 Signed-off-by: Simon Lejeune (sle) --- addons/stock/__manifest__.py | 1 + addons/stock/report/__init__.py | 1 + addons/stock/report/report_stock_quantity.py | 99 +++++++++++++++++++ addons/stock/report/report_stock_quantity.xml | 55 +++++++++++ addons/stock/security/ir.model.access.csv | 1 + addons/stock/security/stock_security.xml | 7 ++ addons/stock/views/product_views.xml | 33 +++++++ 7 files changed, 197 insertions(+) create mode 100644 addons/stock/report/report_stock_quantity.py create mode 100644 addons/stock/report/report_stock_quantity.xml diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index 217bec03858..7df37828b1e 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -26,6 +26,7 @@ 'data/stock_traceability_report_data.xml', 'data/procurement_data.xml', + 'report/report_stock_quantity.xml', 'report/stock_report_views.xml', 'report/report_package_barcode.xml', 'report/report_lot_barcode.xml', diff --git a/addons/stock/report/__init__.py b/addons/stock/report/__init__.py index 55ce8712c5c..437a4c100c9 100644 --- a/addons/stock/report/__init__.py +++ b/addons/stock/report/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import report_stock_quantity from . import report_stock_rule diff --git a/addons/stock/report/report_stock_quantity.py b/addons/stock/report/report_stock_quantity.py new file mode 100644 index 00000000000..d06a1a96b4d --- /dev/null +++ b/addons/stock/report/report_stock_quantity.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, tools + + +class ReportStockQuantity(models.Model): + _name = 'report.stock.quantity' + _auto = False + _description = 'Stock Quantity Report' + + date = fields.Date(string='Date', readonly=True) + product_tmpl_id = fields.Many2one('product.template', related='product_id.product_tmpl_id') + product_id = fields.Many2one('product.product', string='Product', readonly=True) + state = fields.Selection([ + ('forecast', 'Forecasted Stock'), + ('in', 'Forecasted Receipts'), + ('out', 'Forecasted Deliveries'), + ], string='State', readonly=True) + product_qty = fields.Float(string='Quantity', readonly=True) + move_ids = fields.One2many('stock.move', readonly=True) + company_id = fields.Many2one('res.company', readonly=True) + + def init(self): + tools.drop_view_if_exists(self._cr, 'report_stock_quantity') + query = """ +CREATE or REPLACE VIEW report_stock_quantity AS ( +SELECT + m.id, + product_id, + CASE + WHEN (ls.usage = 'internal' OR ls.usage = 'transit' AND ls.company_id IS NOT NULL) AND ld.usage != 'internal' THEN 'out' + WHEN ls.usage != 'internal' AND (ld.usage = 'internal' OR ld.usage = 'transit' AND ld.company_id IS NOT NULL) THEN 'in' + END AS state, + date_expected::date AS date, + CASE + WHEN (ls.usage = 'internal' OR ls.usage = 'transit' AND ls.company_id IS NOT NULL) AND ld.usage != 'internal' THEN -product_qty + WHEN ls.usage != 'internal' AND (ld.usage = 'internal' OR ld.usage = 'transit' AND ld.company_id IS NOT NULL) THEN product_qty + END AS product_qty, + m.company_id +FROM + stock_move m +LEFT JOIN stock_location ls on (ls.id=m.location_id) +LEFT JOIN stock_location ld on (ld.id=m.location_dest_id) +WHERE + product_qty != 0 AND ( + (ls.usage = 'internal' OR ls.usage = 'transit' AND ls.company_id IS NOT NULL) AND ld.usage != 'internal' OR + ls.usage != 'internal' AND (ld.usage = 'internal' OR ld.usage = 'transit' AND ld.company_id IS NOT NULL)) AND + m.state NOT IN ('cancel', 'draft', 'done') +UNION +SELECT + -MIN(q.id) as id, + product_id, + 'forecast' as state, + date.*::date, + SUM(quantity) as product_qty, + q.company_id +FROM + GENERATE_SERIES((now() at time zone 'utc')::date - interval '3month', + (now() at time zone 'utc')::date + interval '3 month', '1 day'::interval) date, + stock_quant q +LEFT JOIN stock_location l on (l.id=q.location_id) +WHERE + l.usage = 'internal' +GROUP BY + product_id, date, q.company_id +UNION +SELECT + m.id, + product_id, + 'forecast' as state, + GENERATE_SERIES( + CASE + WHEN state = 'done' THEN (now() at time zone 'utc')::date - interval '3month' + ELSE date_expected::date + END, + CASE + WHEN state != 'done' THEN (now() at time zone 'utc')::date + interval '3 month' + ELSE date::date - interval '1 day' + END, '1 day'::interval)::date date, + CASE + WHEN (ls.usage = 'internal' OR ls.usage = 'transit' AND ls.company_id IS NOT NULL) AND ld.usage != 'internal' AND state = 'done' THEN product_qty + WHEN ls.usage != 'internal' AND (ld.usage = 'internal' OR ld.usage = 'transit' AND ld.company_id IS NOT NULL) AND state = 'done' THEN -product_qty + WHEN (ls.usage = 'internal' OR ls.usage = 'transit' AND ls.company_id IS NOT NULL) AND ld.usage != 'internal' THEN -product_qty + WHEN ls.usage != 'internal' AND (ld.usage = 'internal' OR ld.usage = 'transit' AND ld.company_id IS NOT NULL) THEN product_qty + END AS product_qty, + m.company_id +FROM + stock_move m +LEFT JOIN stock_location ls on (ls.id=m.location_id) +LEFT JOIN stock_location ld on (ld.id=m.location_dest_id) +WHERE + product_qty != 0 AND ( + (ls.usage = 'internal' OR ls.usage = 'transit' AND ls.company_id IS NOT NULL) AND ld.usage != 'internal' OR + ls.usage != 'internal' AND (ld.usage = 'internal' OR ld.usage = 'transit' AND ld.company_id IS NOT NULL)) AND + m.state NOT IN ('cancel', 'draft') +); +""" + self.env.cr.execute(query) diff --git a/addons/stock/report/report_stock_quantity.xml b/addons/stock/report/report_stock_quantity.xml new file mode 100644 index 00000000000..a0987a1ca94 --- /dev/null +++ b/addons/stock/report/report_stock_quantity.xml @@ -0,0 +1,55 @@ + + + + stock_report_view_graph + report.stock.quantity + + + + + + + + + + + report.stock.quantity.search + report.stock.quantity + + + + + + + + + + + + + + + + + + + Forecasted Inventory + report.stock.quantity + graph + { + 'search_default_filter_forecast': 1, + 'graph_groupbys': ['date:day', 'state'], + } + + + + Forecasted Inventory + report.stock.quantity + graph + { + 'search_default_filter_forecast': 1, + 'graph_groupbys': ['date:day', 'state', 'product_id'], + } + + + diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv index b0eb20c8afc..599da9f91c9 100644 --- a/addons/stock/security/ir.model.access.csv +++ b/addons/stock/security/ir.model.access.csv @@ -70,3 +70,4 @@ access_product_attribute_value_manager,product.attribute manager value,product.m access_product_product_attribute_manager,product.product.attribute manager value,product.model_product_template_attribute_value,stock.group_stock_manager,1,1,1,1 access_product_template_attribute_exclusion_manager,product.attribute manager filter line,product.model_product_template_attribute_exclusion,stock.group_stock_manager,1,1,1,1 access_product_template_attribute_line_manager,product.attribute manager line,product.model_product_template_attribute_line,stock.group_stock_manager,1,1,1,1 +access_report_stock_quantity,access_report_stock_quantity,model_report_stock_quantity,base.group_user,1,0,0,0 diff --git a/addons/stock/security/stock_security.xml b/addons/stock/security/stock_security.xml index 0adb7c6db34..2ce2d742bdf 100644 --- a/addons/stock/security/stock_security.xml +++ b/addons/stock/security/stock_security.xml @@ -172,5 +172,12 @@ [('company_id', 'in', company_ids)] + + report_stock_quantity_flow multi-company + + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + diff --git a/addons/stock/views/product_views.xml b/addons/stock/views/product_views.xml index ae2c14b4714..660897759fc 100644 --- a/addons/stock/views/product_views.xml +++ b/addons/stock/views/product_views.xml @@ -224,6 +224,22 @@ On Hand + +