"""Reassign ir_model_data ownership from fusion_accounting to fusion_accounting_ai. Pre-Phase-0, all fusion code lived in module='fusion_accounting'. Post-Phase-0, fusion_accounting is the meta-module and the AI code lives in 'fusion_accounting_ai'. Odoo loads the Python from the new location, but existing ir_model_data rows still record the old module name. This script rewrites them. Special case: if the data-load phase of this very upgrade already created a new row in module='fusion_accounting_ai' with the same `name` as an old orphan (because the orphan lived under the old module name when data-load looked for it, missed it, and re-created the record), the UPDATE below would violate the unique constraint on (module, name). For those conflicts we delete the old orphan — the newly-created row is the one that records and the runtime will actually use going forward. Idempotent: running it a second time does nothing because the WHERE clauses find no matches. """ import logging _logger = logging.getLogger(__name__) # Exact xml-id names (model_ prefix, one per fusion.* model) that belonged to # the AI module. Each corresponds to a auto-created # by Odoo when the model class loads. AI_MODEL_PREFIXES = ( 'model_fusion_accounting_session', 'model_fusion_accounting_match_history', 'model_fusion_accounting_rule', 'model_fusion_accounting_tool', 'model_fusion_accounting_dashboard', 'model_fusion_accounting_recurring_pattern', 'model_fusion_accounting_vendor_tax_profile', 'model_fusion_accounting_rule_wizard', ) # XML-id name patterns for views/data/security/wizard/etc. that belong to # the AI sub-module. These cover every xml-id the AI module declares in its # data files (cron.xml, default_rules.xml, tool_definitions.xml, views/*.xml, # wizards/*.xml, report/*.xml) plus the ACL entries in ir.model.access.csv. # # Patterns use SQL LIKE syntax; '%' matches anything. These are broad on # purpose: we want to catch every past and present xml-id declared by the AI # data files, including Odoo-auto-generated companions (e.g. ir.cron auto- # creates an ir.actions.server with xml-id '_ir_actions_server'). AI_NAME_LIKE = ( 'view_fusion_%', 'action_fusion_%', 'menu_fusion_%', 'fusion_tool_%', 'fusion_rule_%', 'cron_fusion_%', 'seq_fusion_%', 'access_fusion_%', 'rule_fusion_%', 'paperformat_fusion_%', 'report_fusion_%', 'audit_report_template', ) # 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(""" DELETE FROM ir_model_data AS old WHERE old.module = 'fusion_accounting' AND (old.name = ANY(%s) OR old.name LIKE ANY(%s)) AND EXISTS ( SELECT 1 FROM ir_model_data AS new WHERE new.module = 'fusion_accounting_ai' AND new.name = old.name ) """, (list(AI_MODEL_PREFIXES), list(AI_NAME_LIKE))) deleted_conflicts = cr.rowcount # Step 2: Reassign the non-conflicting orphans to fusion_accounting_ai. cr.execute(""" UPDATE ir_model_data SET module = 'fusion_accounting_ai' WHERE module = 'fusion_accounting' AND ( name = ANY(%s) OR name LIKE ANY(%s) ) """, (list(AI_MODEL_PREFIXES), list(AI_NAME_LIKE))) moved_to_ai = cr.rowcount _logger.info( "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_to_ai, )