From b718dbd7443ac0ebbe5c34d5883d458d84001b41 Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Thu, 18 Jan 2024 14:29:42 +0000 Subject: [PATCH] [FIX] stock: create orderpoints faster This commit changes the computation of product having a negative forecasted quantity to create manual orderpoints. The issue was that each replenish location needed multiple `_read_group` on `stock.quant` and `stock.move` on all storable product. This commit makes only 3 `_read_group`s for all products x locations and post process the group and quantity sum by location in Python. This method gives some performance gain in time as well as in memory consumption closes odoo/odoo#156507 Task: 3653272 X-original-commit: 1a061791e68a524480b4f230031af25411603606 Signed-off-by: Arnold Moyaux (arm) Signed-off-by: William Henrotin (whe) --- addons/stock/models/stock_orderpoint.py | 44 ++++++++++++++++++++----- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/addons/stock/models/stock_orderpoint.py b/addons/stock/models/stock_orderpoint.py index fe0ffbbf235..6270723660b 100644 --- a/addons/stock/models/stock_orderpoint.py +++ b/addons/stock/models/stock_orderpoint.py @@ -349,15 +349,43 @@ class StockWarehouseOrderpoint(models.Model): all_replenish_location_ids = self._get_orderpoint_locations() ploc_per_day = defaultdict(set) # For each replenish location get products with negative virtual_available aka forecast + + + Move = self.env['stock.move'].with_context(active_test=False) + Quant = self.env['stock.quant'].with_context(active_test=False) + domain_quant, domain_move_in_loc, domain_move_out_loc = all_product_ids._get_domain_locations_new(all_replenish_location_ids.ids) + domain_state = [('state', 'in', ('waiting', 'confirmed', 'assigned', 'partially_available'))] + domain_product = [['product_id', 'in', all_product_ids.ids]] + + domain_quant = expression.AND([domain_product, domain_quant]) + domain_move_in = expression.AND([domain_product, domain_state, domain_move_in_loc]) + domain_move_out = expression.AND([domain_product, domain_state, domain_move_out_loc]) + + moves_in = defaultdict(list) + for item in Move._read_group(domain_move_in, ['product_id', 'location_dest_id'], ['product_qty:sum']): + moves_in[item[0]].append((item[1], item[2])) + + moves_out = defaultdict(list) + for item in Move._read_group(domain_move_out, ['product_id', 'location_id'], ['product_qty:sum']): + moves_out[item[0]].append((item[1], item[2])) + + quants = defaultdict(list) + for item in Quant._read_group(domain_quant, ['product_id', 'location_id'], ['quantity:sum']): + quants[item[0]].append((item[1], item[2])) + + rounding = {product.id: product.uom_id.rounding for product in all_product_ids} + path = {loc: loc.parent_path for loc in self.env['stock.location'].search([('id', 'child_of', all_replenish_location_ids.ids)])} for loc in all_replenish_location_ids: - for product in all_product_ids.with_context(location=loc.id): - if float_compare(product.virtual_available, 0, precision_rounding=product.uom_id.rounding) >= 0: - continue - # group product by lead_days and location in order to read virtual_available - # in batch - rules = product._get_rules_from_location(loc) - lead_days = rules.with_context(bypass_delay_description=True)._get_lead_days(product)[0]['total_delay'] - ploc_per_day[(lead_days, loc)].add(product.id) + for product in all_product_ids: + qty_available = sum(q[1] for q in quants.get(product, [(0, 0)]) if q[0] and path[q[0]] in loc.parent_path) + incoming_qty = sum(m[1] for m in moves_in.get(product, [(0, 0)]) if m[0] and path[m[0]] in loc.parent_path) + outgoing_qty = sum(m[1] for m in moves_out.get(product, [(0, 0)]) if m[0] and path[m[0]] in loc.parent_path) + if float_compare(qty_available + incoming_qty - outgoing_qty, 0, precision_rounding=rounding[product.id]) < 0: + # group product by lead_days and location in order to read virtual_available + # in batch + rules = product._get_rules_from_location(loc) + lead_days = rules.with_context(bypass_delay_description=True)._get_lead_days(product)[0]['total_delay'] + ploc_per_day[(lead_days, loc)].add(product.id) # recompute virtual_available with lead days today = fields.datetime.now().replace(hour=23, minute=59, second=59)