From 2ca449759868ef41c53e1798c866644c9cb85a4e Mon Sep 17 00:00:00 2001 From: David Date: Thu, 13 Jun 2024 17:42:19 +0200 Subject: [PATCH] [FIX] base: keep translations for null src while upgrade The method ``update_translatable_fields`` is inspired in the core ``_get_translation_upgrade_queries`` method, which had a bug that has been recently addressed in https://github.com/odoo/odoo/pull/168038 Also, we're avoiding removing any original ir_translation record so we can address any further issue easly without needing to redump the table from the former DB. For html/xml tranlatable fields we'll use ``openupgrade_160.migrate_translations_to_jsonb`` which calls to the proper core method in ``end-migration``. Ideally we'd like to do everything with that method, but it relies on having the models already in the registry, and that doesn't happen when we're in `base`. Rationale from Odoo's fix: Before Odoo 16.0, it is possible to have database column whose value is ``NULL`` but still has translations in the ir_translation table. And these translations can be displayed correctly in the UI. How to reproduce before Odoo 16.0: 1. Open the form view of a record with non-required translated field. E.g. product.template.sale_description 2. Create a new record without touching the translated field for test 3. Directly click the translation button and fill all translations 4. click save The column for the record's translated field has ``NULL`` value, and the ir_translation table has new translation records with ``NULL`` in the src column During upgrade, when the column value is converted to jsonb for the translated field by the ORM, it will still be ``NULL``. And in the upgrade script when update the value with all translations, the result will still be ``NULL``. E.g. ``NULL || '{"fr_FR": "french"}'::jsonb`` ``NULL || '{"en_US": "english", "fr_FR": "french"}'::jsonb`` In this commit, for the above corner case, we assume the src was empty string instead of NULL. In the above example, the result would be ``'{"en_US": ""}'::jsonb || '{"fr_FR": "french"}'::jsonb`` ``'{"en_US": ""}'::jsonb || '{"en_US": "english", "fr_FR": "french"}'::jsonb`` TT49615 --- .../scripts/base/16.0.1.3/end-migration.py | 44 +++++++++++++++++++ .../scripts/base/16.0.1.3/pre-migration.py | 12 +++-- 2 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 openupgrade_scripts/scripts/base/16.0.1.3/end-migration.py diff --git a/openupgrade_scripts/scripts/base/16.0.1.3/end-migration.py b/openupgrade_scripts/scripts/base/16.0.1.3/end-migration.py new file mode 100644 index 000000000000..3afb9085e12b --- /dev/null +++ b/openupgrade_scripts/scripts/base/16.0.1.3/end-migration.py @@ -0,0 +1,44 @@ +# Copyright 2024 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade, openupgrade_160 + +from odoo.tools import column_exists + + +def update_callable_translatable_fields(env): + """Use Odoo's core method to get complete translations of translated fields with + a callable method for translations (html_translate, xml_translate)""" + exclusions = [ + # ir.actions.* inherits the name and help columns from ir.actions.actions + ("ir.actions.act_window", "name"), + ("ir.actions.act_window", "help"), + ("ir.actions.act_url", "name"), + ("ir.actions.act_url", "help"), + ("ir.actions.server", "name"), + ("ir.actions.server", "help"), + ("ir.actions.client", "name"), + ("ir.actions.client", "help"), + ("ir.actions.report", "name"), + ("ir.actions.report", "help"), + ] + fields = env["ir.model.fields"].search_read( + [("translate", "=", True)], ["model", "name"] + ) + fields_spec = [ + (f["model"], f["name"]) + for f in fields + if ( + (f["model"], f["name"]) not in exclusions + and f["model"] in env + and column_exists(env.cr, env[f["model"]]._table, f["name"]) + and f["name"] in env[f["model"]]._fields + and callable(env[f["model"]]._fields[f["name"]].translate) + ) + ] + openupgrade_160.migrate_translations_to_jsonb(env, fields_spec) + + +@openupgrade.migrate() +def migrate(env, version): + update_callable_translatable_fields(env) diff --git a/openupgrade_scripts/scripts/base/16.0.1.3/pre-migration.py b/openupgrade_scripts/scripts/base/16.0.1.3/pre-migration.py index 2e431953f2a6..e87c95550458 100644 --- a/openupgrade_scripts/scripts/base/16.0.1.3/pre-migration.py +++ b/openupgrade_scripts/scripts/base/16.0.1.3/pre-migration.py @@ -86,6 +86,7 @@ def update_translatable_fields(cr): continue # borrowed from odoo/tools/translate.py#_get_translation_upgrade_queries translation_name = "%s,%s" % (model, field) + emtpy_src = """'{"en_US": ""}'::jsonb""" openupgrade.logged_query( cr, f""" @@ -98,8 +99,10 @@ def update_translatable_fields(cr): GROUP BY it.res_id ) UPDATE {table} m - SET "{field}" = CASE WHEN t.noupdate IS FALSE THEN t.value || m."{field}" - ELSE m."{field}" || t.value END + SET "{field}" = CASE + WHEN m."{field}" IS NULL THEN {emtpy_src} || t.value + WHEN t.noupdate IS FALSE THEN t.value || m."{field}" + ELSE m."{field}" || t.value END FROM t WHERE t.res_id = m.id """, @@ -108,11 +111,6 @@ def update_translatable_fields(cr): "name": translation_name, }, ) - openupgrade.logged_query( - cr, - "DELETE FROM ir_translation WHERE type = 'model' AND name = %s", - [translation_name], - ) @openupgrade.migrate(use_env=False)