diff --git a/addons/stock/report/report_stock_forecast.py b/addons/stock/report/report_stock_forecast.py
index b7f7712024f..1fbfe364d61 100644
--- a/addons/stock/report/report_stock_forecast.py
+++ b/addons/stock/report/report_stock_forecast.py
@@ -14,102 +14,176 @@ class ReportStockForecat(models.Model):
cumulative_quantity = fields.Float(string='Cumulative Quantity', readonly=True)
quantity = fields.Float(readonly=True)
company_id = fields.Many2one('res.company', string='Company', readonly=True)
+ reference = fields.Char('Reference')
@api.model_cr
def init(self):
tools.drop_view_if_exists(self._cr, 'report_stock_forecast')
self._cr.execute("""CREATE or REPLACE VIEW report_stock_forecast AS (SELECT
- MIN(id) as id,
- product_id as product_id,
- date as date,
- sum(product_qty) AS quantity,
- sum(sum(product_qty)) OVER (PARTITION BY product_id ORDER BY date) AS cumulative_quantity,
- company_id
+ row_number() OVER (ORDER BY final.date,final.id) AS id,
+ product_id as product_id,
+ date(final.date) as date,
+ sum(product_qty) AS quantity,
+ sum(sum(product_qty)) OVER (PARTITION BY final.product_id, final.company_id ORDER BY final.date) AS cumulative_quantity,
+ reference,
+ final.company_id
FROM
- (SELECT
- MIN(id) as id,
- MAIN.product_id as product_id,
- SUB.date as date,
- CASE WHEN MAIN.date = SUB.date THEN sum(MAIN.product_qty) ELSE 0 END as product_qty,
- MAIN.company_id as company_id
- FROM
- (SELECT
- MIN(sq.id) as id,
- sq.product_id,
- date_trunc('week', to_date(to_char(CURRENT_DATE, 'YYYY/MM/DD'), 'YYYY/MM/DD')) as date,
- SUM(sq.quantity) AS product_qty,
- sq.company_id
+ (SELECT
+ MIN(id) as id,
+ MAIN.product_id as product_id,
+ date(SUB.date) as date,
+ CASE WHEN MAIN.date = SUB.date THEN sum(MAIN.product_qty) ELSE 0 END as product_qty,
+ MAIN.reference as reference,
+ MAIN.company_id as company_id
FROM
- stock_quant as sq
+ (SELECT
+ MIN(-product.id) as id,
+ product.id as product_id,
+ date(CURRENT_DATE) as date,
+ SUM(sq.quantity) AS product_qty,
+ 'Starting Inventory' as reference,
+ sq.company_id
+ FROM
+ product_product as product
+ LEFT JOIN
+ stock_quant sq ON product.id = sq.product_id
+ LEFT JOIN
+ stock_location location_id ON sq.location_id = location_id.id
+ WHERE
+ location_id.usage = 'internal'
+ GROUP BY
+ date, product.id, sq.company_id, sq.quantity
+ UNION ALL
+ SELECT
+ MIN(sm.id) as id,
+ sm.product_id,
+ CASE WHEN sm.date_expected > CURRENT_DATE
+ THEN date(sm.date_expected)
+ ELSE date(CURRENT_DATE) END
+ AS date,
+ SUM(sm.product_qty) AS product_qty,
+ sm.reference as reference,
+ sm.company_id
+ FROM
+ stock_move as sm
+ LEFT JOIN
+ product_product ON product_product.id = sm.product_id
+ LEFT JOIN
+ stock_location dest_location ON sm.location_dest_id = dest_location.id
+ LEFT JOIN
+ stock_location source_location ON sm.location_id = source_location.id
+ WHERE
+ sm.state IN ('confirmed','partially_available','assigned','waiting') and
+ source_location.usage != 'internal' and dest_location.usage = 'internal'
+ GROUP BY
+ sm.date_expected,sm.product_id, sm.company_id, sm.reference
+ UNION ALL
+ SELECT
+ MIN(sm.id) as id,
+ sm.product_id,
+ CASE WHEN sm.date_expected > CURRENT_DATE
+ THEN date(sm.date_expected)
+ ELSE date(CURRENT_DATE) END
+ AS date,
+ SUM(-(sm.product_qty)) AS product_qty,
+ sm.reference AS reference,
+ sm.company_id
+ FROM
+ stock_move as sm
+ LEFT JOIN
+ product_product ON product_product.id = sm.product_id
+ LEFT JOIN
+ stock_location source_location ON sm.location_id = source_location.id
+ LEFT JOIN
+ stock_location dest_location ON sm.location_dest_id = dest_location.id
+ WHERE
+ sm.state IN ('confirmed','partially_available','assigned','waiting') and
+ source_location.usage = 'internal' and dest_location.usage != 'internal'
+ GROUP BY
+ sm.date_expected,sm.product_id, sm.company_id, sm.reference
+ ) AS MAIN
LEFT JOIN
- product_product ON product_product.id = sq.product_id
- LEFT JOIN
- stock_location location_id ON sq.location_id = location_id.id
- WHERE
- location_id.usage = 'internal'
- GROUP BY date, sq.product_id, sq.company_id
- UNION ALL
- SELECT
- MIN(-sm.id) as id,
- sm.product_id,
- CASE WHEN sm.date_expected > CURRENT_DATE
- THEN date_trunc('week', to_date(to_char(sm.date_expected, 'YYYY/MM/DD'), 'YYYY/MM/DD'))
- ELSE date_trunc('week', to_date(to_char(CURRENT_DATE, 'YYYY/MM/DD'), 'YYYY/MM/DD')) END
- AS date,
- SUM(sm.product_qty) AS product_qty,
- sm.company_id
- FROM
- stock_move as sm
- LEFT JOIN
- product_product ON product_product.id = sm.product_id
- LEFT JOIN
- stock_location dest_location ON sm.location_dest_id = dest_location.id
- LEFT JOIN
- stock_location source_location ON sm.location_id = source_location.id
- WHERE
- sm.state IN ('confirmed','partially_available','assigned','waiting') and
- source_location.usage != 'internal' and dest_location.usage = 'internal'
- GROUP BY sm.date_expected,sm.product_id, sm.company_id
- UNION ALL
- SELECT
- MIN(-sm.id) as id,
- sm.product_id,
- CASE WHEN sm.date_expected > CURRENT_DATE
- THEN date_trunc('week', to_date(to_char(sm.date_expected, 'YYYY/MM/DD'), 'YYYY/MM/DD'))
- ELSE date_trunc('week', to_date(to_char(CURRENT_DATE, 'YYYY/MM/DD'), 'YYYY/MM/DD')) END
- AS date,
- SUM(-(sm.product_qty)) AS product_qty,
- sm.company_id
- FROM
- stock_move as sm
- LEFT JOIN
- product_product ON product_product.id = sm.product_id
- LEFT JOIN
- stock_location source_location ON sm.location_id = source_location.id
- LEFT JOIN
- stock_location dest_location ON sm.location_dest_id = dest_location.id
- WHERE
- sm.state IN ('confirmed','partially_available','assigned','waiting') and
- source_location.usage = 'internal' and dest_location.usage != 'internal'
- GROUP BY sm.date_expected,sm.product_id, sm.company_id)
- as MAIN
- LEFT JOIN
- (SELECT DISTINCT date
- FROM
- (
- SELECT date_trunc('week', CURRENT_DATE) AS DATE
- UNION ALL
- SELECT date_trunc('week', to_date(to_char(sm.date_expected, 'YYYY/MM/DD'), 'YYYY/MM/DD')) AS date
- FROM stock_move sm
- LEFT JOIN
- stock_location source_location ON sm.location_id = source_location.id
- LEFT JOIN
- stock_location dest_location ON sm.location_dest_id = dest_location.id
- WHERE
- sm.state IN ('confirmed','assigned','waiting') and sm.date_expected > CURRENT_DATE and
- ((dest_location.usage = 'internal' AND source_location.usage != 'internal')
- or (source_location.usage = 'internal' AND dest_location.usage != 'internal'))) AS DATE_SEARCH)
- SUB ON (SUB.date IS NOT NULL)
- GROUP BY MAIN.product_id,SUB.date, MAIN.date, MAIN.company_id
- ) AS FINAL
- GROUP BY product_id,date,company_id)""")
+ (SELECT
+ DISTINCT date
+ FROM
+ (SELECT
+ date_trunc('day', CURRENT_DATE) AS DATE
+ UNION ALL
+ SELECT
+ date(sm.date_expected) AS date
+ FROM
+ stock_move sm
+ LEFT JOIN
+ stock_location source_location ON sm.location_id = source_location.id
+ LEFT JOIN
+ stock_location dest_location ON sm.location_dest_id = dest_location.id
+ WHERE
+ sm.state IN ('confirmed','assigned','waiting') AND
+ sm.date_expected > CURRENT_DATE AND
+ ((dest_location.usage = 'internal' AND
+ source_location.usage != 'internal') OR
+ (source_location.usage = 'internal' AND
+ dest_location.usage != 'internal'))
+ ) AS DATE_SEARCH
+ ) AS SUB ON SUB.date IS NOT NULL
+ GROUP BY
+ MAIN.product_id,SUB.date, MAIN.date, MAIN.company_id,MAIN.reference
+ ) AS FINAL
+ LEFT JOIN
+ stock_picking sp ON final.reference=sp.name
+ WHERE
+ final.product_qty != 0 OR final.reference = 'Starting Inventory'
+ GROUP BY
+ product_id,final.date,final.id,final.company_id,final.reference,sp.priority,sp.date,sp.id)""")
+
+ @api.model
+ def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
+ """ Override read_group in order to display the correct cumulative
+ quantity. The cumulative quantity for a group of moves/quants
+ should be the last for the selected time interval (given by domain).
+ However select the 'last' record's value in a groupby is not possible
+ (only possible aggregation functions are the ones provided by
+ PostgreSQL). The cumulative quantity is recomputed since a search on
+ each group returned by the super would consume too much time. The
+ cumulative quantity is computed based on previous group.
+ - Group1: sum of the quantity of records in group1.
+ - Group2: cumulative_quantity of group1 + sum of quantity of records in group2
+ - Group3: cumulative_quantity of group2 + sum of quantity of records in group3
+ ...
+ Since read_group depends on domain, some records could be excluded and
+ the computed cumulative quantity could be different than the one computed
+ in SQL. It's wanted since the customer could play with the domain in
+ order to analyze different quantities in different situations.
+ """
+ # Sort result by date_expected and id.
+ orderby = 'date, id' if not orderby else orderby
+ if 'cumulative_quantity' in fields and 'quantity' not in fields:
+ fields.append('quantity')
+ res = super(ReportStockForecat, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
+ total_cumulative_quantity = 0
+ cumulative_quantity_by_products = {}
+ if 'cumulative_quantity' in fields:
+ for index, line in enumerate(res):
+ # If the stock forecast cell have a 0 quantity the read_group
+ # will not return their sum as 0 but False.
+ if 'quantity' in line and not line['quantity']:
+ line['quantity'] = 0.0
+ if line.get('product_id'):
+ product_id = line['product_id']
+ if not cumulative_quantity_by_products.get(product_id):
+ cumulative_quantity_by_products[product_id] = 0
+ cumulative_quantity_by_products[product_id] += line['quantity'] # Get cumulative quantity product wise
+ line['cumulative_quantity'] = cumulative_quantity_by_products[product_id] # Sum of all quantities (i.e. cumulative quantity) product wise
+ elif line.get('reference'):
+ if line['reference'] == 'Starting Inventory':
+ if index == 0:
+ line['cumulative_quantity'] = line['quantity']
+ else:
+ line['quantity'] = 0.0
+ line['cumulative_quantity'] = res[index - 1]['cumulative_quantity'] # Set cumulatiove quantity if line does not have any cumulative
+ elif line.get('quantity', False):
+ total_cumulative_quantity += line['quantity']
+ line['cumulative_quantity'] = total_cumulative_quantity # Sum of all Move Operations' quantity
+ else:
+ line['cumulative_quantity'] = 0
+ return res
diff --git a/addons/stock/report/report_stock_forecast.xml b/addons/stock/report/report_stock_forecast.xml
index 8045b108369..e25e00acd50 100644
--- a/addons/stock/report/report_stock_forecast.xml
+++ b/addons/stock/report/report_stock_forecast.xml
@@ -7,19 +7,10 @@
+
-
-
-
-
-
- Stock.forecast.graph
- report.stock.forecast
-
-
-
-
+
@@ -32,8 +23,10 @@
-
-
+
+
+
+
@@ -43,9 +36,9 @@
Stock Level Forecastreport.stock.forecastform
- graph,pivot
+ pivot
- {'search_default_product_tmpl_id': active_id, 'group_by': ['company_id']}
+ {'search_default_product_tmpl_id': active_id, 'search_default_inventory_by_week':1}
@@ -53,9 +46,25 @@
Stock Level Forecastreport.stock.forecastform
- graph,pivot
+ pivot
- {'search_default_product_id': active_id, 'group_by': ['company_id']}
+ {'search_default_product_id': active_id, 'search_default_inventory_by_week':1}
+
+
+
+
+ Forecasted Inventory
+ report.stock.forecast
+ form
+ pivot
+
+ [('product_id.stock_move_ids.state', 'not in', ('draft', 'done', 'cancel'))]
+
+