refactor(fusion_accounting): move security groups to _core, add multi-company session rule

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-04-19 00:14:36 -04:00
parent 10140a6968
commit 7ac01991e5
13 changed files with 237 additions and 151 deletions

View File

@@ -1,94 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Module Category -->
<record id="module_category_fusion_accounting" model="ir.module.category">
<field name="name">Fusion Accounting AI</field>
<field name="sequence">25</field>
</record>
<!-- Groups Privilege -->
<record id="res_groups_privilege_fusion_accounting" model="res.groups.privilege">
<field name="name">Fusion Accounting AI</field>
<field name="category_id" ref="module_category_fusion_accounting"/>
</record>
<!-- User Group (Staff) -->
<record id="group_fusion_accounting_user" model="res.groups">
<field name="name">User</field>
<field name="sequence">10</field>
<field name="implied_ids" eval="[(4, ref('account.group_account_user'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
</record>
<!-- Manager Group -->
<record id="group_fusion_accounting_manager" model="res.groups">
<field name="name">Manager</field>
<field name="sequence">20</field>
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_user'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
</record>
<!-- Admin Group -->
<record id="group_fusion_accounting_admin" model="res.groups">
<field name="name">Administrator</field>
<field name="sequence">30</field>
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_manager'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
</record>
<!-- Auto-assign: Accounting users get Fusion AI User, Advisers get Admin -->
<record id="account.group_account_user" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_user'))]"/>
</record>
<record id="account.group_account_manager" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_admin'))]"/>
</record>
<!-- Record Rules -->
<record id="rule_fusion_session_user" model="ir.rule">
<field name="name">Fusion Session: Own Sessions</field>
<field name="model_id" ref="model_fusion_accounting_session"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('group_fusion_accounting_user'))]"/>
</record>
<record id="rule_fusion_session_manager" model="ir.rule">
<field name="name">Fusion Session: All Sessions</field>
<field name="model_id" ref="model_fusion_accounting_session"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_fusion_accounting_manager'))]"/>
</record>
<record id="rule_fusion_history_user" model="ir.rule">
<field name="name">Fusion History: Own History</field>
<field name="model_id" ref="model_fusion_accounting_match_history"/>
<field name="domain_force">[('session_id.user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('group_fusion_accounting_user'))]"/>
</record>
<record id="rule_fusion_history_manager" model="ir.rule">
<field name="name">Fusion History: All History</field>
<field name="model_id" ref="model_fusion_accounting_match_history"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_fusion_accounting_manager'))]"/>
</record>
<!-- Multi-company rules -->
<record id="rule_fusion_tool_company" model="ir.rule">
<field name="name">Fusion Tool: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_tool"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="rule_fusion_rule_company" model="ir.rule">
<field name="name">Fusion Rule: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_rule"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="rule_fusion_history_company" model="ir.rule">
<field name="name">Fusion History: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_match_history"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
</odoo>

View File

@@ -30,6 +30,7 @@ Built by Nexa Systems Inc.
},
'data': [
'security/ir.model.access.csv',
'security/fusion_accounting_ai_security.xml',
'data/cron.xml',
'data/tool_definitions.xml',
'data/default_rules.xml',

View File

@@ -13,7 +13,7 @@ class FusionAccountingChatController(http.Controller):
"""S1-S3: Verify the current user owns the session."""
if session.user_id.id != request.env.user.id:
# Allow managers to access any session
if not request.env.user.has_group('fusion_accounting.group_fusion_accounting_manager'):
if not request.env.user.has_group('fusion_accounting_core.group_fusion_accounting_manager'):
return {'error': 'Access denied: you do not own this session'}
return None
@@ -55,7 +55,7 @@ class FusionAccountingChatController(http.Controller):
@http.route('/fusion_accounting/approve', type='jsonrpc', auth='user')
def approve_action(self, match_history_id, **kwargs):
if not request.env.user.has_group('fusion_accounting.group_fusion_accounting_manager'):
if not request.env.user.has_group('fusion_accounting_core.group_fusion_accounting_manager'):
return {'error': 'Insufficient permissions to approve actions'}
agent = request.env['fusion.accounting.agent']
result = agent.approve_action(int(match_history_id))
@@ -63,7 +63,7 @@ class FusionAccountingChatController(http.Controller):
@http.route('/fusion_accounting/reject', type='jsonrpc', auth='user')
def reject_action(self, match_history_id, reason='', **kwargs):
if not request.env.user.has_group('fusion_accounting.group_fusion_accounting_manager'):
if not request.env.user.has_group('fusion_accounting_core.group_fusion_accounting_manager'):
return {'error': 'Insufficient permissions to reject actions'}
agent = request.env['fusion.accounting.agent']
result = agent.reject_action(int(match_history_id), reason)
@@ -103,7 +103,7 @@ class FusionAccountingChatController(http.Controller):
@http.route('/fusion_accounting/approve_all', type='jsonrpc', auth='user')
def approve_all(self, match_history_ids, **kwargs):
if not request.env.user.has_group('fusion_accounting.group_fusion_accounting_manager'):
if not request.env.user.has_group('fusion_accounting_core.group_fusion_accounting_manager'):
return {'error': 'Insufficient permissions to approve actions'}
agent = request.env['fusion.accounting.agent']
results = []
@@ -119,7 +119,7 @@ class FusionAccountingChatController(http.Controller):
@http.route('/fusion_accounting/reject_all', type='jsonrpc', auth='user')
def reject_all(self, match_history_ids, reason='', **kwargs):
if not request.env.user.has_group('fusion_accounting.group_fusion_accounting_manager'):
if not request.env.user.has_group('fusion_accounting_core.group_fusion_accounting_manager'):
return {'error': 'Insufficient permissions to reject actions'}
agent = request.env['fusion.accounting.agent']
results = []

View File

@@ -25,7 +25,7 @@
<field name="domain">bank_reconciliation</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"statement_line_id": {"type": "integer", "description": "Bank statement line ID"}, "move_line_ids": {"type": "array", "items": {"type": "integer"}, "description": "Journal item IDs to match"}}, "required": ["statement_line_id", "move_line_ids"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_auto_reconcile_bank_lines" model="fusion.accounting.tool">
<field name="name">auto_reconcile_bank_lines</field>
@@ -34,7 +34,7 @@
<field name="domain">bank_reconciliation</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"company_id": {"type": "integer"}}}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_apply_reconcile_model" model="fusion.accounting.tool">
<field name="name">apply_reconcile_model</field>
@@ -43,7 +43,7 @@
<field name="domain">bank_reconciliation</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"model_id": {"type": "integer"}, "statement_line_id": {"type": "integer"}}, "required": ["model_id", "statement_line_id"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_unmatch_bank_line" model="fusion.accounting.tool">
<field name="name">unmatch_bank_line</field>
@@ -52,7 +52,7 @@
<field name="domain">bank_reconciliation</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"statement_line_id": {"type": "integer"}}, "required": ["statement_line_id"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_reconcile_suggestions" model="fusion.accounting.tool">
<field name="name">get_reconcile_suggestions</field>
@@ -119,7 +119,7 @@
<field name="domain">hst_management</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {}}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_validate_tax_return" model="fusion.accounting.tool">
<field name="name">validate_tax_return</field>
@@ -128,7 +128,7 @@
<field name="domain">hst_management</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"return_id": {"type": "integer"}}, "required": ["return_id"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<!-- Domain 3: Accounts Receivable -->
@@ -163,7 +163,7 @@
<field name="domain">accounts_receivable</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {"partner_id": {"type": "integer"}, "send_email": {"type": "boolean"}, "print_letter": {"type": "boolean"}, "email_subject": {"type": "string"}, "body": {"type": "string"}}, "required": ["partner_id"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_followup_report" model="fusion.accounting.tool">
<field name="name">get_followup_report</field>
@@ -180,7 +180,7 @@
<field name="domain">accounts_receivable</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"move_line_ids": {"type": "array", "items": {"type": "integer"}}}, "required": ["move_line_ids"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_unmatched_payments" model="fusion.accounting.tool">
<field name="name">get_unmatched_payments</field>
@@ -449,7 +449,7 @@
<field name="domain">adp</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"move_line_ids": {"type": "array", "items": {"type": "integer"}}}, "required": ["move_line_ids"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_verify_adp_split" model="fusion.accounting.tool">
<field name="name">verify_adp_split</field>
@@ -483,7 +483,7 @@
<field name="domain">adp</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"invoices": {"type": "array", "items": {"type": "object", "properties": {"invoice_number": {"type": "string"}, "amount": {"type": "number"}}, "required": ["invoice_number", "amount"]}, "description": "List of invoices with number and payment amount"}, "payment_date": {"type": "string", "description": "Payment date from remittance (YYYY-MM-DD)"}, "journal_id": {"type": "integer", "description": "Bank journal ID (default 50 = Scotia Current)"}}, "required": ["invoices", "payment_date"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<!-- Domain 10: Reporting -->
@@ -542,7 +542,7 @@
<field name="domain">reporting</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {"report_ref": {"type": "string"}, "format": {"type": "string", "enum": ["pdf", "xlsx"]}, "date_from": {"type": "string"}, "date_to": {"type": "string"}}, "required": ["report_ref"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_invoicing_summary" model="fusion.accounting.tool">
@@ -626,7 +626,7 @@
<field name="domain">audit</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {"move_id": {"type": "integer"}, "flag": {"type": "string"}, "recommendation": {"type": "string"}}, "required": ["move_id"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_audit_status" model="fusion.accounting.tool">
<field name="name">get_audit_status</field>
@@ -643,7 +643,7 @@
<field name="domain">audit</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {"status_id": {"type": "integer"}, "status": {"type": "string", "enum": ["todo", "reviewed", "supervised", "anomaly"]}}, "required": ["status_id", "status"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_audit_trail" model="fusion.accounting.tool">
<field name="name">get_audit_trail</field>
@@ -686,7 +686,7 @@
<field name="domain">payroll_management</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"journal_id": {"type": "integer"}, "date": {"type": "string"}, "ref": {"type": "string"}, "lines": {"type": "array", "items": {"type": "object", "properties": {"account_id": {"type": "integer"}, "name": {"type": "string"}, "debit": {"type": "number"}, "credit": {"type": "number"}, "partner_id": {"type": "integer"}}}}}, "required": ["journal_id", "date", "lines"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_match_payroll_cheques" model="fusion.accounting.tool">
<field name="name">match_payroll_cheques</field>
@@ -695,7 +695,7 @@
<field name="domain">payroll_management</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"statement_line_id": {"type": "integer"}, "move_line_ids": {"type": "array", "items": {"type": "integer"}}}, "required": ["statement_line_id", "move_line_ids"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_prepare_cra_payment" model="fusion.accounting.tool">
<field name="name">prepare_cra_payment</field>
@@ -704,7 +704,7 @@
<field name="domain">payroll_management</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"journal_id": {"type": "integer"}, "date": {"type": "string"}, "lines": {"type": "array"}}, "required": ["journal_id", "date", "lines"]}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_generate_t4" model="fusion.accounting.tool">
<field name="name">generate_t4</field>
@@ -713,7 +713,7 @@
<field name="domain">payroll_management</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {}}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_generate_roe" model="fusion.accounting.tool">
<field name="name">generate_roe</field>
@@ -722,7 +722,7 @@
<field name="domain">payroll_management</field>
<field name="tier">2</field>
<field name="parameters_schema">{"type": "object", "properties": {}}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_get_payroll_cost_report" model="fusion.accounting.tool">
<field name="name">get_payroll_cost_report</field>
@@ -823,7 +823,7 @@
<field name="domain">bank_reconciliation</field>
<field name="tier">3</field>
<field name="parameters_schema">{"type": "object", "properties": {"journal_id": {"type": "integer", "description": "Bank journal ID (default 50)"}, "line_ids": {"type": "array", "items": {"type": "integer"}, "description": "Optional: specific bank line IDs to reconcile. If empty, reconciles all matching payroll cheques."}}}</field>
<field name="required_groups">fusion_accounting.group_fusion_accounting_manager</field>
<field name="required_groups">fusion_accounting_core.group_fusion_accounting_manager</field>
</record>
<record id="tool_create_expense_entry" model="fusion.accounting.tool">

View File

@@ -61,7 +61,31 @@ AI_NAME_LIKE = (
)
# Group/category/privilege xml-ids that moved from 'fusion_accounting' to
# 'fusion_accounting_core' in Phase 0 (Task 16). Both _core and _ai
# post-migrations run this same UPDATE — whichever runs first wins, the other
# is a no-op. We reassign these here too so that if _ai happens to upgrade
# first (before _core's own post-migration has had a chance to run) the groups
# are still rehomed correctly.
CORE_SECURITY_NAMES = (
'module_category_fusion_accounting',
'res_groups_privilege_fusion_accounting',
'group_fusion_accounting_user',
'group_fusion_accounting_manager',
'group_fusion_accounting_admin',
)
def migrate(cr, version):
# Step 0: Reassign security groups/category/privilege to fusion_accounting_core.
cr.execute("""
UPDATE ir_model_data
SET module = 'fusion_accounting_core'
WHERE module = 'fusion_accounting'
AND name = ANY(%s)
""", (list(CORE_SECURITY_NAMES),))
moved_to_core = cr.rowcount
# Step 1: Delete orphan rows that conflict with an already-existing row in
# fusion_accounting_ai (data-load artifact). The new row is the survivor.
cr.execute("""
@@ -76,7 +100,7 @@ def migrate(cr, version):
""", (list(AI_MODEL_PREFIXES), list(AI_NAME_LIKE)))
deleted_conflicts = cr.rowcount
# Step 2: Reassign the non-conflicting orphans.
# Step 2: Reassign the non-conflicting orphans to fusion_accounting_ai.
cr.execute("""
UPDATE ir_model_data
SET module = 'fusion_accounting_ai'
@@ -86,12 +110,14 @@ def migrate(cr, version):
OR name LIKE ANY(%s)
)
""", (list(AI_MODEL_PREFIXES), list(AI_NAME_LIKE)))
moved = cr.rowcount
moved_to_ai = cr.rowcount
_logger.info(
"fusion_accounting_ai post-migration: deleted %d conflicting orphans, "
"reassigned %d ir_model_data rows from module='fusion_accounting' "
"to module='fusion_accounting_ai'",
"fusion_accounting_ai post-migration: reassigned %d security rows to "
"fusion_accounting_core, deleted %d conflicting AI orphans, reassigned "
"%d ir_model_data rows from module='fusion_accounting' to "
"module='fusion_accounting_ai'",
moved_to_core,
deleted_conflicts,
moved,
moved_to_ai,
)

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Per-user record rules (sessions visible only to the owning user; managers see all) -->
<record id="rule_fusion_session_user" model="ir.rule">
<field name="name">Fusion Session: Own Sessions</field>
<field name="model_id" ref="model_fusion_accounting_session"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('fusion_accounting_core.group_fusion_accounting_user'))]"/>
</record>
<record id="rule_fusion_session_manager" model="ir.rule">
<field name="name">Fusion Session: All Sessions</field>
<field name="model_id" ref="model_fusion_accounting_session"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('fusion_accounting_core.group_fusion_accounting_manager'))]"/>
</record>
<record id="rule_fusion_history_user" model="ir.rule">
<field name="name">Fusion History: Own History</field>
<field name="model_id" ref="model_fusion_accounting_match_history"/>
<field name="domain_force">[('session_id.user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('fusion_accounting_core.group_fusion_accounting_user'))]"/>
</record>
<record id="rule_fusion_history_manager" model="ir.rule">
<field name="name">Fusion History: All History</field>
<field name="model_id" ref="model_fusion_accounting_match_history"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('fusion_accounting_core.group_fusion_accounting_manager'))]"/>
</record>
<!-- Multi-company rules -->
<record id="rule_fusion_tool_company" model="ir.rule">
<field name="name">Fusion Tool: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_tool"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="rule_fusion_rule_company" model="ir.rule">
<field name="name">Fusion Rule: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_rule"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="rule_fusion_history_company" model="ir.rule">
<field name="name">Fusion History: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_match_history"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<!-- NEW (Phase 0): Multi-company rule on session itself
(per spec Section 4.2 + existing CLAUDE.md Known Issues) -->
<record id="rule_fusion_session_company" model="ir.rule">
<field name="name">Fusion Session: Multi-Company</field>
<field name="model_id" ref="model_fusion_accounting_session"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
</odoo>

View File

@@ -1,19 +1,19 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_fusion_session_user,fusion.accounting.session.user,model_fusion_accounting_session,group_fusion_accounting_user,1,1,1,0
access_fusion_session_admin,fusion.accounting.session.admin,model_fusion_accounting_session,group_fusion_accounting_admin,1,1,1,1
access_fusion_history_user,fusion.accounting.match.history.user,model_fusion_accounting_match_history,group_fusion_accounting_user,1,0,0,0
access_fusion_history_manager,fusion.accounting.match.history.manager,model_fusion_accounting_match_history,group_fusion_accounting_manager,1,1,1,0
access_fusion_history_admin,fusion.accounting.match.history.admin,model_fusion_accounting_match_history,group_fusion_accounting_admin,1,1,1,1
access_fusion_rule_user,fusion.accounting.rule.user,model_fusion_accounting_rule,group_fusion_accounting_user,1,0,0,0
access_fusion_rule_manager,fusion.accounting.rule.manager,model_fusion_accounting_rule,group_fusion_accounting_manager,1,1,1,0
access_fusion_rule_admin,fusion.accounting.rule.admin,model_fusion_accounting_rule,group_fusion_accounting_admin,1,1,1,1
access_fusion_tool_user,fusion.accounting.tool.user,model_fusion_accounting_tool,group_fusion_accounting_user,1,0,0,0
access_fusion_tool_admin,fusion.accounting.tool.admin,model_fusion_accounting_tool,group_fusion_accounting_admin,1,1,1,1
access_fusion_dashboard_user,fusion.accounting.dashboard.user,model_fusion_accounting_dashboard,group_fusion_accounting_user,1,1,1,1
access_fusion_rule_wizard_manager,fusion.accounting.rule.wizard.manager,model_fusion_accounting_rule_wizard,group_fusion_accounting_manager,1,1,1,1
access_fusion_recurring_pattern_user,fusion.recurring.pattern.user,model_fusion_recurring_pattern,group_fusion_accounting_user,1,0,0,0
access_fusion_recurring_pattern_manager,fusion.recurring.pattern.manager,model_fusion_recurring_pattern,group_fusion_accounting_manager,1,1,1,0
access_fusion_recurring_pattern_admin,fusion.recurring.pattern.admin,model_fusion_recurring_pattern,group_fusion_accounting_admin,1,1,1,1
access_fusion_vendor_profile_user,fusion.vendor.tax.profile.user,model_fusion_vendor_tax_profile,group_fusion_accounting_user,1,0,0,0
access_fusion_vendor_profile_manager,fusion.vendor.tax.profile.manager,model_fusion_vendor_tax_profile,group_fusion_accounting_manager,1,1,1,0
access_fusion_vendor_profile_admin,fusion.vendor.tax.profile.admin,model_fusion_vendor_tax_profile,group_fusion_accounting_admin,1,1,1,1
access_fusion_session_user,fusion.accounting.session.user,model_fusion_accounting_session,fusion_accounting_core.group_fusion_accounting_user,1,1,1,0
access_fusion_session_admin,fusion.accounting.session.admin,model_fusion_accounting_session,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
access_fusion_history_user,fusion.accounting.match.history.user,model_fusion_accounting_match_history,fusion_accounting_core.group_fusion_accounting_user,1,0,0,0
access_fusion_history_manager,fusion.accounting.match.history.manager,model_fusion_accounting_match_history,fusion_accounting_core.group_fusion_accounting_manager,1,1,1,0
access_fusion_history_admin,fusion.accounting.match.history.admin,model_fusion_accounting_match_history,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
access_fusion_rule_user,fusion.accounting.rule.user,model_fusion_accounting_rule,fusion_accounting_core.group_fusion_accounting_user,1,0,0,0
access_fusion_rule_manager,fusion.accounting.rule.manager,model_fusion_accounting_rule,fusion_accounting_core.group_fusion_accounting_manager,1,1,1,0
access_fusion_rule_admin,fusion.accounting.rule.admin,model_fusion_accounting_rule,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
access_fusion_tool_user,fusion.accounting.tool.user,model_fusion_accounting_tool,fusion_accounting_core.group_fusion_accounting_user,1,0,0,0
access_fusion_tool_admin,fusion.accounting.tool.admin,model_fusion_accounting_tool,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
access_fusion_dashboard_user,fusion.accounting.dashboard.user,model_fusion_accounting_dashboard,fusion_accounting_core.group_fusion_accounting_user,1,1,1,1
access_fusion_rule_wizard_manager,fusion.accounting.rule.wizard.manager,model_fusion_accounting_rule_wizard,fusion_accounting_core.group_fusion_accounting_manager,1,1,1,1
access_fusion_recurring_pattern_user,fusion.recurring.pattern.user,model_fusion_recurring_pattern,fusion_accounting_core.group_fusion_accounting_user,1,0,0,0
access_fusion_recurring_pattern_manager,fusion.recurring.pattern.manager,model_fusion_recurring_pattern,fusion_accounting_core.group_fusion_accounting_manager,1,1,1,0
access_fusion_recurring_pattern_admin,fusion.recurring.pattern.admin,model_fusion_recurring_pattern,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
access_fusion_vendor_profile_user,fusion.vendor.tax.profile.user,model_fusion_vendor_tax_profile,fusion_accounting_core.group_fusion_accounting_user,1,0,0,0
access_fusion_vendor_profile_manager,fusion.vendor.tax.profile.manager,model_fusion_vendor_tax_profile,fusion_accounting_core.group_fusion_accounting_manager,1,1,1,0
access_fusion_vendor_profile_admin,fusion.vendor.tax.profile.admin,model_fusion_vendor_tax_profile,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_fusion_session_user fusion.accounting.session.user model_fusion_accounting_session group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 1 1 0
3 access_fusion_session_admin fusion.accounting.session.admin model_fusion_accounting_session group_fusion_accounting_admin fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1
4 access_fusion_history_user fusion.accounting.match.history.user model_fusion_accounting_match_history group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 0 0 0
5 access_fusion_history_manager fusion.accounting.match.history.manager model_fusion_accounting_match_history group_fusion_accounting_manager fusion_accounting_core.group_fusion_accounting_manager 1 1 1 0
6 access_fusion_history_admin fusion.accounting.match.history.admin model_fusion_accounting_match_history group_fusion_accounting_admin fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1
7 access_fusion_rule_user fusion.accounting.rule.user model_fusion_accounting_rule group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 0 0 0
8 access_fusion_rule_manager fusion.accounting.rule.manager model_fusion_accounting_rule group_fusion_accounting_manager fusion_accounting_core.group_fusion_accounting_manager 1 1 1 0
9 access_fusion_rule_admin fusion.accounting.rule.admin model_fusion_accounting_rule group_fusion_accounting_admin fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1
10 access_fusion_tool_user fusion.accounting.tool.user model_fusion_accounting_tool group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 0 0 0
11 access_fusion_tool_admin fusion.accounting.tool.admin model_fusion_accounting_tool group_fusion_accounting_admin fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1
12 access_fusion_dashboard_user fusion.accounting.dashboard.user model_fusion_accounting_dashboard group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 1 1 1
13 access_fusion_rule_wizard_manager fusion.accounting.rule.wizard.manager model_fusion_accounting_rule_wizard group_fusion_accounting_manager fusion_accounting_core.group_fusion_accounting_manager 1 1 1 1
14 access_fusion_recurring_pattern_user fusion.recurring.pattern.user model_fusion_recurring_pattern group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 0 0 0
15 access_fusion_recurring_pattern_manager fusion.recurring.pattern.manager model_fusion_recurring_pattern group_fusion_accounting_manager fusion_accounting_core.group_fusion_accounting_manager 1 1 1 0
16 access_fusion_recurring_pattern_admin fusion.recurring.pattern.admin model_fusion_recurring_pattern group_fusion_accounting_admin fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1
17 access_fusion_vendor_profile_user fusion.vendor.tax.profile.user model_fusion_vendor_tax_profile group_fusion_accounting_user fusion_accounting_core.group_fusion_accounting_user 1 0 0 0
18 access_fusion_vendor_profile_manager fusion.vendor.tax.profile.manager model_fusion_vendor_tax_profile group_fusion_accounting_manager fusion_accounting_core.group_fusion_accounting_manager 1 1 1 0
19 access_fusion_vendor_profile_admin fusion.vendor.tax.profile.admin model_fusion_vendor_tax_profile group_fusion_accounting_admin fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1

View File

@@ -31,10 +31,10 @@
<header>
<button name="action_approve" string="Approve" type="object"
class="btn-primary" invisible="decision != 'pending'"
groups="fusion_accounting.group_fusion_accounting_manager"/>
groups="fusion_accounting_core.group_fusion_accounting_manager"/>
<button name="action_reject" string="Reject" type="object"
class="btn-danger" invisible="decision != 'pending'"
groups="fusion_accounting.group_fusion_accounting_manager"/>
groups="fusion_accounting_core.group_fusion_accounting_manager"/>
<field name="decision" widget="statusbar"
statusbar_visible="pending,approved,rejected,auto"/>
</header>

View File

@@ -5,7 +5,7 @@
name="Fusion AI"
parent="accountant.menu_accounting"
sequence="8"
groups="group_fusion_accounting_user"/>
groups="fusion_accounting_core.group_fusion_accounting_user"/>
<!-- Dashboard -->
<menuitem id="menu_fusion_dashboard"
@@ -34,7 +34,7 @@
parent="menu_fusion_accounting_root"
action="action_fusion_rule"
sequence="40"
groups="group_fusion_accounting_manager"/>
groups="fusion_accounting_core.group_fusion_accounting_manager"/>
<!-- Vendor Tax Profiles -->
<menuitem id="menu_fusion_vendor_profiles"
@@ -42,7 +42,7 @@
parent="menu_fusion_accounting_root"
action="action_vendor_tax_profiles"
sequence="50"
groups="group_fusion_accounting_manager"/>
groups="fusion_accounting_core.group_fusion_accounting_manager"/>
<!-- Recurring Patterns -->
<menuitem id="menu_fusion_recurring_patterns"
@@ -50,7 +50,7 @@
parent="menu_fusion_accounting_root"
action="action_recurring_patterns"
sequence="55"
groups="group_fusion_accounting_manager"/>
groups="fusion_accounting_core.group_fusion_accounting_manager"/>
<!-- Configuration (link to settings) -->
<menuitem id="menu_fusion_config"
@@ -58,5 +58,5 @@
parent="menu_fusion_accounting_root"
action="account.action_account_config"
sequence="90"
groups="group_fusion_accounting_admin"/>
groups="fusion_accounting_core.group_fusion_accounting_admin"/>
</odoo>

View File

@@ -27,10 +27,10 @@
<header>
<button name="action_demote" string="Demote to Needs Approval" type="object"
class="btn-warning" invisible="approval_tier != 'auto'"
groups="fusion_accounting.group_fusion_accounting_admin"/>
groups="fusion_accounting_core.group_fusion_accounting_admin"/>
<button name="action_rollback" string="Rollback to Previous Version" type="object"
class="btn-secondary" invisible="not parent_rule_id"
groups="fusion_accounting.group_fusion_accounting_admin"/>
groups="fusion_accounting_core.group_fusion_accounting_admin"/>
</header>
<sheet>
<div class="oe_title">

View File

@@ -24,6 +24,7 @@ Built by Nexa Systems Inc.
'maintainer': 'Nexa Systems Inc.',
'depends': ['account', 'mail'],
'data': [
'security/fusion_accounting_security.xml',
'security/ir.model.access.csv',
],
'installable': True,

View File

@@ -0,0 +1,48 @@
"""Reassign security group/category/privilege xml-ids from the old module name.
Pre-Phase-0, the three fusion security groups (user, manager, admin), the
module category and the privilege all lived in module='fusion_accounting'.
Post-Phase-0 (Task 16) they moved into module='fusion_accounting_core'.
Odoo loads the XML from the new location on upgrade, but the existing
ir_model_data rows still reference the old module. This script rewrites them.
Both fusion_accounting_core and fusion_accounting_ai ship an equivalent
UPDATE — whichever post-migration runs first wins the rehoming, the other
is a no-op. This redundancy protects the common case where the two modules
are upgraded in either order (as well as the case where only one is
installed in a given database).
Idempotent: running it a second time matches zero rows.
"""
import logging
_logger = logging.getLogger(__name__)
CORE_SECURITY_NAMES = (
'module_category_fusion_accounting',
'res_groups_privilege_fusion_accounting',
'group_fusion_accounting_user',
'group_fusion_accounting_manager',
'group_fusion_accounting_admin',
)
def migrate(cr, version):
cr.execute(
"""
UPDATE ir_model_data
SET module = 'fusion_accounting_core'
WHERE module = 'fusion_accounting'
AND name = ANY(%s)
""",
(list(CORE_SECURITY_NAMES),),
)
moved = cr.rowcount
_logger.info(
"fusion_accounting_core post-migration: reassigned %d security rows "
"from module='fusion_accounting' to module='fusion_accounting_core'",
moved,
)

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Module Category -->
<record id="module_category_fusion_accounting" model="ir.module.category">
<field name="name">Fusion Accounting</field>
<field name="sequence">25</field>
</record>
<!-- Groups Privilege -->
<record id="res_groups_privilege_fusion_accounting" model="res.groups.privilege">
<field name="name">Fusion Accounting</field>
<field name="category_id" ref="module_category_fusion_accounting"/>
</record>
<!-- User Group (Staff) -->
<record id="group_fusion_accounting_user" model="res.groups">
<field name="name">User</field>
<field name="sequence">10</field>
<field name="implied_ids" eval="[(4, ref('account.group_account_user'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
</record>
<!-- Manager Group -->
<record id="group_fusion_accounting_manager" model="res.groups">
<field name="name">Manager</field>
<field name="sequence">20</field>
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_user'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
</record>
<!-- Admin Group -->
<record id="group_fusion_accounting_admin" model="res.groups">
<field name="name">Administrator</field>
<field name="sequence">30</field>
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_manager'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
</record>
<!-- Auto-assign: Accounting users get Fusion User; Advisers get Admin -->
<record id="account.group_account_user" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_user'))]"/>
</record>
<record id="account.group_account_manager" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_admin'))]"/>
</record>
</odoo>