diff --git a/openupgrade_scripts/scripts/sale_management/19.0.1.0/end-migration.py b/openupgrade_scripts/scripts/sale_management/19.0.1.0/end-migration.py new file mode 100644 index 000000000000..ae1c87a048f0 --- /dev/null +++ b/openupgrade_scripts/scripts/sale_management/19.0.1.0/end-migration.py @@ -0,0 +1,56 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import json + +from openupgradelib import openupgrade + +from odoo.tools.translate import get_translation + + +def translate_section_name(env): + """ + After translations have been loaded, translate the section name for optional + products + """ + translated_section_name = { + "en_US": "Optional Products Section", + } + translated_section_name.update( + { + lang.code: get_translation( + "sale_management", lang.code, translated_section_name["en_US"], [] + ) + for lang in env["res.lang"].search([]) + } + ) + for lang, name in translated_section_name.items(): + openupgrade.logged_query( + env.cr, + """ + UPDATE sale_order_line + SET name=%s + FROM sale_order, res_users, res_partner + WHERE + sale_order_line.order_id=sale_order.id + AND sale_order.create_uid=res_users.id + AND res_users.partner_id=res_partner.id + AND display_type='line_section' + AND is_optional=True + AND COALESCE(res_partner.lang, 'en_US')=%s + """, + (name, lang), + ) + openupgrade.logged_query( + env.cr, + """ + UPDATE sale_order_template_line + SET name=%s + WHERE display_type='line_section' AND is_optional=True + """, + (json.dumps(translated_section_name),), + ) + + +@openupgrade.migrate() +def migrate(env, version): + translate_section_name(env) diff --git a/openupgrade_scripts/scripts/sale_management/19.0.1.0/post-migration.py b/openupgrade_scripts/scripts/sale_management/19.0.1.0/post-migration.py index 127207e5e6ec..9c3411fd2ec2 100644 --- a/openupgrade_scripts/scripts/sale_management/19.0.1.0/post-migration.py +++ b/openupgrade_scripts/scripts/sale_management/19.0.1.0/post-migration.py @@ -1,5 +1,7 @@ # Copyright 2026 Hunki Enterprises BV # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import json + from openupgradelib import openupgrade @@ -8,20 +10,26 @@ def sale_order_line_options(env): Model sale.order.option has been replaced by optional lines on sale orders """ SaleOrderLine = env["sale.order.line"] - link_column = openupgrade.get_legacy_name("option_id") - env.cr.execute( - f"ALTER TABLE sale_order_line ADD COLUMN IF NOT EXISTS {link_column} int " - ) env.cr.execute( """ SELECT - id, order_id, name, product_id, quantity, price_unit, sequence, uom_id, discount + soo.order_id, name, product_id, quantity, price_unit, sequence, uom_id, + discount, line_id, max_sequence FROM - sale_order_option + sale_order_option soo + LEFT JOIN + ( + SELECT order_id, MAX(sequence) max_sequence + FROM sale_order_line + GROUP BY order_id + ) order2max_sequence + ON order2max_sequence.order_id=soo.order_id + ORDER BY + order_id """ ) + last_order_id = 0 for ( - option_id, order_id, name, product_id, @@ -30,27 +38,36 @@ def sale_order_line_options(env): sequence, uom_id, discount, + line_id, + max_sequence, ) in env.cr.fetchall(): - line = SaleOrderLine.create( - { - "is_optional": True, - "order_id": order_id, - "name": name, - "product_id": product_id, - "product_uom_qty": quantity, - "price_unit": price_unit, - "sequence": sequence, - "product_uom_id": uom_id, - "discount": discount, - } - ) - env.cr.execute( - f""" - UPDATE sale_order_line - SET {link_column}={option_id} - WHERE id={line.id} - """ - ) + if order_id != last_order_id: + SaleOrderLine.create( + { + "is_optional": True, + "order_id": order_id, + "name": "/", + "display_type": "line_section", + "sequence": (max_sequence or 0) + 1, + } + ) + sequence = (max_sequence or 0) + 2 + (sequence or 0) + if line_id: + SaleOrderLine.browse(line_id).sequence = sequence + else: + SaleOrderLine.create( + { + "order_id": order_id, + "name": name, + "product_id": product_id, + "product_uom_qty": quantity, + "price_unit": price_unit, + "sequence": sequence, + "product_uom_id": uom_id, + "discount": discount, + } + ) + last_order_id = order_id def sale_order_template_options(env): @@ -66,11 +83,23 @@ def sale_order_template_options(env): env.cr.execute( """ SELECT - id, sale_order_template_id, name, product_id, quantity, uom_id + id, soto.sale_order_template_id, name, product_id, quantity, uom_id, + max_sequence FROM - sale_order_template_option + sale_order_template_option soto + LEFT JOIN + ( + SELECT sale_order_template_id, max(sequence) max_sequence + FROM sale_order_template_line + GROUP BY sale_order_template_id + ) template2max_sequence + ON + template2max_sequence.sale_order_template_id=soto.sale_order_template_id + ORDER BY + sale_order_template_id """ ) + last_template_id = 0 for ( option_id, template_id, @@ -78,24 +107,37 @@ def sale_order_template_options(env): product_id, quantity, uom_id, + max_sequence, ) in env.cr.fetchall(): + if template_id != last_template_id: + SaleOrderTemplateLine.create( + { + "is_optional": True, + "sale_order_template_id": template_id, + "name": "/", + "display_type": "line_section", + "sequence": (max_sequence or 0) + 1, + } + ) line = SaleOrderTemplateLine.create( { - "is_optional": True, "sale_order_template_id": template_id, - "name": name, + "name": "/", "product_id": product_id, "product_uom_qty": quantity, "product_uom_id": uom_id, + "sequence": (max_sequence or 0) + 2, } ) env.cr.execute( f""" UPDATE sale_order_template_line - SET {link_column}={option_id} + SET {link_column}={option_id}, name=%s WHERE id={line.id} - """ + """, + (json.dumps(name),), ) + last_template_id = template_id @openupgrade.migrate() diff --git a/openupgrade_scripts/scripts/sale_management/19.0.1.0/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/sale_management/19.0.1.0/upgrade_analysis_work.txt index 7aae2c5ce594..49e0e0d7fdf7 100644 --- a/openupgrade_scripts/scripts/sale_management/19.0.1.0/upgrade_analysis_work.txt +++ b/openupgrade_scripts/scripts/sale_management/19.0.1.0/upgrade_analysis_work.txt @@ -27,7 +27,7 @@ sale_management / sale.order.template.option / quantity (float) : D sale_management / sale.order.template.option / sale_order_template_id (many2one): DEL relation: sale.order.template, required sale_management / sale.order.template.option / uom_id (many2one) : DEL relation: uom.uom, required -# DONE: create optional lines / template lines from former options +# DONE: create optional lines / template lines + sections from former options ---XML records in module 'sale_management'--- DEL ir.model.access: sale_management.access_sale_order_option diff --git a/openupgrade_scripts/scripts/sale_management/tests/data_sale_management_migration.py b/openupgrade_scripts/scripts/sale_management/tests/data_sale_management_migration.py index d630650488d6..417c1bc4f83c 100644 --- a/openupgrade_scripts/scripts/sale_management/tests/data_sale_management_migration.py +++ b/openupgrade_scripts/scripts/sale_management/tests/data_sale_management_migration.py @@ -37,4 +37,41 @@ ) order._onchange_sale_order_template_id() +order.write( + { + "order_line": [ + ( + 0, + 0, + { + "name": "non-optional line", + "product_id": env.ref("product.product_product_3").id, + "product_uom": env.ref("uom.product_uom_unit").id, + "product_uom_qty": 2, + }, + ) + ] + } +) + +order.sale_order_option_ids[0].add_option_to_order() + +order.write( + { + "order_line": [ + ( + 0, + 0, + { + "name": "non-optional line2", + "product_id": env.ref("product.product_product_3").id, + "product_uom": env.ref("uom.product_uom_unit").id, + "product_uom_qty": 22, + }, + ) + ] + } +) + + env.cr.commit() diff --git a/openupgrade_scripts/scripts/sale_management/tests/test_sale_management_migration.py b/openupgrade_scripts/scripts/sale_management/tests/test_sale_management_migration.py index 8a03a4aeba6d..a33a44cb009a 100644 --- a/openupgrade_scripts/scripts/sale_management/tests/test_sale_management_migration.py +++ b/openupgrade_scripts/scripts/sale_management/tests/test_sale_management_migration.py @@ -1,4 +1,4 @@ -from odoo.tests import TransactionCase +from odoo.tests import TransactionCase, tagged from odoo.addons.openupgrade_framework import openupgrade_test @@ -13,9 +13,48 @@ def test_sale_order_template_migration(self): [("sale_order_template_id", "=", template.id)] ) self.assertItemsEqual( - order.order_line.mapped("name"), ("some option", "another option") + order.order_line.mapped("name"), + ( + "non-optional line", + "non-optional line2", + "/", + "some option", + "another option", + ), ) - self.assertItemsEqual(order.order_line.mapped("is_optional"), (True, True)) self.assertItemsEqual( - template.sale_order_template_line_ids.mapped("is_optional"), (True, True) + order.order_line.mapped("is_optional"), (False, False, True, False, False) + ) + self.assertItemsEqual( + map(lambda x: x._is_line_optional(), order.order_line), + (False, False, False, True, True), + ) + self.assertItemsEqual( + template.sale_order_template_line_ids.mapped("name"), + ("/", "some option", "another option"), + ) + self.assertItemsEqual( + template.sale_order_template_line_ids.mapped("is_optional"), + (True, False, False), + ) + + +@openupgrade_test +@tagged("-at_install", "post_install") +class TestSaleManagementMigrationPost(TransactionCase): + def test_sale_order_template_migration(self): + template = self.env["sale.order.template"].search( + [("name", "=", "Sale order template")], + ) + self.assertItemsEqual( + template.sale_order_template_line_ids.with_context(lang="fr_FR").mapped( + "name" + ), + ("Section produits optionnels", "some option", "another option"), + ) + self.assertItemsEqual( + template.sale_order_template_line_ids.with_context(lang="en_US").mapped( + "name" + ), + ("Optional Products Section", "some option", "another option"), )