From 5ea9dfee32324f49720bb8df1b36f49de0a549f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Mon, 27 Sep 2021 15:58:46 +0000 Subject: [PATCH] [IMP] stock: small improve performance of forecasted availability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid some useless read_group on stock move (fetch too much qty fields in product.product) and use a query object to avoid fetching data. To compute 11K of forecasted information of stock move (multi-warehouse ): - Before: 1.704 ± 0.046 sec and 174 SQL request - After: 1.554 ± 0.012 sec and 134 SQL request X-original-commit: 338edd616c2afe1d7a08a06a0b0278c3d7083c05 Part-of: odoo/odoo#77402 --- addons/stock/models/product.py | 12 ++++++++++++ addons/stock/models/stock_move.py | 7 ++++--- addons/stock/report/report_stock_forecasted.py | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index 19dddf1dcda..966519f8d63 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -602,6 +602,18 @@ class Product(models.Model): else: return self._get_rules_from_location(rule.location_src_id, seen_rules=seen_rules | rule) + def _get_only_qty_available(self): + """ Get only quantities available, it is equivalent to read qty_available + but avoid fetching other qty fields (avoid costly read group on moves) + + :rtype: defaultdict(float) + """ + domain_quant = expression.AND([self._get_domain_locations()[0], [('product_id', 'in', self.ids)]]) + quants_groupby = self.env['stock.quant'].read_group(domain_quant, ['product_id', 'quantity'], ['product_id'], orderby='id') + currents = defaultdict(float) + for c in quants_groupby: + currents[c['product_id'][0]] = c['quantity'] + return currents def _filter_to_unlink(self): domain = [('product_id', 'in', self.ids)] diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 53a37b1a164..fe9409e73fe 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -1931,10 +1931,10 @@ class StockMove(models.Model): ids_in_self = set(self.ids) product_ids = self.product_id - wh_location_ids = self.env['stock.location'].search([('id', 'child_of', warehouse.view_location_id.id)]).ids + wh_location_query = self.env['stock.location']._search([('id', 'child_of', warehouse.view_location_id.id)]) in_domain, out_domain = self.env['report.stock.report_product_product_replenishment']._move_confirmed_domain( - None, product_ids.ids, wh_location_ids + None, product_ids.ids, wh_location_query ) outs = self.env['stock.move'].search(out_domain, order='reservation_date, priority desc, date, id') reserved_outs = self.env['stock.move'].search( @@ -1944,7 +1944,8 @@ class StockMove(models.Model): # Prefetch data to avoid future request (outs - self).read(['product_id', 'product_uom', 'product_qty', 'state'], load=False) # remove self because data is already fetch ins.read(['product_id', 'product_qty', 'date', 'move_dest_ids'], load=False) - currents = {c['id']: c['qty_available'] for c in product_ids.with_context(warehouse=warehouse.id).read(['qty_available'])} + + currents = product_ids.with_context(warehouse=warehouse.id)._get_only_qty_available() outs_per_product = defaultdict(list) reserved_outs_per_product = defaultdict(list) diff --git a/addons/stock/report/report_stock_forecasted.py b/addons/stock/report/report_stock_forecasted.py index 143e7e3ea46..2dd4fa42737 100644 --- a/addons/stock/report/report_stock_forecasted.py +++ b/addons/stock/report/report_stock_forecasted.py @@ -176,7 +176,7 @@ class ReplenishmentReport(models.AbstractModel): 'move': in_, 'move_dests': in_._rollup_move_dests(set()) }) - currents = {c['id']: c['qty_available'] for c in outs.product_id.read(['qty_available'])} + currents = outs.product_id._get_only_qty_available() lines = [] for product in (ins | outs).product_id: