"""Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load. 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'. Running this rename in pre-migration (rather than post-migration) is essential: Odoo's XML data-load looks up records by (module, name) in ir_model_data. If the old row still has module='fusion_accounting' when data-load runs, Odoo will not find a match for 'fusion_accounting_core.group_fusion_accounting_user' and will CREATE a brand-new res.groups record plus a new ir_model_data row. That leaves the database with two groups per name: 1. The ORIGINAL group (still tagged module='fusion_accounting') that every existing user is linked to via res_groups_users_rel. 2. A FRESH empty group (newly tagged module='fusion_accounting_core'). The subsequent post-migration UPDATE...SET module='fusion_accounting_core' would then violate the (module, name) unique constraint on ir_model_data and the upgrade transaction would roll back. By renaming ir_model_data rows BEFORE data-load, Odoo finds the existing row (now tagged fusion_accounting_core.*), UPDATEs the res.groups record in place with the XML-defined values, and the user-group links are preserved untouched. Idempotent: running this a second time matches zero rows. """ import logging _logger = logging.getLogger(__name__) 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(SECURITY_NAMES),), ) moved = cr.rowcount _logger.info( "fusion_accounting_core pre-migration: renamed %d security rows " "from module='fusion_accounting' to module='fusion_accounting_core' " "before data-load (idempotent; non-zero only on first upgrade from " "pre-Phase-0)", moved, )