diff --git a/fusion_accounting_core/__init__.py b/fusion_accounting_core/__init__.py index 0650744f..92e4dfd7 100644 --- a/fusion_accounting_core/__init__.py +++ b/fusion_accounting_core/__init__.py @@ -1 +1,6 @@ from . import models + + +def post_init_hook(env): + """Initialize coexistence group membership based on current Enterprise install state.""" + env['res.users']._fusion_recompute_coexistence_group() diff --git a/fusion_accounting_core/__manifest__.py b/fusion_accounting_core/__manifest__.py index ad3aa1f3..89e2572f 100644 --- a/fusion_accounting_core/__manifest__.py +++ b/fusion_accounting_core/__manifest__.py @@ -1,6 +1,6 @@ { 'name': 'Fusion Accounting Core', - 'version': '19.0.1.0.1', + 'version': '19.0.1.0.2', 'category': 'Accounting/Accounting', 'sequence': 24, 'summary': 'Shared base for the Fusion Accounting sub-module suite (security, shared schema, runtime helpers).', @@ -30,4 +30,5 @@ Built by Nexa Systems Inc. 'installable': True, 'application': False, 'license': 'OPL-1', + 'post_init_hook': 'post_init_hook', } diff --git a/fusion_accounting_core/models/__init__.py b/fusion_accounting_core/models/__init__.py index 503920da..6e7ee09f 100644 --- a/fusion_accounting_core/models/__init__.py +++ b/fusion_accounting_core/models/__init__.py @@ -1,4 +1,5 @@ from . import ir_module_module +from . import res_users from . import account_move from . import account_reconcile_model from . import account_bank_statement_line diff --git a/fusion_accounting_core/models/ir_module_module.py b/fusion_accounting_core/models/ir_module_module.py index 27e02076..50d0949b 100644 --- a/fusion_accounting_core/models/ir_module_module.py +++ b/fusion_accounting_core/models/ir_module_module.py @@ -30,3 +30,26 @@ class IrModuleModule(models.Model): ('name', '=', module_name), ('state', '=', 'installed'), ])) + + def button_immediate_install(self): + """Recompute the coexistence group after install state changes.""" + result = super().button_immediate_install() + self.env['res.users']._fusion_recompute_coexistence_group() + return result + + def button_immediate_uninstall(self): + """Recompute the coexistence group after uninstall state changes. + + The MRO chains into fusion_accounting_migration's override (which runs + the safety guard before calling super); we recompute only after the + whole chain completes. + """ + result = super().button_immediate_uninstall() + self.env['res.users']._fusion_recompute_coexistence_group() + return result + + def module_uninstall(self): + """Recompute the coexistence group after the lower-level uninstall.""" + result = super().module_uninstall() + self.env['res.users']._fusion_recompute_coexistence_group() + return result diff --git a/fusion_accounting_core/models/res_users.py b/fusion_accounting_core/models/res_users.py new file mode 100644 index 00000000..a865efc3 --- /dev/null +++ b/fusion_accounting_core/models/res_users.py @@ -0,0 +1,27 @@ +"""Coexistence group membership recomputation.""" + +from odoo import api, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + @api.model + def _fusion_recompute_coexistence_group(self): + """Set group membership = all internal users iff Enterprise absent. + + Called from ir.module.module.button_immediate_install / uninstall + overrides. Idempotent; safe to call multiple times. + """ + group = self.env.ref( + 'fusion_accounting_core.group_fusion_show_when_enterprise_absent', + raise_if_not_found=False, + ) + if not group: + return + enterprise_installed = self.env['ir.module.module']._fusion_is_enterprise_accounting_installed() + if enterprise_installed: + group.sudo().write({'user_ids': [(5, 0, 0)]}) + else: + all_internal = self.sudo().search([('share', '=', False)]) + group.sudo().write({'user_ids': [(6, 0, all_internal.ids)]}) diff --git a/fusion_accounting_core/security/fusion_accounting_security.xml b/fusion_accounting_core/security/fusion_accounting_security.xml index 59953d9c..b56b358e 100644 --- a/fusion_accounting_core/security/fusion_accounting_security.xml +++ b/fusion_accounting_core/security/fusion_accounting_security.xml @@ -43,4 +43,10 @@ + + + + Fusion: Show menus when Enterprise absent + Computed group. Membership: all internal users when no Enterprise accounting module is installed. Used to hide fusion sub-module menus that would conflict with Enterprise UIs. + diff --git a/fusion_accounting_core/tests/__init__.py b/fusion_accounting_core/tests/__init__.py index 832786bc..de61dd27 100644 --- a/fusion_accounting_core/tests/__init__.py +++ b/fusion_accounting_core/tests/__init__.py @@ -1,3 +1,4 @@ from . import test_enterprise_detection from . import test_shared_field_ownership from . import test_shared_field_bank_statement +from . import test_coexistence_group diff --git a/fusion_accounting_core/tests/test_coexistence_group.py b/fusion_accounting_core/tests/test_coexistence_group.py new file mode 100644 index 00000000..36d2f16c --- /dev/null +++ b/fusion_accounting_core/tests/test_coexistence_group.py @@ -0,0 +1,46 @@ +from odoo.tests.common import TransactionCase, tagged + + +@tagged('post_install', '-at_install') +class TestCoexistenceGroup(TransactionCase): + """The 'show when Enterprise absent' group must exist and have computed membership.""" + + def test_group_exists(self): + group = self.env.ref( + 'fusion_accounting_core.group_fusion_show_when_enterprise_absent', + raise_if_not_found=False, + ) + self.assertTrue(group, "Coexistence group must exist") + + def test_membership_matches_enterprise_state(self): + """A user is in the group iff Enterprise accounting is NOT installed. + + We can't toggle Enterprise mid-test, so just assert the current state + matches: if Enterprise is installed, group should have 0 members; if + not, the group should include all internal users. + """ + group = self.env.ref( + 'fusion_accounting_core.group_fusion_show_when_enterprise_absent' + ) + enterprise_installed = self.env['ir.module.module']._fusion_is_enterprise_accounting_installed() + all_internal = self.env['res.users'].sudo().search([('share', '=', False)]) + if enterprise_installed: + self.assertEqual( + len(group.user_ids), 0, + "Enterprise installed -> coexistence group should be empty", + ) + else: + self.assertEqual( + set(group.user_ids.ids), set(all_internal.ids), + "Enterprise absent -> coexistence group should contain all internal users", + ) + + def test_recompute_method_exists(self): + """The recompute helper must be callable on res.users.""" + self.assertTrue( + callable(getattr( + self.env['res.users'], + '_fusion_recompute_coexistence_group', + None, + )) + )