From 03f2ed895526f8058a2e4f45d08f23760c9e72a3 Mon Sep 17 00:00:00 2001 From: "Gaetan Vanden Bergh (gavb)" Date: Mon, 13 Nov 2023 14:41:09 +0100 Subject: [PATCH] [FIX] base: Module category can reference itself as parent Steps: - Update a module category to reference itself as parent OR - Update multiple module categories to create a circular dependency (A -> B -> A) Actual result: - Upgrade a module is impossible - Un/Install a module is impossible - Infinite loop: https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/models/ir_module.py#L900 Expected result: - Circular dependencies not possible - Module can be upgrade and un/install opw-3490162 closes odoo/odoo#149284 X-original-commit: 6932714200d158a21bf10b90b2f71fa5cae0b13f Signed-off-by: Raphael Collet --- odoo/addons/base/models/ir_module.py | 8 ++++++- odoo/addons/base/tests/__init__.py | 1 + .../base/tests/test_ir_module_category.py | 21 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 odoo/addons/base/tests/test_ir_module_category.py diff --git a/odoo/addons/base/models/ir_module.py b/odoo/addons/base/models/ir_module.py index 1320ed5df79..c9b69a1798a 100644 --- a/odoo/addons/base/models/ir_module.py +++ b/odoo/addons/base/models/ir_module.py @@ -26,7 +26,7 @@ import psycopg2 import odoo from odoo import api, fields, models, modules, tools, _ from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG -from odoo.exceptions import AccessDenied, UserError +from odoo.exceptions import AccessDenied, UserError, ValidationError from odoo.osv import expression from odoo.tools.parse_version import parse_version from odoo.tools.misc import topological_sort, get_flag @@ -98,6 +98,12 @@ class ModuleCategory(models.Model): for cat in self: cat.xml_id = xml_ids.get(cat.id, [''])[0] + @api.constrains('parent_id') + def _check_parent_not_circular(self): + if not self._check_recursion(): + raise ValidationError(_("Error ! You cannot create recursive categories.")) + + class MyFilterMessages(Transform): """ Custom docutils transform to remove `system message` for a document and diff --git a/odoo/addons/base/tests/__init__.py b/odoo/addons/base/tests/__init__.py index e57be3c2c5e..314c5e4e602 100644 --- a/odoo/addons/base/tests/__init__.py +++ b/odoo/addons/base/tests/__init__.py @@ -64,3 +64,4 @@ from . import test_profiler from . import test_pdf from . import test_neutralize from . import test_config_parameter +from . import test_ir_module_category diff --git a/odoo/addons/base/tests/test_ir_module_category.py b/odoo/addons/base/tests/test_ir_module_category.py new file mode 100644 index 00000000000..76b14e87c78 --- /dev/null +++ b/odoo/addons/base/tests/test_ir_module_category.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase + +class TestModuleCategory(TransactionCase): + + def test_parent_circular_dependencies(self): + Cats = self.env['ir.module.category'] + + def create(name, **kw): + return Cats.create(dict(kw, name=name)) + + category_a = create('A', parent_id=False) + category_b = create('B', parent_id=category_a.id) + category_c = create('C', parent_id=category_b.id) + + with self.assertRaises(ValidationError): + category_a.write({'parent_id': category_c.id}) + with self.assertRaises(ValidationError): + category_b.write({'parent_id': category_b.id})