-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FIX] Preserve all translated fields on v16 migration
Currently, the upgrade process preserves translations only for Odoo CE+EE fields. However, databases usually have more modules (or even custom fields) and those translations get lost when upgrading to Odoo 16. With this script, all translated fields will inherit their translations in all languages. Since it is executed in the `pre` phase of `base`, it should be able to still find the `ir_translation` table. @moduon MT-7120
- Loading branch information
Showing
1 changed file
with
68 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"""Store ir.translation records in jsonb fields.""" | ||
|
||
import logging | ||
|
||
from psycopg2.errors import InvalidTableDefinition | ||
from psycopg2.extras import Json | ||
from psycopg2.sql import SQL, Identifier | ||
|
||
from odoo.upgrade import util | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
def migrate(cr, version): | ||
already_converted = set() | ||
missing_tables = set() | ||
missing_columns = set() | ||
cr.execute( | ||
""" | ||
SELECT name, res_id, src, jsonb_agg(lang) AS langs, jsonb_agg(value) AS values | ||
FROM ir_translation | ||
WHERE res_id != 0 AND type = 'model' AND state in ('translated', 'to_translate') | ||
GROUP BY name, res_id, src | ||
ORDER BY src IS NOT NULL | ||
""" | ||
) | ||
for name, res_id, src, langs, values in cr.fetchall(): | ||
# Build a dict with all translations | ||
all_values = dict(zip(langs, values), en_US=src) | ||
if not all_values["en_US"]: | ||
all_values["en_US"] = all_values[langs[0]] | ||
# Find the translation target table and column | ||
model, field = name.split(",") | ||
table = util.table_of_model(cr, model) | ||
# Skip early if the translation is no longer usable | ||
if not util.table_exists(cr, table): | ||
missing_tables.add(table) | ||
continue | ||
if not util.column_exists(cr, table, field): | ||
missing_columns.add("{}.{}".format(table, field)) | ||
continue | ||
# Make sure the field is translatable in JSONB format (noop if done already) | ||
if (model, field) not in already_converted: | ||
already_converted.add((model, field)) | ||
try: | ||
util.convert_field_to_translatable(cr, model, field) | ||
except InvalidTableDefinition: | ||
_logger.debug("Couldn't convert %s.%s to jsonb", table, field, exc_info=True) | ||
_logger.info("Skipping translation recovery for %s.%s", table, field) | ||
# Store updated translation | ||
cr.execute( | ||
SQL("UPDATE {table} SET {field} = %s WHERE id = %s").format( | ||
table=Identifier(table), | ||
field=Identifier(field), | ||
), | ||
(Json(all_values), res_id), | ||
) | ||
# Log missing tables and columns | ||
if missing_tables: | ||
_logger.warning( | ||
"Couldn't recover translation for these missing tables: %s", | ||
", ".join(sorted(missing_tables)), | ||
) | ||
if missing_columns: | ||
_logger.warning( | ||
"Couldn't recover translation for these missing columns: %s", | ||
", ".join(sorted(missing_columns)), | ||
) |