fix(fusion_clock): pre-migrate re-links orphaned config-param external ids
Settings saved via set_param() have no ir_model_data; the noupdate config XML then collides on UNIQUE(key) during -u. Pre-migrate links existing params to their XML external id (value-preserving) so upgrades are robust. Found on the Entech clone-verify; affects prod (35 params vs 32 xmlids). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
65
fusion_clock/migrations/19.0.5.0.0/pre-migrate.py
Normal file
65
fusion_clock/migrations/19.0.5.0.0/pre-migrate.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
#
|
||||
# Defensive pre-migration: re-link orphaned fusion_clock config-parameter
|
||||
# external IDs.
|
||||
#
|
||||
# Booleans/floats saved through the Settings UI go in via set_param(), which
|
||||
# creates the ir_config_parameter row WITHOUT an ir_model_data external id. If a
|
||||
# param later also appears in the noupdate data/ir_config_parameter_data.xml,
|
||||
# a plain `-u` can't match it by external id, treats it as new, and the INSERT
|
||||
# trips the UNIQUE(key) constraint -> "Failed to load registry".
|
||||
#
|
||||
# This runs BEFORE the data files load: for every config record in the XML whose
|
||||
# param already exists but whose external id is missing, we create the external
|
||||
# id pointing at the existing param. The noupdate load then matches + skips it,
|
||||
# so the existing (possibly customised) value is preserved.
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo.modules.module import get_module_path
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def migrate(cr, version):
|
||||
module_path = get_module_path('fusion_clock')
|
||||
if not module_path:
|
||||
return
|
||||
xml_path = os.path.join(module_path, 'data', 'ir_config_parameter_data.xml')
|
||||
if not os.path.exists(xml_path):
|
||||
return
|
||||
|
||||
tree = etree.parse(xml_path)
|
||||
fixed = 0
|
||||
for rec in tree.findall('.//record[@model="ir.config_parameter"]'):
|
||||
xmlid = rec.get('id')
|
||||
key_node = rec.find('./field[@name="key"]')
|
||||
if not xmlid or key_node is None or not (key_node.text or '').strip():
|
||||
continue
|
||||
key = key_node.text.strip()
|
||||
|
||||
cr.execute("SELECT id FROM ir_config_parameter WHERE key = %s", (key,))
|
||||
param = cr.fetchone()
|
||||
if not param:
|
||||
continue # not set yet -> the noupdate load will create it cleanly
|
||||
|
||||
cr.execute(
|
||||
"SELECT id FROM ir_model_data WHERE module = 'fusion_clock' AND name = %s",
|
||||
(xmlid,))
|
||||
if cr.fetchone():
|
||||
continue # already linked
|
||||
|
||||
cr.execute("""
|
||||
INSERT INTO ir_model_data (module, name, model, res_id, noupdate, create_date, write_date)
|
||||
VALUES ('fusion_clock', %s, 'ir.config_parameter', %s, true, now(), now())
|
||||
""", (xmlid, param[0]))
|
||||
fixed += 1
|
||||
|
||||
if fixed:
|
||||
_logger.info(
|
||||
"Fusion Clock: re-linked %s orphaned config-parameter external id(s).", fixed)
|
||||
Reference in New Issue
Block a user