From 454c83a11f23b6ef85c3e9340db4e604c407fc6a Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 12 Dec 2025 17:47:30 +0100 Subject: [PATCH] [MIG] uom --- docsource/modules180-190.rst | 2 +- .../uom/19.0.1.0/noupdate_changes_work.xml | 187 ++++++++++++++++++ .../scripts/uom/19.0.1.0/post-migration.py | 61 ++++++ .../scripts/uom/19.0.1.0/pre-migration.py | 32 +++ .../uom/19.0.1.0/upgrade_analysis_work.txt | 70 +++++++ .../scripts/uom/tests/data_uom_migration.py | 51 +++++ .../scripts/uom/tests/test_uom_migration.py | 31 +++ 7 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 openupgrade_scripts/scripts/uom/19.0.1.0/noupdate_changes_work.xml create mode 100644 openupgrade_scripts/scripts/uom/19.0.1.0/post-migration.py create mode 100644 openupgrade_scripts/scripts/uom/19.0.1.0/pre-migration.py create mode 100644 openupgrade_scripts/scripts/uom/19.0.1.0/upgrade_analysis_work.txt create mode 100644 openupgrade_scripts/scripts/uom/tests/data_uom_migration.py create mode 100644 openupgrade_scripts/scripts/uom/tests/test_uom_migration.py diff --git a/docsource/modules180-190.rst b/docsource/modules180-190.rst index 8770a2716e5..21f7f26cda1 100644 --- a/docsource/modules180-190.rst +++ b/docsource/modules180-190.rst @@ -1110,7 +1110,7 @@ Module coverage 18.0 -> 19.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | transifex | |No DB layout changes. | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| uom | | | +| uom |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ | utm | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ diff --git a/openupgrade_scripts/scripts/uom/19.0.1.0/noupdate_changes_work.xml b/openupgrade_scripts/scripts/uom/19.0.1.0/noupdate_changes_work.xml new file mode 100644 index 00000000000..b05973ee1c6 --- /dev/null +++ b/openupgrade_scripts/scripts/uom/19.0.1.0/noupdate_changes_work.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + 1000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ton + + + + + + + + + + + + + diff --git a/openupgrade_scripts/scripts/uom/19.0.1.0/post-migration.py b/openupgrade_scripts/scripts/uom/19.0.1.0/post-migration.py new file mode 100644 index 00000000000..ac53ed54bc2 --- /dev/null +++ b/openupgrade_scripts/scripts/uom/19.0.1.0/post-migration.py @@ -0,0 +1,61 @@ +# Copyright 2025 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + + +def _uom_relative_uom_id_relative_factor(env): + """ + UOM categories have been obsoleted, UOMs are a tree now with the reference + unit as root and relative_factor giving the ratio to the UOM linked in + relative_uom_id + """ + + Uom = env["uom.uom"] + + env.cr.execute( + "SELECT res_id FROM ir_model_data WHERE " + "model='uom.uom' AND module != '__export__'" + ) + with_xml_id = Uom.browse(set(_id for (_id,) in env.cr.fetchall())) + + env.cr.execute( + f""" + SELECT + array_agg(id), + array_agg({openupgrade.get_legacy_name("factor")}), + array_agg({openupgrade.get_legacy_name("uom_type")}) + FROM + uom_uom + WHERE {openupgrade.get_legacy_name("category_id")} IS NOT NULL + GROUP BY {openupgrade.get_legacy_name("category_id")} + """ + ) + for ids, factors, uom_types in env.cr.fetchall(): + uoms = Uom.browse(ids) + uom2factor = dict(zip(uoms, factors, strict=True)) + uom2type = dict(zip(uoms, uom_types, strict=True)) + + old_reference = uoms.filtered( + lambda x, uom2type=uom2type: uom2type[x] == "reference" + ) + + for uom in uoms - old_reference - with_xml_id: + # uoms with xmlid will be updated by the migration of the module + # providing them + relative_factor = ( + uom2factor[uom] if uom2type[uom] == "bigger" else (1 / uom2factor[uom]) + ) + + uom.write( + { + "relative_uom_id": old_reference, + "relative_factor": relative_factor, + } + ) + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data(env, "uom", "19.0.1.0/noupdate_changes_work.xml") + _uom_relative_uom_id_relative_factor(env) diff --git a/openupgrade_scripts/scripts/uom/19.0.1.0/pre-migration.py b/openupgrade_scripts/scripts/uom/19.0.1.0/pre-migration.py new file mode 100644 index 00000000000..635d213272e --- /dev/null +++ b/openupgrade_scripts/scripts/uom/19.0.1.0/pre-migration.py @@ -0,0 +1,32 @@ +# Copyright 2025 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.copy_columns( + env.cr, + { + "uom_uom": [ + ("category_id", None, None), + ("factor", None, None), + ("rounding", None, None), + ("uom_type", None, None), + ] + }, + ) + openupgrade.rename_xmlids( + env.cr, + [ + ("product.decimal_product_uom", "uom.decimal_product_uom"), + ("uom.uom_square_foot", "uom.product_uom_square_foot"), + ("uom.uom_square_meter", "uom.product_uom_square_meter"), + ], + ) + # this would be cleaned up after the migration, but we can't have it during + # migration + openupgrade.delete_sql_constraint_safely( + env, "uom", "uom_uom", "factor_reference_is_one" + ) diff --git a/openupgrade_scripts/scripts/uom/19.0.1.0/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/uom/19.0.1.0/upgrade_analysis_work.txt new file mode 100644 index 00000000000..ea3c9aebe71 --- /dev/null +++ b/openupgrade_scripts/scripts/uom/19.0.1.0/upgrade_analysis_work.txt @@ -0,0 +1,70 @@ +---Models in module 'uom'--- +obsolete model uom.category +---Fields in module 'uom'--- +uom / uom.category / name (char) : DEL required +uom / uom.category / reference_uom_id (many2one) : DEL relation: uom.uom, stored: False +uom / uom.category / uom_ids (one2many) : DEL relation: uom.uom + +# NOTHING TO DO + +uom / uom.uom / _order : _order is now 'sequence, relative_uom_id, id' ('factor DESC, id') + +# NOTHING TO DO + +uom / uom.uom / category_id (many2one) : DEL relation: uom.category, required + +# NOTHING TO DO + +uom / uom.uom / factor (float) : now a function +uom / uom.uom / parent_path (char) : NEW +uom / uom.uom / related_uom_ids (one2many) : NEW relation: uom.uom +uom / uom.uom / relative_factor (float) : NEW required, hasdefault: default +uom / uom.uom / relative_uom_id (many2one) : NEW relation: uom.uom + +# DONE: compute relative_factor, relative_uom_id for custom UOMs in post-migration + +uom / uom.uom / rounding (float) : not stored anymore + +# DONE: copied to legacy column in pre-migration + +uom / uom.uom / rounding (float) : now a function +uom / uom.uom / sequence (integer) : NEW hasdefault: compute + +# NOTHING TO DO + +uom / uom.uom / uom_type (selection) : DEL required, selection_keys: ['bigger', 'reference', 'smaller'] + +# DONE: copied to legacy column in pre-migration + +---XML records in module 'uom'--- +NEW decimal.precision: uom.decimal_product_uom [renamed from product module] (noupdate) + +# DONE: renamed in pre-migration + +DEL ir.actions.act_window: uom.product_uom_categ_form_action +DEL ir.model.access: uom.access_uom_category_manager +DEL ir.model.access: uom.access_uom_category_user +ir.model.constraint: uom.constraint_uom_uom_factor_gt_zero (changed definition: is now 'CHECK (relative_factor!=0)' ('check(factor!=0)')) +DEL ir.model.constraint: uom.constraint_uom_uom_factor_reference_is_one +DEL ir.model.constraint: uom.constraint_uom_uom_rounding_gt_zero +DEL ir.ui.view: uom.product_uom_categ_form_view +DEL ir.ui.view: uom.product_uom_categ_tree_view +DEL ir.ui.view: uom.uom_categ_view_search +DEL uom.category: uom.product_uom_categ_energy (noupdate) +DEL uom.category: uom.product_uom_categ_kgm (noupdate) +DEL uom.category: uom.product_uom_categ_unit (noupdate) +DEL uom.category: uom.product_uom_categ_vol (noupdate) +DEL uom.category: uom.uom_categ_length (noupdate) +DEL uom.category: uom.uom_categ_surface (noupdate) +DEL uom.category: uom.uom_categ_wtime (noupdate) +NEW uom.uom: uom.product_uom_milliliter (noupdate) +NEW uom.uom: uom.product_uom_pack_6 (noupdate) + +# NOTHING TO DO + +NEW uom.uom: uom.product_uom_square_foot (noupdate) +NEW uom.uom: uom.product_uom_square_meter (noupdate) +DEL uom.uom: uom.uom_square_foot (noupdate) +DEL uom.uom: uom.uom_square_meter (noupdate) + +# DONE: renamed in pre-migration diff --git a/openupgrade_scripts/scripts/uom/tests/data_uom_migration.py b/openupgrade_scripts/scripts/uom/tests/data_uom_migration.py new file mode 100644 index 00000000000..2136c11c219 --- /dev/null +++ b/openupgrade_scripts/scripts/uom/tests/data_uom_migration.py @@ -0,0 +1,51 @@ +env = locals().get("env") +# decimeter +env["uom.uom"].create( + { + "name": "dm", + "category_id": env.ref("uom.uom_categ_length").id, + "factor": 10, + "uom_type": "smaller", + } +) +# megameter +env["uom.uom"].create( + { + "name": "Mm", + "category_id": env.ref("uom.uom_categ_length").id, + "factor": 1000000, + "uom_type": "bigger", + } +) +# entirely custom uom category +uom_category = env["uom.category"].create( + { + "name": "Electric current", + } +) +env["uom.uom"].create( + { + "name": "A", + "category_id": uom_category.id, + "factor": 1, + "uom_type": "reference", + } +) +env["uom.uom"].create( + { + "name": "mA", + "category_id": uom_category.id, + "factor": 1000, + "uom_type": "smaller", + } +) +env["uom.uom"].create( + { + "name": "MA", + "category_id": uom_category.id, + "factor": 1000000, + "uom_type": "bigger", + } +) + +env.cr.commit() diff --git a/openupgrade_scripts/scripts/uom/tests/test_uom_migration.py b/openupgrade_scripts/scripts/uom/tests/test_uom_migration.py new file mode 100644 index 00000000000..66faa649609 --- /dev/null +++ b/openupgrade_scripts/scripts/uom/tests/test_uom_migration.py @@ -0,0 +1,31 @@ +from odoo.tests import TransactionCase + +from odoo.addons.openupgrade_framework import openupgrade_test + + +@openupgrade_test +class TestUomMigration(TransactionCase): + def test_uoms(self): + """ + Test that decimeters and megameters compute correctly + """ + dm = self.env["uom.uom"].search([("name", "=", "dm")]) + Mm = self.env["uom.uom"].search([("name", "=", "Mm")]) + self.assertEqual( + dm._compute_quantity(1, Mm, round=False), + 0.0000001, + ) + self.assertEqual( + Mm._compute_quantity(1, dm), + 10000000, + ) + mA = self.env["uom.uom"].search([("name", "=", "mA")]) + MA = self.env["uom.uom"].search([("name", "=", "MA")]) + self.assertEqual( + mA._compute_quantity(1, MA, round=False), + 0.000000001, + ) + self.assertEqual( + MA._compute_quantity(1, mA), + 1000000000, + )