diff --git a/docsource/modules180-190.rst b/docsource/modules180-190.rst index 0b20a4f9a21..ff2c053df64 100644 --- a/docsource/modules180-190.rst +++ b/docsource/modules180-190.rst @@ -1004,7 +1004,7 @@ Module coverage 18.0 -> 19.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | sale_loyalty_delivery | |No DB layout changes. | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| sale_management | | | +| sale_management |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ | sale_margin | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ 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 new file mode 100644 index 00000000000..127207e5e6e --- /dev/null +++ b/openupgrade_scripts/scripts/sale_management/19.0.1.0/post-migration.py @@ -0,0 +1,104 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from openupgradelib import openupgrade + + +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 + FROM + sale_order_option + """ + ) + for ( + option_id, + order_id, + name, + product_id, + quantity, + price_unit, + sequence, + uom_id, + discount, + ) 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} + """ + ) + + +def sale_order_template_options(env): + """ + Model sale.order.template.option has been replaced by optional lines + """ + SaleOrderTemplateLine = env["sale.order.template.line"] + link_column = openupgrade.get_legacy_name("option_id") + env.cr.execute( + "ALTER TABLE sale_order_template_line " + f"ADD COLUMN IF NOT EXISTS {link_column} int " + ) + env.cr.execute( + """ + SELECT + id, sale_order_template_id, name, product_id, quantity, uom_id + FROM + sale_order_template_option + """ + ) + for ( + option_id, + template_id, + name, + product_id, + quantity, + uom_id, + ) in env.cr.fetchall(): + line = SaleOrderTemplateLine.create( + { + "is_optional": True, + "sale_order_template_id": template_id, + "name": name, + "product_id": product_id, + "product_uom_qty": quantity, + "product_uom_id": uom_id, + } + ) + env.cr.execute( + f""" + UPDATE sale_order_template_line + SET {link_column}={option_id} + WHERE id={line.id} + """ + ) + + +@openupgrade.migrate() +def migrate(env, version): + sale_order_line_options(env) + sale_order_template_options(env) 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 new file mode 100644 index 00000000000..7aae2c5ce59 --- /dev/null +++ b/openupgrade_scripts/scripts/sale_management/19.0.1.0/upgrade_analysis_work.txt @@ -0,0 +1,41 @@ +---Models in module 'sale_management'--- +obsolete model sale.order.option +obsolete model sale.order.template.option + +# DONE: moved to sale.order.line/sale.order.template.line + +---Fields in module 'sale_management'--- +sale_management / sale.order / sale_order_option_ids (one2many): DEL relation: sale.order.option +sale_management / sale.order.line / is_optional (boolean) : NEW hasdefault: default +sale_management / sale.order.line / sale_order_option_ids (one2many): DEL relation: sale.order.option +sale_management / sale.order.option / discount (float) : DEL +sale_management / sale.order.option / line_id (many2one) : DEL relation: sale.order.line +sale_management / sale.order.option / name (text) : DEL required +sale_management / sale.order.option / order_id (many2one) : DEL relation: sale.order +sale_management / sale.order.option / price_unit (float) : DEL required +sale_management / sale.order.option / product_id (many2one) : DEL relation: product.product, required +sale_management / sale.order.option / quantity (float) : DEL required +sale_management / sale.order.option / sequence (integer) : DEL +sale_management / sale.order.option / uom_id (many2one) : DEL relation: uom.uom, required +sale_management / sale.order.template / sale_order_template_option_ids (one2many): DEL relation: sale.order.template.option +sale_management / sale.order.template.line / display_type (selection) : selection_keys added: [line_subsection] (most likely nothing to do) +sale_management / sale.order.template.line / is_optional (boolean) : NEW hasdefault: default +sale_management / sale.order.template.option / company_id (many2one) : DEL relation: res.company +sale_management / sale.order.template.option / name (text) : DEL required +sale_management / sale.order.template.option / product_id (many2one) : DEL relation: product.product, required +sale_management / sale.order.template.option / quantity (float) : DEL required +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 + +---XML records in module 'sale_management'--- +DEL ir.model.access: sale_management.access_sale_order_option +DEL ir.model.access: sale_management.access_sale_order_option_invoice +DEL ir.model.access: sale_management.access_sale_order_option_readonly +DEL ir.model.access: sale_management.access_sale_order_template_option +DEL ir.model.access: sale_management.access_sale_order_template_option_manager +NEW ir.ui.view: sale_management.sale_order_portal_optional_product_quantity +DEL ir.ui.view: sale_management.report_saleorder_document_inherit_sale_management + +# NOTHING TO DO 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 new file mode 100644 index 00000000000..d630650488d --- /dev/null +++ b/openupgrade_scripts/scripts/sale_management/tests/data_sale_management_migration.py @@ -0,0 +1,40 @@ +env = locals().get("env") + +# create template with options +template = env["sale.order.template"].create( + { + "name": "Sale order template", + "sale_order_template_line_ids": [], + "sale_order_template_option_ids": [ + ( + 0, + 0, + { + "name": "some option", + "product_id": env.ref("product.product_product_1").id, + "uom_id": env.ref("uom.product_uom_unit").id, + "quantity": 42, + }, + ), + ( + 0, + 0, + { + "name": "another option", + "product_id": env.ref("product.product_product_2").id, + "uom_id": env.ref("uom.product_uom_unit").id, + "quantity": 4242, + }, + ), + ], + } +) +order = env["sale.order"].create( + { + "partner_id": env.user.partner_id.id, + "sale_order_template_id": template.id, + } +) +order._onchange_sale_order_template_id() + +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 new file mode 100644 index 00000000000..8a03a4aeba6 --- /dev/null +++ b/openupgrade_scripts/scripts/sale_management/tests/test_sale_management_migration.py @@ -0,0 +1,21 @@ +from odoo.tests import TransactionCase + +from odoo.addons.openupgrade_framework import openupgrade_test + + +@openupgrade_test +class TestSaleManagementMigration(TransactionCase): + def test_sale_order_template_migration(self): + template = self.env["sale.order.template"].search( + [("name", "=", "Sale order template")], + ) + order = self.env["sale.order"].search( + [("sale_order_template_id", "=", template.id)] + ) + self.assertItemsEqual( + order.order_line.mapped("name"), ("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) + )