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,
+ ))
+ )