From ccc85fd269c493fc41182d88cbf04d8cc44a1b28 Mon Sep 17 00:00:00 2001 From: Charles Ferir Date: Tue, 5 Oct 2021 14:46:22 +0000 Subject: [PATCH] [FIX] stock: solve possible infinite recursion Steps to reproduce: - Test upgrade request 40290 Issue: Traceback raised. Cause: The _find_delivery_ids_by_lot function can raise a RecursionError: maximum recursion depth exceeded if there is a loop in the path of lots. When running the function recursively for lot_id contained in produce_line_ids the same lot can be reached multiple times. In the case of db 40290: move_380->move_381 move_381 and move_419 both in lot_1320 move_419->move_423 move_423 and move_380 both in lot_567 move_380->move_381 ...(goes on forever) Solution: Use a lot_path optionnal argument to test if a lot has already been visited. Full Traceback: ``` Traceback (most recent call last): File "/tmp/tmp81rzi7n7/migrations/testing.py", line 208, in test_check self.check(value) File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 85, in check self.assertFalse(diff, msg) AssertionError: [('stock.menu_action_production_lot_form', 195, 'Inventory > Master Data > Lots/Serial Numbers', 310), ('mrp.menu_mrp_traceability', 258, 'Manufacturing > Products > Lots/Serial Numbers', 310)] is not false : At least one menu or view working before upgrade is not working after upgrade. ('stock.menu_action_production_lot_form', 195, 'Inventory > Master Data > Lots/Serial Numbers', 310): Traceback (most recent call last): File "/home/odoo/src/odoo/15.0/odoo/api.py", line 879, in get return field_cache[record._ids[0]] KeyError: 567 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/odoo/src/odoo/15.0/odoo/fields.py", line 1057, in __get__ value = env.cache.get(record, self) File "/home/odoo/src/odoo/15.0/odoo/api.py", line 882, in get raise CacheMiss(record, field) odoo.exceptions.CacheMiss: 'stock.production.lot(567,).delivery_count' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 182, in crawl_menu self.mock_action(action_vals) File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 293, in mock_action mock_method(model, view, fields_list, domain, group_by) File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 319, in mock_view_form [data] = record.read(fields_list) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 3227, in read return self._read_format(fnames=fields, load=load) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 3247, in _read_format vals[name] = convert(record[name], record, use_name_get) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 5867, in __getitem__ return self._fields[key].__get__(self, type(self)) File "/home/odoo/src/odoo/15.0/odoo/fields.py", line 1106, in __get__ self.compute_value(recs) File "/home/odoo/src/odoo/15.0/odoo/fields.py", line 1265, in compute_value records._compute_field_value(self) File "/home/odoo/src/odoo/15.0/addons/mail/models/mail_thread.py", line 410, in _compute_field_value return super()._compute_field_value(field) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4249, in _compute_field_value getattr(self, field.compute)() File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 122, in _compute_delivery_ids delivery_ids_by_lot = self._find_delivery_ids_by_lot() File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 204, in _find_delivery_ids_by_lot for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 204, in _find_delivery_ids_by_lot for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 204, in _find_delivery_ids_by_lot for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): [Previous line repeated 926 more times] File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 197, in _find_delivery_ids_by_lot move_lines = self.env['stock.move.line'].search(domain) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 1810, in search res = self._search(args, offset=offset, limit=limit, order=order, count=count) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4682, in _search query = self._where_calc(args) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4438, in _where_calc return expression.expression(domain, self).query File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 442, in __init__ self.parse() File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 701, in parse right_ids = comodel.with_context(active_test=False)._search([(path[1], operator, right)], order='id') File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4683, in _search self._apply_ir_rules(query, 'read') File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4464, in _apply_ir_rules expression.expression(domain, self.sudo(), self._table, query) File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 442, in __init__ self.parse() File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 701, in parse right_ids = comodel.with_context(active_test=False)._search([(path[1], operator, right)], order='id') File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4682, in _search query = self._where_calc(args) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4438, in _where_calc return expression.expression(domain, self).query File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 431, in __init__ self._unaccent = get_unaccent_wrapper(model._cr) File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 405, in get_unaccent_wrapper if odoo.registry(cr.dbname).has_unaccent: File "/home/odoo/src/odoo/15.0/odoo/__init__.py", line 104, in registry return modules.registry.Registry(database_name) File "/home/odoo/src/odoo/15.0/odoo/modules/registry.py", line 61, in __new__ return cls.registries[db_name] File "/home/odoo/src/odoo/15.0/odoo/tools/func.py", line 71, in wrapper return func(self, *args, **kwargs) RecursionError: maximum recursion depth exceeded ('mrp.menu_mrp_traceability', 258, 'Manufacturing > Products > Lots/Serial Numbers', 310): Traceback (most recent call last): File "/home/odoo/src/odoo/15.0/odoo/api.py", line 879, in get return field_cache[record._ids[0]] KeyError: 567 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/odoo/src/odoo/15.0/odoo/fields.py", line 1057, in __get__ value = env.cache.get(record, self) File "/home/odoo/src/odoo/15.0/odoo/api.py", line 882, in get raise CacheMiss(record, field) odoo.exceptions.CacheMiss: 'stock.production.lot(567,).delivery_count' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 182, in crawl_menu self.mock_action(action_vals) File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 293, in mock_action mock_method(model, view, fields_list, domain, group_by) File "/tmp/tmp81rzi7n7/migrations/base/tests/test_mock_crawl.py", line 319, in mock_view_form [data] = record.read(fields_list) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 3227, in read return self._read_format(fnames=fields, load=load) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 3247, in _read_format vals[name] = convert(record[name], record, use_name_get) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 5867, in __getitem__ return self._fields[key].__get__(self, type(self)) File "/home/odoo/src/odoo/15.0/odoo/fields.py", line 1106, in __get__ self.compute_value(recs) File "/home/odoo/src/odoo/15.0/odoo/fields.py", line 1265, in compute_value records._compute_field_value(self) File "/home/odoo/src/odoo/15.0/addons/mail/models/mail_thread.py", line 410, in _compute_field_value return super()._compute_field_value(field) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4249, in _compute_field_value getattr(self, field.compute)() File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 122, in _compute_delivery_ids delivery_ids_by_lot = self._find_delivery_ids_by_lot() File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 204, in _find_delivery_ids_by_lot for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 204, in _find_delivery_ids_by_lot for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 204, in _find_delivery_ids_by_lot for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): [Previous line repeated 926 more times] File "/home/odoo/src/odoo/15.0/addons/stock/models/stock_production_lot.py", line 197, in _find_delivery_ids_by_lot move_lines = self.env['stock.move.line'].search(domain) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 1810, in search res = self._search(args, offset=offset, limit=limit, order=order, count=count) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4682, in _search query = self._where_calc(args) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4438, in _where_calc return expression.expression(domain, self).query File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 442, in __init__ self.parse() File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 701, in parse right_ids = comodel.with_context(active_test=False)._search([(path[1], operator, right)], order='id') File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4683, in _search self._apply_ir_rules(query, 'read') File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4464, in _apply_ir_rules expression.expression(domain, self.sudo(), self._table, query) File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 442, in __init__ self.parse() File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 701, in parse right_ids = comodel.with_context(active_test=False)._search([(path[1], operator, right)], order='id') File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4682, in _search query = self._where_calc(args) File "/home/odoo/src/odoo/15.0/odoo/models.py", line 4438, in _where_calc return expression.expression(domain, self).query File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 431, in __init__ self._unaccent = get_unaccent_wrapper(model._cr) File "/home/odoo/src/odoo/15.0/odoo/osv/expression.py", line 405, in get_unaccent_wrapper if odoo.registry(cr.dbname).has_unaccent: File "/home/odoo/src/odoo/15.0/odoo/__init__.py", line 104, in registry return modules.registry.Registry(database_name) File "/home/odoo/src/odoo/15.0/odoo/modules/registry.py", line 61, in __new__ return cls.registries[db_name] File "/home/odoo/src/odoo/15.0/odoo/tools/func.py", line 71, in wrapper return func(self, *args, **kwargs) RecursionError: maximum recursion depth exceeded ``` closes odoo/odoo#80606 X-original-commit: acceb903b0487987edd5da9ce343c0f6c3ff44d8 Signed-off-by: William Henrotin (whe) --- addons/stock/models/stock_production_lot.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/addons/stock/models/stock_production_lot.py b/addons/stock/models/stock_production_lot.py index 870d061f0fe..78d897957ed 100644 --- a/addons/stock/models/stock_production_lot.py +++ b/addons/stock/models/stock_production_lot.py @@ -188,7 +188,10 @@ class ProductionLot(models.Model): }) return action - def _find_delivery_ids_by_lot(self): + def _find_delivery_ids_by_lot(self, lot_path=None): + if lot_path is None: + lot_path = set() + domain = [ ('lot_id', 'in', self.ids), ('state', '=', 'done'), @@ -198,10 +201,14 @@ class ProductionLot(models.Model): delivery_by_lot = dict() for lot in self: delivery_ids = set() + if lot.id in lot_path: + continue for line in move_lines.filtered(lambda ml: ml.lot_id.id == lot.id): if line.produce_line_ids: - # Do the same process for lot_id contained in produce_line_ids, to fetch the end product deliveries - for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot().values(): + # Do the same process for lot_id contained in produce_line_ids, + # to fetch the end product deliveries + lot_path.add(lot.id) + for delivery_ids_set in line.produce_line_ids.lot_id._find_delivery_ids_by_lot(lot_path=lot_path).values(): delivery_ids.update(delivery_ids_set) else: delivery_ids.add(line.picking_id.id)