From e382b70c699d8a0ef0abc8f2a96c904b8abce9a2 Mon Sep 17 00:00:00 2001 From: Alvaro Fuentes Date: Fri, 18 Nov 2022 20:29:51 +0000 Subject: [PATCH] [FIX] stock: avoid MemError on `_get_orderpoint_action` When we have too many products from orderpoints we cannot compute the forecasts for all at once. Otherwise we may get a MemoryError. Here we iterate by chunks. This error was observed on upg-417045, where there were 381927 prodcuts. closes odoo/odoo#106196 X-original-commit: feea4e5f147b3b26cbc032daaf0ceabbe3417912 Signed-off-by: Arnold Moyaux (arm) Signed-off-by: Alvaro Fuentes Suarez (afu) --- addons/stock/models/stock_orderpoint.py | 31 ++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/addons/stock/models/stock_orderpoint.py b/addons/stock/models/stock_orderpoint.py index 8cda89cd2ae..be6559ba7ec 100644 --- a/addons/stock/models/stock_orderpoint.py +++ b/addons/stock/models/stock_orderpoint.py @@ -11,7 +11,7 @@ from odoo import SUPERUSER_ID, _, api, fields, models, registry from odoo.addons.stock.models.stock_rule import ProcurementException from odoo.exceptions import RedirectWarning, UserError, ValidationError from odoo.osv import expression -from odoo.tools import add, float_compare, frozendict, split_every +from odoo.tools import float_compare, frozendict, split_every _logger = logging.getLogger(__name__) @@ -335,24 +335,22 @@ class StockWarehouseOrderpoint(models.Model): orderpoints_removed = orderpoints._unlink_processed_orderpoints() orderpoints = orderpoints - orderpoints_removed to_refill = defaultdict(float) - all_product_ids = self._get_orderpoint_products() + all_product_ids = self._get_orderpoint_products().ids all_replenish_location_ids = self.env['stock.location'].search([('replenish_location', '=', True)]) ploc_per_day = defaultdict(set) - product_loc_by_qty = {} # For each replenish location get products with negative virtual_available aka forecast - for loc in all_replenish_location_ids: - quantities = all_product_ids.with_context(location=loc.id).mapped('virtual_available') - for product, quantity in zip(all_product_ids, quantities): - if float_compare(quantity, 0, precision_rounding=product.uom_id.rounding) >= 0: - continue - product_loc_by_qty[(product, loc)] = quantity - - # group product by lead_days and location in order to read virtual_available - # in batch - for (product, loc), qty in product_loc_by_qty.items(): - rules = product._get_rules_from_location(loc) - lead_days = rules.with_context(bypass_delay_description=True)._get_lead_days(product)[0] - ploc_per_day[(lead_days, loc)].add(product.id) + for products in map(self.env['product.product'].browse, split_every(5000, all_product_ids)): + for loc in all_replenish_location_ids: + quantities = products.with_context(location=loc.id).mapped('virtual_available') + for product, quantity in zip(products, quantities): + if float_compare(quantity, 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] + ploc_per_day[(lead_days, loc)].add(product.id) + products.invalidate_recordset() # recompute virtual_available with lead days today = fields.datetime.now().replace(hour=23, minute=59, second=59) @@ -365,6 +363,7 @@ class StockWarehouseOrderpoint(models.Model): for qty in qties: if float_compare(qty['virtual_available'], 0, precision_rounding=product.uom_id.rounding) < 0: to_refill[(qty['id'], loc.id)] = qty['virtual_available'] + products.invalidate_recordset() if not to_refill: return action