diff --git a/base_multi_company/README.rst b/base_multi_company/README.rst index ed6261a4f75..32b809f5af2 100644 --- a/base_multi_company/README.rst +++ b/base_multi_company/README.rst @@ -84,6 +84,7 @@ Contributors * Rodrigo Ferreira * Florian da Costa * Denis Roussel +* Jairo Llopis (`Moduon `__) Maintainers ~~~~~~~~~~~ diff --git a/base_multi_company/hooks.py b/base_multi_company/hooks.py index 7fd8ecfc96b..9277c2b0d47 100644 --- a/base_multi_company/hooks.py +++ b/base_multi_company/hooks.py @@ -1,6 +1,8 @@ # Copyright 2015-2016 Pedro M. Baeza # Copyright 2017 LasLabs Inc. # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html +import warnings + from odoo import SUPERUSER_ID, api __all__ = [ @@ -15,6 +17,10 @@ def set_security_rule(env, rule_ref): :param: env: Environment :param: rule_ref: XML-ID of the security rule to change. """ + warnings.warn( + "This hook is deprecated. Use `fill_company_ids` instead.", + DeprecationWarning, + ) rule = env.ref(rule_ref) if not rule: # safeguard if it's deleted return @@ -38,6 +44,12 @@ def post_init_hook(cr, rule_ref, model_name): """ env = api.Environment(cr, SUPERUSER_ID, {}) set_security_rule(env, rule_ref) + fill_company_ids(cr, model_name) + + +def fill_company_ids(cr, model_name): + """Fill company_ids with company_id values.""" + env = api.Environment(cr, SUPERUSER_ID, {}) # Copy company values model = env[model_name] table_name = model._fields["company_ids"].relation @@ -65,6 +77,7 @@ def uninstall_hook(cr, rule_ref): rule_ref (string): XML ID of security rule to remove the `domain_force` from. """ + warnings.warn("This hook is deprecated.", DeprecationWarning) env = api.Environment(cr, SUPERUSER_ID, {}) # Change access rule rule = env.ref(rule_ref) diff --git a/base_multi_company/models/multi_company_abstract.py b/base_multi_company/models/multi_company_abstract.py index a34906ef7c8..6c1c9196062 100644 --- a/base_multi_company/models/multi_company_abstract.py +++ b/base_multi_company/models/multi_company_abstract.py @@ -2,6 +2,8 @@ # Copyright 2023 Tecnativa - Pedro M. Baeza # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +import warnings + from odoo import api, fields, models @@ -49,7 +51,12 @@ def _inverse_company_id(self): record.company_ids = [(6, 0, record.company_id.ids)] def _search_company_id(self, operator, value): - return [("company_ids", operator, value)] + domain = [("company_ids", operator, value)] + new_op = {"in": "=", "not in": "!="}.get(operator) + if new_op and (False in value or None in value): + # We need to workaround an ORM issue to find records with no company + domain = ["|", ("company_ids", new_op, False)] + domain + return domain @api.model_create_multi def create(self, vals_list): @@ -67,6 +74,7 @@ def write(self, vals): @api.model def _patch_company_domain(self, args): + warnings.warn("This method is deprecated.", DeprecationWarning) # In some situations the 'in' operator is used with company_id in a # name_search or search_read. ORM does not convert to a proper WHERE clause when using # the 'in' operator. @@ -97,28 +105,3 @@ def _patch_company_domain(self, args): else: new_args.append(arg) return new_args - - @api.model - def _name_search( - self, name, args=None, operator="ilike", limit=100, name_get_uid=None - ): - new_args = self._patch_company_domain(args) - return super()._name_search( - name, - args=new_args, - operator=operator, - limit=limit, - name_get_uid=name_get_uid, - ) - - @api.model - def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None): - new_domain = self._patch_company_domain(domain) - return super().search_read(new_domain, fields, offset, limit, order) - - @api.model - def search(self, args, offset=0, limit=None, order=None, count=False): - args = self._patch_company_domain(args) - return super().search( - args, offset=offset, limit=limit, order=order, count=count - ) diff --git a/base_multi_company/readme/CONTRIBUTORS.rst b/base_multi_company/readme/CONTRIBUTORS.rst index 1f667b12056..49837c59f84 100644 --- a/base_multi_company/readme/CONTRIBUTORS.rst +++ b/base_multi_company/readme/CONTRIBUTORS.rst @@ -5,3 +5,4 @@ * Rodrigo Ferreira * Florian da Costa * Denis Roussel +* Jairo Llopis (`Moduon `__) diff --git a/base_multi_company/readme/DEVELOPER.rst b/base_multi_company/readme/DEVELOPER.rst index 36ce0d0d30d..697fa602a8d 100644 --- a/base_multi_company/readme/DEVELOPER.rst +++ b/base_multi_company/readme/DEVELOPER.rst @@ -4,13 +4,13 @@ Implementation Multi Company Abstract ---------------------- -The `multi.company.abstract` model is meant to be inherited by any model that +The ``multi.company.abstract`` model is meant to be inherited by any model that wants to implement multi-company functionality. The logic does not require a pre-existing company field on the inheriting model, but will not be affected if one does exist. -When inheriting the `multi.company.abstract` model, you must take care that -it is the first model listed in the `_inherit` array +When inheriting the ``multi.company.abstract`` model, you must take care that +it is the first model listed in the ``_inherit`` array .. code-block:: python @@ -19,53 +19,35 @@ it is the first model listed in the `_inherit` array _name = "product.template" _description = "Product Template (Multi-Company)" -The following fields are provided by `multi.company.abstract`: +The following fields are provided by ``multi.company.abstract``: -* `company_ids` - All of the companies that this record belongs to. This is a - special `res.company.assignment` view, which allows for the circumvention of +* ``company_ids`` - All of the companies that this record belongs to. This is a + special ``res.company.assignment`` view, which allows for the circumvention of standard cross-company security policies. These policies would normally restrict a user from seeing another company unless it is currently operating under that company. Be aware of apples to oranges issues when comparing the records from this field against actual company records. -* `company_id` - Passes through a singleton company based on the current user, +* ``company_id`` - Passes through a singleton company based on the current user, and the allowed companies for the record. -* `no_company_ids` - As there is a limitation in Odoo ORM to get real False values - in Many2many fields (solved on 2022-03-23 https://github.com/odoo/odoo/pull/81344). Hooks ----- -A generic `post_init_hook` and `uninstall_hook` is provided, which will alter -a pre-existing single-company security rule to be multi-company aware. +A generic ``fill_company_ids`` hook is provided, to be used in submodules' +``post_init_hook``, which will convert the ``company_id`` field to a +``company_ids`` field, respecting previous company assignments. -These hooks will unfortunately not work in every circumstance, but they cut out +It will unfortunately not work in every circumstance, but it cuts out significant boilerplate when relevant. .. code-block:: python - import logging - - _logger = logging.getLogger(__name__) - - try: - from odoo.addons.base_multi_company import hooks - except ImportError: - _logger.info('Cannot find `base_multi_company` module in addons path.') - + from odoo.addons.base_multi_company import hooks def post_init_hook(cr, registry): - hooks.post_init_hook( + hooks.fill_company_ids( cr, - 'product.product_comp_rule', 'product.template', ) - - def uninstall_hook(cr, registry): - hooks.uninstall_hook( - cr, - 'product.product_comp_rule', - ) - -A module implementing these hooks would need to first identify the proper rule -for the record (`product.product_comp_rule` in the above example). +Other hooks are deprecated and no longer needed. diff --git a/base_multi_company/static/description/index.html b/base_multi_company/static/description/index.html index f3f7d6eb99c..bdc1e0ab2e1 100644 --- a/base_multi_company/static/description/index.html +++ b/base_multi_company/static/description/index.html @@ -427,6 +427,7 @@

Contributors

  • Rodrigo Ferreira <rodrigosferreira91@gmail.com>
  • Florian da Costa <florian.dacosta@akretion.com>
  • Denis Roussel <denis.roussel@acsone.eu>
  • +
  • Jairo Llopis (Moduon)
  • diff --git a/base_multi_company/tests/test_multi_company_abstract.py b/base_multi_company/tests/test_multi_company_abstract.py index 83a55eb5339..608449e0fd4 100644 --- a/base_multi_company/tests/test_multi_company_abstract.py +++ b/base_multi_company/tests/test_multi_company_abstract.py @@ -4,6 +4,7 @@ from odoo_test_helper import FakeModelLoader +from odoo.fields import Command from odoo.tests import common @@ -287,3 +288,29 @@ def test_set_company_id(self): tester.company_id = False self.assertFalse(tester.sudo().company_ids) + + def test_rule_in_false(self): + # Create an ir.rule imitating base.res_partner_rule + self.env["ir.rule"].create( + { + "name": "Test rule", + "model_id": self.tester_model.id, + "domain_force": repr( + [("company_id", "in", [False, self.company_1.id])] + ), + "groups": [Command.link(self.ref("base.group_user"))], + } + ) + user = common.new_test_user( + self.env, + login="test_user", + groups="base.group_user", + company_id=self.company_1.id, + ) + # Record has no company, so user can read it + self.assertFalse(self.record_1.company_ids) + self.assertFalse(self.record_1.company_id) + self.assertEqual( + self.record_1.with_user(user).read(["name"]), + [{"id": self.record_1.id, "name": "test"}], + )