Files
odoo_source/addons/stock/report/report_stock_quantity.py
T
Rémy Voet (ryv) 6ce3807918 [FIX] stock: improve performance of report.stock.quantity
Use UNION ALL instead of UNION to let PostgreSQL
filter out rows before sorting and avoid Unique check.
Note that it shouldn't change the result because each 3 SELECT
doesn't have domain intersection.

Improve performance the `_get_orderpoint_action` and the Forecasted
Inventory.

Performance Gain:
With a large Database (5K products, 120K stock move, 20K quants, etc):
To open the Forecasted Inventory:
Before: 10.06 sec +- 0.02
After:   3.98 sec +- 0.07

task-2390516

X-original-commit: 0a89bc231aaadee959deb6cda4aa09318b08f591
2021-01-22 15:49:11 +00:00

132 lines
5.3 KiB
Python

# -*- 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)
warehouse_id = fields.Many2one('stock.warehouse', readonly=True)
def init(self):
tools.drop_view_if_exists(self._cr, 'report_stock_quantity')
query = """
CREATE or REPLACE VIEW report_stock_quantity AS (
WITH forecast_qty AS (
SELECT
m.id,
m.product_id,
CASE
WHEN (whs.id IS NOT NULL AND whd.id IS NULL) OR ls.usage = 'transit' THEN 'out'
WHEN (whs.id IS NULL AND whd.id IS NOT NULL) OR ld.usage = 'transit' THEN 'in'
END AS state,
m.date::date AS date,
CASE
WHEN (whs.id IS NOT NULL AND whd.id IS NULL) OR ls.usage = 'transit' THEN -product_qty
WHEN (whs.id IS NULL AND whd.id IS NOT NULL) OR ld.usage = 'transit' THEN product_qty
END AS product_qty,
m.company_id,
CASE
WHEN (whs.id IS NOT NULL AND whd.id IS NULL) OR ls.usage = 'transit' THEN whs.id
WHEN (whs.id IS NULL AND whd.id IS NOT NULL) OR ld.usage = 'transit' THEN whd.id
END AS warehouse_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)
LEFT JOIN stock_warehouse whs ON ls.parent_path like concat('%/', whs.view_location_id, '/%')
LEFT JOIN stock_warehouse whd ON ld.parent_path like concat('%/', whd.view_location_id, '/%')
LEFT JOIN product_product pp on pp.id=m.product_id
LEFT JOIN product_template pt on pt.id=pp.product_tmpl_id
WHERE
pt.type = 'product' AND
product_qty != 0 AND
(whs.id IS NOT NULL OR whd.id IS NOT NULL) AND
(whs.id IS NULL OR whd.id IS NULL OR whs.id != whd.id) AND
m.state NOT IN ('cancel', 'draft', 'done')
UNION ALL
SELECT
-q.id as id,
q.product_id,
'forecast' as state,
date.*::date,
q.quantity as product_qty,
q.company_id,
wh.id as warehouse_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)
LEFT JOIN stock_warehouse wh ON l.parent_path like concat('%/', wh.view_location_id, '/%')
WHERE
(l.usage = 'internal' AND wh.id IS NOT NULL) OR
l.usage = 'transit'
UNION ALL
SELECT
m.id,
m.product_id,
'forecast' as state,
GENERATE_SERIES(
CASE
WHEN m.state = 'done' THEN (now() at time zone 'utc')::date - interval '3month'
ELSE m.date::date
END,
CASE
WHEN m.state != 'done' THEN (now() at time zone 'utc')::date + interval '3 month'
ELSE m.date::date - interval '1 day'
END, '1 day'::interval)::date date,
CASE
WHEN ((whs.id IS NOT NULL AND whd.id IS NULL) OR ls.usage = 'transit') AND m.state = 'done' THEN product_qty
WHEN ((whs.id IS NULL AND whd.id IS NOT NULL) OR ld.usage = 'transit') AND m.state = 'done' THEN -product_qty
WHEN (whs.id IS NOT NULL AND whd.id IS NULL) OR ls.usage = 'transit' THEN -product_qty
WHEN (whs.id IS NULL AND whd.id IS NOT NULL) OR ld.usage = 'transit' THEN product_qty
END AS product_qty,
m.company_id,
CASE
WHEN (whs.id IS NOT NULL AND whd.id IS NULL) OR ls.usage = 'transit' THEN whs.id
WHEN (whs.id IS NULL AND whd.id IS NOT NULL) OR ld.usage = 'transit' THEN whd.id
END AS warehouse_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)
LEFT JOIN stock_warehouse whs ON ls.parent_path like concat('%/', whs.view_location_id, '/%')
LEFT JOIN stock_warehouse whd ON ld.parent_path like concat('%/', whd.view_location_id, '/%')
LEFT JOIN product_product pp on pp.id=m.product_id
LEFT JOIN product_template pt on pt.id=pp.product_tmpl_id
WHERE
pt.type = 'product' AND
product_qty != 0 AND
(whs.id IS NOT NULL OR whd.id IS NOT NULL) AND
(whs.id IS NULL or whd.id IS NULL OR whs.id != whd.id) AND
m.state NOT IN ('cancel', 'draft')
) -- /forecast_qty
SELECT
MIN(id) as id,
product_id,
state,
date,
sum(product_qty) as product_qty,
company_id,
warehouse_id
FROM forecast_qty
GROUP BY product_id, state, date, company_id, warehouse_id
);
"""
self.env.cr.execute(query)