Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docsource/modules170-180.rst
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ Module coverage 17.0 -> 18.0
+---------------------------------------------------+----------------------+-------------------------------------------------+
| privacy_lookup | |No DB layout changes. |
+---------------------------------------------------+----------------------+-------------------------------------------------+
| product | | |
| product |Done | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
| product_email_template | |No DB layout changes. |
+---------------------------------------------------+----------------------+-------------------------------------------------+
Expand Down
73 changes: 73 additions & 0 deletions openupgrade_scripts/scripts/product/18.0.1.2/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2025 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from openupgradelib import openupgrade, openupgrade_180


def product_document_sequence(env):
"""
Set sequence matching previous name only ordering
"""
openupgrade.logged_query(
env.cr,
"""
UPDATE product_document
SET sequence=sequence.sequence
FROM
(
SELECT
product_document.id, row_number() OVER (
PARTITION BY ir_attachment.res_id, ir_attachment.res_model
ORDER BY ir_attachment.name
) sequence
FROM
product_document
JOIN ir_attachment
ON product_document.ir_attachment_id=ir_attachment.id
) sequence
WHERE
sequence.id=product_document.id
AND product_document.sequence IS NULL
""",
)


def product_template_is_favorite(env):
"""
Set is_favorite flag based on priority
"""
openupgrade.logged_query(
env.cr,
"""
UPDATE
product_template
SET is_favorite = (priority IS NOT NULL AND priority != '0')
""",
)


def res_partner_specific_property_product_pricelist(env):
"""
Get value for specific_property_product_pricelist from v17
property_product_pricelist
"""
old_field = env.ref("product.field_res_partner__property_product_pricelist")
openupgrade_180.convert_company_dependent(
env,
"res.partner",
"specific_property_product_pricelist",
old_field_id=old_field.id,
)
# convert_company_dependent might have created ir.default entries, wipe them
new_field = env.ref(
"product.field_res_partner__specific_property_product_pricelist"
)
env["ir.default"].search([("field_id", "=", new_field.id)]).unlink()


@openupgrade.migrate()
def migrate(env, version):
product_document_sequence(env)
product_template_is_favorite(env)
res_partner_specific_property_product_pricelist(env)
openupgrade_180.convert_company_dependent(env, "product.product", "standard_price")
73 changes: 73 additions & 0 deletions openupgrade_scripts/scripts/product/18.0.1.2/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2025 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from openupgradelib import openupgrade

xmlid_renames = [
("product.group_discount_per_so_line", "sale.group_discount_per_so_line"),
]

column_creates = [
("product.attribute", "active", "boolean", "TRUE"),
("product.attribute.value", "active", "boolean", "TRUE"),
("product.pricelist.item", "display_applied_on", "char"),
("product.pricelist.item", "price_markup", "float"),
]


def rename_pos_models(env):
"""
pos.combo and pos.combo.line have been moved to product from point_of_sale and
renamed to product.combo and product.combo.item respectively
"""
if not openupgrade.table_exists(env.cr, "pos_combo"):
return

openupgrade.rename_tables(
env.cr,
[
("pos_combo", "product_combo"),
("pos_combo_line", "product_combo_item"),
],
)
openupgrade.rename_models(
env.cr,
[
("pos.combo", "product.combo"),
("pos.combo.line", "product.combo.item"),
],
)
openupgrade.rename_fields(
env,
[
("product.combo", "product_combo", "combo_line_ids", "combo_item_ids"),
("product.combo.item", "product_combo_item", "combo_price", "extra_price"),
],
)


def fill_product_pricelist_item_columns(env):
"""
Set display_applied_on to '2_product_category' if applied_on is
'2_product_category', else '1_product'
Set price_markup = -price_discount
"""
env.cr.execute(
"""
UPDATE product_pricelist_item
SET
display_applied_on=CASE
WHEN applied_on='2_product_category' THEN '2_product_category'
ELSE '1_product'
END,
price_markup=-price_discount
"""
)


@openupgrade.migrate()
def migrate(env, version):
openupgrade.rename_xmlids(env.cr, xmlid_renames)
openupgrade.add_columns(env, column_creates)
fill_product_pricelist_item_columns(env)
rename_pos_models(env)
106 changes: 106 additions & 0 deletions openupgrade_scripts/scripts/product/18.0.1.2/upgrade_analysis_work.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---Models in module 'product'---
new model product.combo
new model product.combo.item
# DONE: pre-migration: rename models and fields from point_of_sale models if installed

new model update.product.attribute.value [transient]
# NOTHING TO DO: new functionality

---Fields in module 'product'---
product / product.attribute / active (boolean) : NEW hasdefault: default
# DONE: added in pre-migration

product / product.attribute / template_value_ids (one2many) : NEW relation: product.template.attribute.value
# NOTHING TO DO

product / product.attribute.value / active (boolean) : NEW hasdefault: default
# DONE: added in pre-migration

product / product.category / message_follower_ids (one2many): NEW relation: mail.followers
product / product.category / message_ids (one2many) : NEW relation: mail.message
product / product.combo / combo_item_ids (one2many) : NEW relation: product.combo.item
product / product.combo / company_id (many2one) : NEW relation: res.company
product / product.combo / name (char) : NEW required
product / product.combo / sequence (integer) : NEW hasdefault: default
product / product.combo.item / combo_id (many2one) : NEW relation: product.combo, required
product / product.combo.item / company_id (many2one) : NEW relation: res.company, isrelated: related, stored
product / product.combo.item / extra_price (float) : NEW hasdefault: default
product / product.combo.item / product_id (many2one) : NEW relation: product.product, required
# NOTHING TO DO

product / product.document / _order : _order is now 'sequence, name' ('name')
# DONE: see product.document#sequence below

product / product.document / sequence (integer) : NEW hasdefault: default
# DONE: post-migration: create sequence matching name

product / product.pricelist / _order : _order is now 'sequence, id, name' ('sequence asc, id asc')
# NOTHING TO DO: change doesn't actually affect ordering

product / product.pricelist / discount_policy (selection) : DEL required, selection_keys: ['with_discount', 'without_discount']
product / product.pricelist.item / display_applied_on (selection): NEW required, selection_keys: ['1_product', '2_product_category'], hasdefault: default
product / product.pricelist.item / price_markup (float) : NEW isfunction: function, stored
# DONE: added and filled in pre-migration

product / product.product / _order : _order is now 'is_favorite desc, default_code, name, id' ('priority desc, default_code, name, id')
# NOTHING TO DO: see product.template#is_favorite below

product / product.product / combo_ids (many2many) : previously in module point_of_sale
# DONE: see product.combo above

product / product.product / service_tracking (selection) : previously in module sale_project
# NOTHING TO DO

product / product.product / standard_price (float) : needs conversion to v18-style company dependent
# DONE: post-migration

product / product.product / type (selection) : now required
# NOTHING TO DO: had a default in previous version already

product / product.tag / _order : _order is now 'sequence, id' ('id')
product / product.tag / sequence (integer) : NEW hasdefault: default
product / product.template / _order : _order is now 'is_favorite desc, name' ('priority desc, name')
# NOTHING TO DO: ordering stays the same

product / product.template / combo_ids (many2many) : previously in module point_of_sale
# NOTHING TO DO: see product.combo above

product / product.template / detailed_type (selection) : DEL required, selection_keys: ['consu', 'service']
# NOTHING TO DO, might need followup in subsequent addons

product / product.template / is_favorite (boolean) : NEW
product / product.template / priority (selection) : DEL selection_keys: ['0', '1']
# DONE: set is_favorite flag based on priority

product / product.template / service_tracking (selection) : previously in module sale_project
product / product.template / type (selection) : now required
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sure this doesn't happen, but maybe just in case, we should fill if empty using detailed_type

product / product.template / type (selection) : selection_keys is now '['combo', 'consu', 'service']' ('['consu', 'service']')
# NOTHING TO DO

product / res.partner / specific_property_product_pricelist (many2one): NEW relation: product.pricelist
# DONE: post-migration: convert from v17 property res.partner#property_product_pricelist

---XML records in module 'product'---
NEW ir.actions.act_window: product.product_combo_action
NEW ir.model.access: product.access_product_combo_item_manager
NEW ir.model.access: product.access_product_combo_item_user
NEW ir.model.access: product.access_product_combo_manager
NEW ir.model.access: product.access_product_combo_user
NEW ir.model.access: product.access_update_product_attribute_value_manager
DEL ir.model.constraint: product.constraint_product_attribute_value_value_company_uniq
NEW ir.rule: product.product_combo_comp_rule (noupdate)
NEW ir.ui.view: product.product_attribute_search
NEW ir.ui.view: product.product_combo_view_form
NEW ir.ui.view: product.product_combo_view_tree
NEW ir.ui.view: product.product_packaging_search_view
NEW ir.ui.view: product.product_product_view_form_normalized
NEW ir.ui.view: product.product_template_attribute_line_view_tree
NEW ir.ui.view: product.update_product_attribute_value_form
NEW res.groups: product.group_product_manager
# NOTHING TO DO

DEL res.groups: product.group_discount_per_so_line [renamed to sale module]
# DONE: pre-migration: moved xmlid to sale

DEL res.groups: product.group_sale_pricelist
# NOTHING TO DO
15 changes: 15 additions & 0 deletions openupgrade_scripts/scripts/product/tests/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
env = locals().get("env")
# set specific pricelist
pricelist_main = env["product.pricelist"].create({"name": "Pricelist for main company"})
company = env["res.company"].create({"name": "Product migration test company"})
pricelist = (
env["product.pricelist"]
.with_company(company)
.create({"name": "Pricelist for demo company"})
)
partner = env.ref("base.user_demo").partner_id

partner.property_product_pricelist = pricelist_main
partner.with_company(company).property_product_pricelist = pricelist

env.cr.commit()
30 changes: 30 additions & 0 deletions openupgrade_scripts/scripts/product/tests/test_migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from odoo.tests import TransactionCase

from odoo.addons.openupgrade_framework import openupgrade_test


@openupgrade_test
class TestProductMigration(TransactionCase):
def test_pricelist(self):
partner = self.env.ref("base.user_demo").partner_id
company = self.env["res.company"].search(
[
("name", "=", "Product migration test company"),
]
)
pricelist_main = self.env["product.pricelist"].search(
[
("name", "=", "Pricelist for main company"),
]
)
pricelist = self.env["product.pricelist"].search(
[
("name", "=", "Pricelist for demo company"),
]
)
self.assertTrue(company)
self.assertTrue(pricelist)
self.assertEqual(partner.property_product_pricelist, pricelist_main)
self.assertEqual(
partner.with_company(company).property_product_pricelist, pricelist
)