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
132 lines
5.3 KiB
Python
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)
|