diff --git a/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py b/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py index 5564954a..9240b7c0 100644 --- a/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py +++ b/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py @@ -1,17 +1,15 @@ -"""Reassign security group/category/privilege xml-ids from the old module name. +"""Safety-net reassignment of security xml-ids to fusion_accounting_core. -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'. +The actual rename lives in pre-migration.py — it MUST run before data-load +to avoid creating duplicate res.groups records and hitting the (module, +name) unique constraint on ir_model_data. This post-migration is a +belt-and-suspenders no-op for the common case: if pre-migration already +ran, this UPDATE matches zero rows. -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). +It also catches a rare edge case: fusion_accounting_ai.post-migration.py +runs an identical UPDATE to cover cross-module upgrade ordering, so both +modules redundantly ensure the rows land in the right module regardless +of which upgrade runs first. Idempotent: running it a second time matches zero rows. """ @@ -21,7 +19,7 @@ import logging _logger = logging.getLogger(__name__) -CORE_SECURITY_NAMES = ( +SECURITY_NAMES = ( 'module_category_fusion_accounting', 'res_groups_privilege_fusion_accounting', 'group_fusion_accounting_user', @@ -38,11 +36,12 @@ def migrate(cr, version): WHERE module = 'fusion_accounting' AND name = ANY(%s) """, - (list(CORE_SECURITY_NAMES),), + (list(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'", + "from module='fusion_accounting' to module='fusion_accounting_core' " + "(usually zero; pre-migration already handled the rename)", moved, ) diff --git a/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py b/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py new file mode 100644 index 00000000..9970b7ae --- /dev/null +++ b/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py @@ -0,0 +1,62 @@ +"""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, + )