From 064be20d687c42f8aff219d8e50300b9409f79eb Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Tue, 4 Nov 2025 10:47:17 -0400 Subject: [PATCH 01/16] imp(l10n_do_banks): migration scripts updated as needed for luminatti --- src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py | 6 ++++++ src/l10n_do_banks/17.0.1.0.0/pre-view-delete.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py b/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py index 8ddcd431d..d3196e896 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py @@ -81,6 +81,12 @@ def uninstall_modules(cr): 'stock_account_product_cost_security', 'product_code_unique', 'account_ecf_auto_post', + 'qztray_base', + 'professional_templates', + 'stock_available_unreserved', + 'product_warehouse_quantity', + 'sale_discount_limit', + 'sales_product_warehouse_quantity', ] for module_name in modules_to_uninstall: diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-view-delete.py b/src/l10n_do_banks/17.0.1.0.0/pre-view-delete.py index dfdc80d5f..01bedfc36 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-view-delete.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-view-delete.py @@ -25,6 +25,11 @@ def migrate(cr, version): 'studio_customization.odoo_studio_warranty_5e101e7c-e388-4589-b9b8-628b03ca43f1', 'studio_customization.odoo_studio_warranty_64540003-0f78-4e15-8bba-6d3475418fed', 'product_product_price_widget.product_product_tree_view_inherit_widget', + 'website_livechat.channel_list_page', + '__export__.ir_ui_view_4053_9a82b3d7', + 'professional_templates.view_sale_order_inherit_customized', + 'professional_templates.view_rfq_inherit_customized', + 'professional_templates.purchase_order_inherited_customized', ] for xml_id in views_to_delete: From 87ecedb6f3e7666c4c7e77b8a93706933e2ff7c4 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Wed, 5 Nov 2025 14:25:44 -0400 Subject: [PATCH 02/16] imp(l10n_do_banks): views added to activate_views_list function --- .../17.0.1.0.0/end-views-activation.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py b/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py index 35a0d9c6e..74a18f5d5 100644 --- a/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py +++ b/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py @@ -162,12 +162,91 @@ def migrate(cr, version): 'stock_inventory_forecasted_report.res_config_settings_view_form', 'stock_landed_costs_features.stock_landed_cost_run_form', 'stock_landed_costs_file.stock_landed_cost_run_form_inherited', + 'stock_landed_costs_file.stock_landed_cost_run_inherited', 'stock_landed_costs_file.view_move_form_inherited', 'tss_report.tss_report_form_wizard', 'tss_report.view_hr_payslip_form', 'website_stock_availability.res_config_settings_view_form_inherit', 'operating_unit.view_user_form', 'sale_discount_display_amount.sale_order_view_form_display_discount', + 'operating_unit.view_users_form', + 'sale_order_transit_notification.res_config_settings_view_form_transit_inherit', + 'whatsapp_connector.res_config_settings_view_form', + 'sale_order_rate.res_config_settings_view_form', + 'l10n_do_hr_payroll.res_config_settings_view_form', + 'product_sequence.res_config_settings_view_form', + 'bi_warranty_registration.res_config_settings_view_form_stock', + 'bi_warranty_registration.res_config_settings_view_form', + 'product_price_checker.res_config_settings_view_form_checker', + 'purchase_request.purchase_order_line_form2_sub', + 'stock.stock_location_view_form_editable', + 'product_stock_qty_date_widgets.product_product_tree_view_inherit_widget_qty', + 'product_brand.view_product_variant_kanban_brand', + 'recurring_sale_order_app.recurring_order_view_form', + 'stock.stock_location_view_tree2_editable', + 'sale_pos_backend_card_bin_promotion.view_order_form_inherited', + 'sale_pos_backend_warranty_reports.view_order_form_warranty_inherit', + 'sale_pos_session_link.view_order_sales_pos_backend_inherit_form', + 'sale_pos_backend_advance_payment.sale_pos_backend_order_form_advance_inherit', + 'l10n_do_sale_pos_backend.view_order_form_inherited', + 'cecomsa_sale_control.view_order_sales_pos_backend_form_inherit', + 'sale_pos_backend.view_partner_form_sale_cashier', + 'l10n_do_chase.journal_payment_view_form', + 'l10n_do_account_bank_payment_se.account_payment_line_mixed_view_form', + 'l10n_do_account_bank_payment_se.account_payment_line_view_form', + 'l10n_do_chase.account_move_form_inherit_view', + 'l10n_do_electronic_invoice.autorized_xml_file_4e_view_form', + 'l10n_do_fiscal_printer.ir_sequence_view_form', + 'l10n_do_fiscal_printer.res_config_settings_view_form', + 'l10n_do_fiscal_printer_ir_series_move_form', + 'l10n_do_fleet_vehicle_journal.inherit_fleet_vehicle_view_form', + 'l10n_do_fleet_vehicle_journal.journal_entry_view_form', + 'l10n_do_hr_payroll_inherit.res_config_settings_view_form', + 'l10n_do_hr_payroll_inherit.sheet_import_line_form_view', + 'l10n_do_hr_payroll_inherit.sheet_import_view_form', + 'l10n_do_hr_payroll_inherit.sheet_import_view_tree', + 'l10n_do_income_statement.wizard_income_statement_config_view', + 'l10n_do_income_statement.report_line_form_view', + 'l10n_do_isr_retention.wizard_view_form', + 'l10n_do_pos_bank_statement.l10n_do_pos_bank_statement_form_view', + 'l10n_do_pos_return.order_form_view', + 'l10n_do_sale_order_extension.res_config_settings_view_form', + 'l10n_do_project_sale.project_project_view_form_inherit', + 'l10n_do_purchase.document_type_wizard_view_form', + 'l10n_do_purchase.requisition_view_form', + 'l10n_do_sale.purchase_order_line_inherit_view', + 'l10n_do_sale_order_view.line_inherit_view_form', + 'l10n_do_sale_order_view.order_view_form', + 'l10n_do_sale_payment_method.form_view_payment_inherit', + 'l10n_do_sale_payment_method.sale_order_form_view_payment_inherit', + 'l10n_do_supplier_invoice.res_config_settings_view_form_supplier_invoice', + 'l10n_do_supplier_payment.wizard_supplier_payment_view_form', + 'l10n_do_supplier_invoice.view_account_move_supplier_invoice_form', + 'l10n_do_supplier_payment.advance_payment_inherit_form_view', + 'l10n_do_supplier_payment.res_partner_form_inherit_view', + 'l10n_do_supplier_payment.view_account_move_supplier_form_inherit', + 'l10n_do_supplier_withholding.account_move_form_supplier_withholding', + 'l10n_do_supplier_withholding.partner_supplier_withholding_view_form', + 'l10n_do_supplier_withholding.res_config_settings_supplier_withholding_view_form', + 'pm_bank_transfer_order.view_bank_transfer_order_form', + 'pm_bank_transfer_order.view_res_partner_bank_inherit_form', + 'pm_bank_transfer_order.wizard_view_form', + 'pm_bank_transfer_order_line.view_bank_transfer_order_line_form', + 'pm_bank_transfer_order_line_inherit.view_bank_transfer_order_line_form_inherit', + 'pos_backend_order.line_inherit_view_form', + 'pos_backend_order.order_view_form', + 'pos_journal_control.wizard_view_form', + 'pos_journal_control.view_session_journal_control_form', + 'pos_payment_method_extension.payment_method_inherit_form_view', + 'pos_payment_method_extension.pos_payment_method_view_form', + 'pos_payment_method_extension.wizard_view_form', + 'pos_sale_report.order_sale_report_wizard_form_view', + 'sale_cashier_inherit.view_sale_cashier_form', + 'sale_cashier_inherit.view_sale_cashier_session_form', + 'sale_cashier_inherit.view_sale_cashier_session_inherit_form', + 'sale_cashier_inherit.view_sale_cashier_session_return_form', + 'sale_cashier_inherit.view_users_form', + ] ] remove_inherit_views_list = [ From 910632fa863deeb11e77b1300db54504d053e680 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Wed, 5 Nov 2025 14:34:49 -0400 Subject: [PATCH 03/16] imp(l10n_do_banks): views added to activate_views_list function --- .../17.0.1.0.0/end-views-activation.py | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py b/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py index 74a18f5d5..9f61497bf 100644 --- a/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py +++ b/src/l10n_do_banks/17.0.1.0.0/end-views-activation.py @@ -162,39 +162,38 @@ def migrate(cr, version): 'stock_inventory_forecasted_report.res_config_settings_view_form', 'stock_landed_costs_features.stock_landed_cost_run_form', 'stock_landed_costs_file.stock_landed_cost_run_form_inherited', - 'stock_landed_costs_file.stock_landed_cost_run_inherited', 'stock_landed_costs_file.view_move_form_inherited', 'tss_report.tss_report_form_wizard', 'tss_report.view_hr_payslip_form', 'website_stock_availability.res_config_settings_view_form_inherit', 'operating_unit.view_user_form', 'sale_discount_display_amount.sale_order_view_form_display_discount', - 'operating_unit.view_users_form', - 'sale_order_transit_notification.res_config_settings_view_form_transit_inherit', - 'whatsapp_connector.res_config_settings_view_form', - 'sale_order_rate.res_config_settings_view_form', - 'l10n_do_hr_payroll.res_config_settings_view_form', - 'product_sequence.res_config_settings_view_form', - 'bi_warranty_registration.res_config_settings_view_form_stock', - 'bi_warranty_registration.res_config_settings_view_form', - 'product_price_checker.res_config_settings_view_form_checker', - 'purchase_request.purchase_order_line_form2_sub', - 'stock.stock_location_view_form_editable', - 'product_stock_qty_date_widgets.product_product_tree_view_inherit_widget_qty', - 'product_brand.view_product_variant_kanban_brand', - 'recurring_sale_order_app.recurring_order_view_form', - 'stock.stock_location_view_tree2_editable', - 'sale_pos_backend_card_bin_promotion.view_order_form_inherited', - 'sale_pos_backend_warranty_reports.view_order_form_warranty_inherit', - 'sale_pos_session_link.view_order_sales_pos_backend_inherit_form', - 'sale_pos_backend_advance_payment.sale_pos_backend_order_form_advance_inherit', - 'l10n_do_sale_pos_backend.view_order_form_inherited', - 'cecomsa_sale_control.view_order_sales_pos_backend_form_inherit', - 'sale_pos_backend.view_partner_form_sale_cashier', - 'l10n_do_chase.journal_payment_view_form', + 'operating_unit.view_users_form' + 'sale_order_transit_notification.res_config_settings_view_form_transit_inherit' + 'whatsapp_connector.res_config_settings_view_form' + 'sale_order_rate.res_config_settings_view_form' + 'l10n_do_hr_payroll.res_config_settings_view_form' + 'product_sequence.res_config_settings_view_form' + 'bi_warranty_registration.res_config_settings_view_form_stock' + 'bi_warranty_registration.res_config_settings_view_form' + 'product_price_checker.res_config_settings_view_form_checker' + 'purchase_request.purchase_order_line_form2_sub' + 'product_stock_qty_date_widgets.product_product_tree_view_inherit_widget_qty' + 'product_brand.view_product_variant_kanban_brand' + 'recurring_sale_order_app.recurring_order_view_form' + 'stock.stock_location_view_form_editable' + 'stock.stock_location_view_tree2_editable' + 'sale_pos_backend_card_bin_promotion.view_order_form_inherited' + 'sale_pos_session_link.view_order_sales_pos_backend_inherit_form' + 'sale_pos_backend_advance_payment.sale_pos_backend_order_form_advance_inherit' + 'l10n_do_sale_pos_backend.view_order_form_inherited' + 'sale_pos_backend_warranty_reports.view_order_form_warranty_inherit' + 'cecomsa_sale_control.view_order_sales_pos_backend_form_inherit' + 'sale_pos_backend.view_partner_form_sale_cashier' 'l10n_do_account_bank_payment_se.account_payment_line_mixed_view_form', 'l10n_do_account_bank_payment_se.account_payment_line_view_form', 'l10n_do_chase.account_move_form_inherit_view', + 'l10n_do_chase.journal_payment_view_form', 'l10n_do_electronic_invoice.autorized_xml_file_4e_view_form', 'l10n_do_fiscal_printer.ir_sequence_view_form', 'l10n_do_fiscal_printer.res_config_settings_view_form', @@ -205,26 +204,26 @@ def migrate(cr, version): 'l10n_do_hr_payroll_inherit.sheet_import_line_form_view', 'l10n_do_hr_payroll_inherit.sheet_import_view_form', 'l10n_do_hr_payroll_inherit.sheet_import_view_tree', - 'l10n_do_income_statement.wizard_income_statement_config_view', 'l10n_do_income_statement.report_line_form_view', + 'l10n_do_income_statement.wizard_income_statement_config_view', 'l10n_do_isr_retention.wizard_view_form', 'l10n_do_pos_bank_statement.l10n_do_pos_bank_statement_form_view', 'l10n_do_pos_return.order_form_view', - 'l10n_do_sale_order_extension.res_config_settings_view_form', 'l10n_do_project_sale.project_project_view_form_inherit', 'l10n_do_purchase.document_type_wizard_view_form', 'l10n_do_purchase.requisition_view_form', 'l10n_do_sale.purchase_order_line_inherit_view', + 'l10n_do_sale_order_extension.res_config_settings_view_form', 'l10n_do_sale_order_view.line_inherit_view_form', 'l10n_do_sale_order_view.order_view_form', 'l10n_do_sale_payment_method.form_view_payment_inherit', 'l10n_do_sale_payment_method.sale_order_form_view_payment_inherit', 'l10n_do_supplier_invoice.res_config_settings_view_form_supplier_invoice', - 'l10n_do_supplier_payment.wizard_supplier_payment_view_form', 'l10n_do_supplier_invoice.view_account_move_supplier_invoice_form', 'l10n_do_supplier_payment.advance_payment_inherit_form_view', 'l10n_do_supplier_payment.res_partner_form_inherit_view', 'l10n_do_supplier_payment.view_account_move_supplier_form_inherit', + 'l10n_do_supplier_payment.wizard_supplier_payment_view_form', 'l10n_do_supplier_withholding.account_move_form_supplier_withholding', 'l10n_do_supplier_withholding.partner_supplier_withholding_view_form', 'l10n_do_supplier_withholding.res_config_settings_supplier_withholding_view_form', @@ -246,7 +245,7 @@ def migrate(cr, version): 'sale_cashier_inherit.view_sale_cashier_session_inherit_form', 'sale_cashier_inherit.view_sale_cashier_session_return_form', 'sale_cashier_inherit.view_users_form', - ] + ] remove_inherit_views_list = [ From 9d754e89de35c7729572964b8702727c5372feb0 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Thu, 6 Nov 2025 16:38:30 -0400 Subject: [PATCH 04/16] imp(l10n_do_banks): pre-drop-constraint-product-code script and ks_dashboard_ninja, report_xlsx, web_m2x_options delete assets functions added --- src/l10n_do_banks/17.0.1.0.0/end-migration.py | 30 +++++++++++++ .../pre-drop-constraint-product-code.py | 44 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py diff --git a/src/l10n_do_banks/17.0.1.0.0/end-migration.py b/src/l10n_do_banks/17.0.1.0.0/end-migration.py index 789cbefac..8ae9e6241 100644 --- a/src/l10n_do_banks/17.0.1.0.0/end-migration.py +++ b/src/l10n_do_banks/17.0.1.0.0/end-migration.py @@ -15,6 +15,36 @@ def delete_advanced_web_domain_widget_assets(cr): asset.unlink() _logger.info("Advanced Web Domain Widget assets deleted") +def delete_ks_dashboard_ninja_assets(cr): + """ + Script to delete ks_dashboard_ninja assets. + """ + env = api.Environment(cr, SUPERUSER_ID, {}) + assets = env['ir.asset'].search([('name', 'like', 'ks_dashboard_ninja.assets_backend%')]) + for asset in assets: + asset.unlink() + _logger.info("Dashboard Ninja assets deleted") + +def delete_report_xlsx_assets(cr): + """ + Script to delete report_xlsx assets. + """ + env = api.Environment(cr, SUPERUSER_ID, {}) + assets = env['ir.asset'].search([('name', 'like', 'report_xlsx.assets_backend%')]) + for asset in assets: + asset.unlink() + _logger.info("Report xlsx assets deleted") + +def delete_web_m2x_options_assets(cr): + """ + Script to delete web_m2x_options assets. + """ + env = api.Environment(cr, SUPERUSER_ID, {}) + assets = env['ir.asset'].search([('name', 'like', 'web_m2x_options.assets_backend%')]) + for asset in assets: + asset.unlink() + _logger.info("Web m2x options assets deleted") + def deactivate_studio_views(cr): """ Script de post-migración para buscar y desactivar vistas de Studio. diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py new file mode 100644 index 000000000..1645ceb64 --- /dev/null +++ b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import logging + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + """ + Pre-migration script to drop the unique constraint on product_product.default_code. + + This script removes the constraint 'product_product_default_code_uniq' from the + product_product table to allow duplicate default codes. + + Args: + cr (cursor): Database cursor + version (str): Module version + """ + _logger.info('Starting pre-migration: dropping product_product.default_code unique constraint') + + try: + # Check if constraint exists before attempting to drop it + cr.execute(""" + SELECT 1 + FROM pg_constraint + WHERE conname = 'product_product_default_code_uniq' + """) + + if cr.rowcount > 0: + _logger.info('Dropping constraint product_product_default_code_uniq') + cr.execute(""" + ALTER TABLE product_product + DROP CONSTRAINT product_product_default_code_uniq + """) + _logger.info('Successfully dropped constraint product_product_default_code_uniq') + else: + _logger.info('Constraint product_product_default_code_uniq does not exist, skipping') + + except Exception as e: + _logger.error(f'Error dropping constraint product_product_default_code_uniq: {e}') + raise + + _logger.info('Finished pre-migration: dropping product_product.default_code unique constraint') + From ae77cf54a1028ec243af41fbb0a6ffe6c3456496 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Fri, 7 Nov 2025 14:12:12 -0400 Subject: [PATCH 05/16] imp(l10n_do_banks): validations for pre-drop-constraint-product-code script improved --- .../pre-drop-constraint-product-code.py | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py index 1645ceb64..e0aa92bf4 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py @@ -8,9 +8,10 @@ def migrate(cr, version): """ Pre-migration script to drop the unique constraint on product_product.default_code. - + This script removes the constraint 'product_product_default_code_uniq' from the - product_product table to allow duplicate default codes. + product_product table, but only when the module 'product_code_unique' has been + uninstalled and there is no data in `product_product.default_code`. Args: cr (cursor): Database cursor @@ -19,6 +20,39 @@ def migrate(cr, version): _logger.info('Starting pre-migration: dropping product_product.default_code unique constraint') try: + cr.execute( + """ + SELECT state + FROM ir_module_module + WHERE name = 'product_code_unique' + ORDER BY id DESC + LIMIT 1 + """ + ) + module_row = cr.fetchone() + + if module_row and module_row[0] not in ('uninstalled', 'uninstallable'): + _logger.info( + "Skipping constraint drop because module product_code_unique is not uninstalled" + ) + return + + cr.execute( + """ + SELECT COUNT(*) + FROM product_product + WHERE COALESCE(default_code, '') != '' + """ + ) + default_code_count = cr.fetchone()[0] + + if default_code_count > 0: + _logger.info( + "Skipping constraint drop because %s products have default_code set", + default_code_count, + ) + return + # Check if constraint exists before attempting to drop it cr.execute(""" SELECT 1 From 609f01eef6d6ebe6088299be500a0bda9ed2f0df Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Fri, 7 Nov 2025 14:12:12 -0400 Subject: [PATCH 06/16] imp(l10n_do_banks): validations for pre-drop-constraint-product-code script improved --- .../pre-drop-constraint-product-code.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py index e0aa92bf4..8705c8faf 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import logging +from odoo.addons.base.maintenance.migrations import util _logger = logging.getLogger(__name__) @@ -13,12 +14,38 @@ def migrate(cr, version): product_product table, but only when the module 'product_code_unique' has been uninstalled and there is no data in `product_product.default_code`. + The constraint is only dropped if: + - The module 'product_code_unique' is installed + - There are products without code in the default_code field + Args: cr (cursor): Database cursor version (str): Module version """ _logger.info('Starting pre-migration: dropping product_product.default_code unique constraint') + # Check if module product_code_unique is installed + if not util.module_installed(cr, 'product_code_unique'): + _logger.info('Module product_code_unique is not installed. Skipping migration.') + return + + _logger.info('Module product_code_unique is installed. Checking for products without code.') + + # Check if there are products without code in default_code + cr.execute(""" + SELECT COUNT(*) + FROM product_product + WHERE default_code IS NULL OR default_code = '' + """) + + products_without_code = cr.fetchone()[0] + + if products_without_code == 0: + _logger.info('No products found without code in default_code. Skipping constraint drop.') + return + + _logger.info(f'Found {products_without_code} products without code. Proceeding to drop constraint.') + try: cr.execute( """ From 15b1db98282e4aa6eaf62c574c844dd768ac5dfa Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Fri, 7 Nov 2025 14:44:10 -0400 Subject: [PATCH 07/16] fix(l10n_do_banks): validations for module product_code_unique as installed for pre-drop-constraint-product-code script fix --- .../pre-drop-constraint-product-code.py | 52 ++++--------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py index 8705c8faf..5f598f714 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -import logging from odoo.addons.base.maintenance.migrations import util +import logging _logger = logging.getLogger(__name__) @@ -9,12 +9,11 @@ def migrate(cr, version): """ Pre-migration script to drop the unique constraint on product_product.default_code. - + This script removes the constraint 'product_product_default_code_uniq' from the - product_product table, but only when the module 'product_code_unique' has been - uninstalled and there is no data in `product_product.default_code`. + product_product table to allow duplicate default codes. - The constraint is only dropped if: + This script only runs when: - The module 'product_code_unique' is installed - There are products without code in the default_code field @@ -24,12 +23,12 @@ def migrate(cr, version): """ _logger.info('Starting pre-migration: dropping product_product.default_code unique constraint') - # Check if module product_code_unique is installed + # Check if product_code_unique module is installed if not util.module_installed(cr, 'product_code_unique'): - _logger.info('Module product_code_unique is not installed. Skipping migration.') + _logger.info('Module product_code_unique is not installed. Skipping constraint drop.') return - _logger.info('Module product_code_unique is installed. Checking for products without code.') + _logger.info('Module product_code_unique is installed. Checking for products without code...') # Check if there are products without code in default_code cr.execute(""" @@ -41,45 +40,12 @@ def migrate(cr, version): products_without_code = cr.fetchone()[0] if products_without_code == 0: - _logger.info('No products found without code in default_code. Skipping constraint drop.') + _logger.info('No products without code found. Skipping constraint drop.') return - _logger.info(f'Found {products_without_code} products without code. Proceeding to drop constraint.') + _logger.info(f'Found {products_without_code} products without code. Proceeding with constraint drop.') try: - cr.execute( - """ - SELECT state - FROM ir_module_module - WHERE name = 'product_code_unique' - ORDER BY id DESC - LIMIT 1 - """ - ) - module_row = cr.fetchone() - - if module_row and module_row[0] not in ('uninstalled', 'uninstallable'): - _logger.info( - "Skipping constraint drop because module product_code_unique is not uninstalled" - ) - return - - cr.execute( - """ - SELECT COUNT(*) - FROM product_product - WHERE COALESCE(default_code, '') != '' - """ - ) - default_code_count = cr.fetchone()[0] - - if default_code_count > 0: - _logger.info( - "Skipping constraint drop because %s products have default_code set", - default_code_count, - ) - return - # Check if constraint exists before attempting to drop it cr.execute(""" SELECT 1 From c04502f4cbc0f28f4c951e1e5c9b295d4e922aad Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Fri, 7 Nov 2025 14:52:59 -0400 Subject: [PATCH 08/16] fix(l10n_do_banks): validations for pre-drop-constraint-product-code script fixed as AI suggestions --- .../pre-drop-constraint-product-code.py | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py index 5f598f714..a83d5d954 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py @@ -45,23 +45,33 @@ def migrate(cr, version): _logger.info(f'Found {products_without_code} products without code. Proceeding with constraint drop.') + # Also check if there are potential conflicts (products with NULL that would conflict + # when modules try to assign default codes like '/') + cr.execute(""" + SELECT COUNT(*) + FROM product_product + WHERE default_code IS NULL + AND EXISTS ( + SELECT 1 + FROM product_product pp2 + WHERE pp2.default_code = '/' + ) + """) + + potential_conflicts = cr.fetchone()[0] + + if potential_conflicts > 0: + _logger.info(f'Found {potential_conflicts} products with NULL default_code that would conflict with existing "/" codes. This confirms constraint drop is needed.') + try: - # Check if constraint exists before attempting to drop it + # Drop the constraint if it exists + # Using IF EXISTS to avoid errors if constraint doesn't exist + _logger.info('Dropping constraint product_product_default_code_uniq') cr.execute(""" - SELECT 1 - FROM pg_constraint - WHERE conname = 'product_product_default_code_uniq' + ALTER TABLE product_product + DROP CONSTRAINT IF EXISTS product_product_default_code_uniq """) - - if cr.rowcount > 0: - _logger.info('Dropping constraint product_product_default_code_uniq') - cr.execute(""" - ALTER TABLE product_product - DROP CONSTRAINT product_product_default_code_uniq - """) - _logger.info('Successfully dropped constraint product_product_default_code_uniq') - else: - _logger.info('Constraint product_product_default_code_uniq does not exist, skipping') + _logger.info('Successfully dropped constraint product_product_default_code_uniq (or it did not exist)') except Exception as e: _logger.error(f'Error dropping constraint product_product_default_code_uniq: {e}') From 8b91dfcc76884b60dd277eb61cf195cb6f4cf0b8 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Fri, 7 Nov 2025 16:29:15 -0400 Subject: [PATCH 09/16] add(l10n_do_banks): verifications pre-drop-constraint-product-code script added --- .../pre-drop-constraint-product-code.py | 137 +++++++++++++++++- 1 file changed, 133 insertions(+), 4 deletions(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py index a83d5d954..397743382 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py @@ -21,14 +21,18 @@ def migrate(cr, version): cr (cursor): Database cursor version (str): Module version """ + _logger.info('=' * 80) + _logger.info('PRE-MIGRATION SCRIPT: pre-drop-constraint-product-code.py') _logger.info('Starting pre-migration: dropping product_product.default_code unique constraint') + _logger.info('=' * 80) # Check if product_code_unique module is installed + _logger.info('Checking if product_code_unique module is installed...') if not util.module_installed(cr, 'product_code_unique'): _logger.info('Module product_code_unique is not installed. Skipping constraint drop.') return - _logger.info('Module product_code_unique is installed. Checking for products without code...') + _logger.info('✓ Module product_code_unique is installed. Checking for products without code...') # Check if there are products without code in default_code cr.execute(""" @@ -63,19 +67,144 @@ def migrate(cr, version): if potential_conflicts > 0: _logger.info(f'Found {potential_conflicts} products with NULL default_code that would conflict with existing "/" codes. This confirms constraint drop is needed.') + # First, verify the constraint exists and get its details try: + # Try the most reliable method: join with pg_class + cr.execute(""" + SELECT c.conname, c.contype + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + JOIN pg_namespace n ON t.relnamespace = n.oid + WHERE c.conname = 'product_product_default_code_uniq' + AND t.relname = 'product_product' + AND n.nspname = 'public' + """) + constraint_info = cr.fetchone() + + if constraint_info: + _logger.info(f'Found constraint: {constraint_info[0]} (type: {constraint_info[1]})') + else: + # Try without schema restriction + cr.execute(""" + SELECT c.conname, c.contype + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + WHERE c.conname = 'product_product_default_code_uniq' + AND t.relname = 'product_product' + """) + constraint_info = cr.fetchone() + if constraint_info: + _logger.info(f'Found constraint (without schema filter): {constraint_info[0]} (type: {constraint_info[1]})') + else: + # Last attempt: simple search by name + cr.execute(""" + SELECT conname + FROM pg_constraint + WHERE conname = 'product_product_default_code_uniq' + """) + constraint_info = cr.fetchone() + if constraint_info: + _logger.info(f'Found constraint (simple search): {constraint_info[0]}') + else: + _logger.warning('Constraint product_product_default_code_uniq not found in database. It may have been already dropped.') + # Drop the constraint if it exists - # Using IF EXISTS to avoid errors if constraint doesn't exist - _logger.info('Dropping constraint product_product_default_code_uniq') + _logger.info('Attempting to drop constraint product_product_default_code_uniq') + + # First attempt: DROP CONSTRAINT IF EXISTS cr.execute(""" ALTER TABLE product_product DROP CONSTRAINT IF EXISTS product_product_default_code_uniq """) - _logger.info('Successfully dropped constraint product_product_default_code_uniq (or it did not exist)') + _logger.info('Executed: DROP CONSTRAINT IF EXISTS product_product_default_code_uniq') + + # Verify the constraint was actually dropped + cr.execute(""" + SELECT 1 + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + WHERE c.conname = 'product_product_default_code_uniq' + AND t.relname = 'product_product' + """) + still_exists = cr.fetchone() + + if still_exists: + _logger.warning('Constraint still exists after first DROP attempt. Trying with explicit name (no IF EXISTS)...') + # Try without IF EXISTS in case there's an issue with the syntax + try: + cr.execute(""" + ALTER TABLE product_product + DROP CONSTRAINT product_product_default_code_uniq + """) + _logger.info('Executed: DROP CONSTRAINT product_product_default_code_uniq (without IF EXISTS)') + except Exception as e1: + _logger.warning(f'First explicit DROP failed: {e1}. Trying with CASCADE...') + # Try with CASCADE in case there are dependencies + try: + cr.execute(""" + ALTER TABLE product_product + DROP CONSTRAINT product_product_default_code_uniq CASCADE + """) + _logger.info('Executed: DROP CONSTRAINT product_product_default_code_uniq CASCADE') + except Exception as e2: + _logger.error(f'All DROP attempts failed. Last error: {e2}') + raise + + # Final verification after all attempts + cr.execute(""" + SELECT 1 + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + WHERE c.conname = 'product_product_default_code_uniq' + AND t.relname = 'product_product' + """) + final_check = cr.fetchone() + + if final_check: + _logger.error('=' * 80) + _logger.error('CRITICAL: Constraint still exists after all DROP attempts!') + _logger.error('This may indicate a transaction rollback or permission issue.') + _logger.error('=' * 80) + # Don't raise here, just log the error so the migration can continue + else: + _logger.info('✓ Successfully verified: constraint product_product_default_code_uniq has been dropped') + + # Commit the transaction explicitly + try: + cr.commit() + _logger.info('✓ Transaction committed successfully') + except Exception as commit_error: + _logger.error(f'Error committing transaction: {commit_error}') + # Some Odoo versions handle commits automatically, so this might not be an error + _logger.info('Note: Some Odoo versions handle commits automatically') except Exception as e: _logger.error(f'Error dropping constraint product_product_default_code_uniq: {e}') + _logger.error(f'Exception type: {type(e).__name__}') + import traceback + _logger.error(traceback.format_exc()) raise + # Final diagnostic check + _logger.info('=' * 80) + _logger.info('FINAL DIAGNOSTIC CHECK:') + try: + cr.execute(""" + SELECT c.conname, c.contype + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + WHERE c.conname = 'product_product_default_code_uniq' + AND t.relname = 'product_product' + """) + final_constraint = cr.fetchone() + if final_constraint: + _logger.warning(f'⚠️ Constraint still exists: {final_constraint[0]} (type: {final_constraint[1]})') + _logger.warning('This may require manual intervention or the constraint may be recreated by another module.') + else: + _logger.info('✓ Constraint successfully removed - verification confirmed') + except Exception as diag_error: + _logger.warning(f'Could not perform final diagnostic check: {diag_error}') + + _logger.info('=' * 80) _logger.info('Finished pre-migration: dropping product_product.default_code unique constraint') From 311b4d00be23cf0fccfb94e2c459cd7f5784c3c8 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Thu, 13 Nov 2025 10:58:08 -0400 Subject: [PATCH 10/16] imp(product_sequence): adding product_sequence directory with end-migration.py and post-remove_constraint.py scripts --- src/l10n_do_banks/17.0.1.0.0/end-migration.py | 24 +- .../pre-drop-constraint-product-code.py | 210 ------------------ .../17.0.1.0.0/end-migration.py | 47 ++++ .../17.0.1.0.0/post-remove_constraint.py | 41 ++++ 4 files changed, 102 insertions(+), 220 deletions(-) delete mode 100644 src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py create mode 100644 src/product_sequence/17.0.1.0.0/end-migration.py create mode 100644 src/product_sequence/17.0.1.0.0/post-remove_constraint.py diff --git a/src/l10n_do_banks/17.0.1.0.0/end-migration.py b/src/l10n_do_banks/17.0.1.0.0/end-migration.py index 8ae9e6241..9677aad55 100644 --- a/src/l10n_do_banks/17.0.1.0.0/end-migration.py +++ b/src/l10n_do_banks/17.0.1.0.0/end-migration.py @@ -15,6 +15,7 @@ def delete_advanced_web_domain_widget_assets(cr): asset.unlink() _logger.info("Advanced Web Domain Widget assets deleted") + def delete_ks_dashboard_ninja_assets(cr): """ Script to delete ks_dashboard_ninja assets. @@ -25,6 +26,7 @@ def delete_ks_dashboard_ninja_assets(cr): asset.unlink() _logger.info("Dashboard Ninja assets deleted") + def delete_report_xlsx_assets(cr): """ Script to delete report_xlsx assets. @@ -35,6 +37,7 @@ def delete_report_xlsx_assets(cr): asset.unlink() _logger.info("Report xlsx assets deleted") + def delete_web_m2x_options_assets(cr): """ Script to delete web_m2x_options assets. @@ -45,6 +48,7 @@ def delete_web_m2x_options_assets(cr): asset.unlink() _logger.info("Web m2x options assets deleted") + def deactivate_studio_views(cr): """ Script de post-migración para buscar y desactivar vistas de Studio. @@ -77,23 +81,24 @@ def deactivate_studio_views(cr): for view in studio_views: try: view_obj = env['ir.ui.view'].browse(view['id']) - + inherited_views = env['ir.ui.view'].search([ ('inherit_id', '=', view_obj.id), ('active', '=', True) ]) - + for inherited in inherited_views: inherited.write({'active': False, 'inherit_id': False}) _logger.info(f"Inherited View Deactivated: {inherited.name} (ID: {inherited.id})") - + view_obj.write({'active': False, 'inherit_id': False}) - _logger.info(f"View Deactivated and Inherited Deactivated: {view['name']} (ID: {view['id']})") + _logger.info(f"View Deactivated and Inherited Deactivated: {view['name']} (ID: {view['id']})") env.cr.commit() - + except Exception as e: _logger.warning(f"Error deactivating view {view['name']} (ID: {view['id']}): {e}") - + + def deactivate_automated_actions(cr): """ Script on end-migration to deactivate automated actions. @@ -103,10 +108,10 @@ def deactivate_automated_actions(cr): """ env = api.Environment(cr, SUPERUSER_ID, {}) - + # Get all active automated actions automated_actions = env['base.automation'].search([('active', '=', True)]) - + # Deactivate each automated action for action in automated_actions: try: @@ -114,11 +119,10 @@ def deactivate_automated_actions(cr): _logger.info(f"Automated action deactivated: {action.name} (ID: {action.id})") except Exception as e: _logger.error(f"Error deactivating automated action {action.name} (ID: {action.id}): {e}") - - def migrate(cr, version): delete_advanced_web_domain_widget_assets(cr) deactivate_studio_views(cr) deactivate_automated_actions(cr) + diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py b/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py deleted file mode 100644 index 397743382..000000000 --- a/src/l10n_do_banks/17.0.1.0.0/pre-drop-constraint-product-code.py +++ /dev/null @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- - -from odoo.addons.base.maintenance.migrations import util -import logging - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - """ - Pre-migration script to drop the unique constraint on product_product.default_code. - - This script removes the constraint 'product_product_default_code_uniq' from the - product_product table to allow duplicate default codes. - - This script only runs when: - - The module 'product_code_unique' is installed - - There are products without code in the default_code field - - Args: - cr (cursor): Database cursor - version (str): Module version - """ - _logger.info('=' * 80) - _logger.info('PRE-MIGRATION SCRIPT: pre-drop-constraint-product-code.py') - _logger.info('Starting pre-migration: dropping product_product.default_code unique constraint') - _logger.info('=' * 80) - - # Check if product_code_unique module is installed - _logger.info('Checking if product_code_unique module is installed...') - if not util.module_installed(cr, 'product_code_unique'): - _logger.info('Module product_code_unique is not installed. Skipping constraint drop.') - return - - _logger.info('✓ Module product_code_unique is installed. Checking for products without code...') - - # Check if there are products without code in default_code - cr.execute(""" - SELECT COUNT(*) - FROM product_product - WHERE default_code IS NULL OR default_code = '' - """) - - products_without_code = cr.fetchone()[0] - - if products_without_code == 0: - _logger.info('No products without code found. Skipping constraint drop.') - return - - _logger.info(f'Found {products_without_code} products without code. Proceeding with constraint drop.') - - # Also check if there are potential conflicts (products with NULL that would conflict - # when modules try to assign default codes like '/') - cr.execute(""" - SELECT COUNT(*) - FROM product_product - WHERE default_code IS NULL - AND EXISTS ( - SELECT 1 - FROM product_product pp2 - WHERE pp2.default_code = '/' - ) - """) - - potential_conflicts = cr.fetchone()[0] - - if potential_conflicts > 0: - _logger.info(f'Found {potential_conflicts} products with NULL default_code that would conflict with existing "/" codes. This confirms constraint drop is needed.') - - # First, verify the constraint exists and get its details - try: - # Try the most reliable method: join with pg_class - cr.execute(""" - SELECT c.conname, c.contype - FROM pg_constraint c - JOIN pg_class t ON c.conrelid = t.oid - JOIN pg_namespace n ON t.relnamespace = n.oid - WHERE c.conname = 'product_product_default_code_uniq' - AND t.relname = 'product_product' - AND n.nspname = 'public' - """) - constraint_info = cr.fetchone() - - if constraint_info: - _logger.info(f'Found constraint: {constraint_info[0]} (type: {constraint_info[1]})') - else: - # Try without schema restriction - cr.execute(""" - SELECT c.conname, c.contype - FROM pg_constraint c - JOIN pg_class t ON c.conrelid = t.oid - WHERE c.conname = 'product_product_default_code_uniq' - AND t.relname = 'product_product' - """) - constraint_info = cr.fetchone() - if constraint_info: - _logger.info(f'Found constraint (without schema filter): {constraint_info[0]} (type: {constraint_info[1]})') - else: - # Last attempt: simple search by name - cr.execute(""" - SELECT conname - FROM pg_constraint - WHERE conname = 'product_product_default_code_uniq' - """) - constraint_info = cr.fetchone() - if constraint_info: - _logger.info(f'Found constraint (simple search): {constraint_info[0]}') - else: - _logger.warning('Constraint product_product_default_code_uniq not found in database. It may have been already dropped.') - - # Drop the constraint if it exists - _logger.info('Attempting to drop constraint product_product_default_code_uniq') - - # First attempt: DROP CONSTRAINT IF EXISTS - cr.execute(""" - ALTER TABLE product_product - DROP CONSTRAINT IF EXISTS product_product_default_code_uniq - """) - _logger.info('Executed: DROP CONSTRAINT IF EXISTS product_product_default_code_uniq') - - # Verify the constraint was actually dropped - cr.execute(""" - SELECT 1 - FROM pg_constraint c - JOIN pg_class t ON c.conrelid = t.oid - WHERE c.conname = 'product_product_default_code_uniq' - AND t.relname = 'product_product' - """) - still_exists = cr.fetchone() - - if still_exists: - _logger.warning('Constraint still exists after first DROP attempt. Trying with explicit name (no IF EXISTS)...') - # Try without IF EXISTS in case there's an issue with the syntax - try: - cr.execute(""" - ALTER TABLE product_product - DROP CONSTRAINT product_product_default_code_uniq - """) - _logger.info('Executed: DROP CONSTRAINT product_product_default_code_uniq (without IF EXISTS)') - except Exception as e1: - _logger.warning(f'First explicit DROP failed: {e1}. Trying with CASCADE...') - # Try with CASCADE in case there are dependencies - try: - cr.execute(""" - ALTER TABLE product_product - DROP CONSTRAINT product_product_default_code_uniq CASCADE - """) - _logger.info('Executed: DROP CONSTRAINT product_product_default_code_uniq CASCADE') - except Exception as e2: - _logger.error(f'All DROP attempts failed. Last error: {e2}') - raise - - # Final verification after all attempts - cr.execute(""" - SELECT 1 - FROM pg_constraint c - JOIN pg_class t ON c.conrelid = t.oid - WHERE c.conname = 'product_product_default_code_uniq' - AND t.relname = 'product_product' - """) - final_check = cr.fetchone() - - if final_check: - _logger.error('=' * 80) - _logger.error('CRITICAL: Constraint still exists after all DROP attempts!') - _logger.error('This may indicate a transaction rollback or permission issue.') - _logger.error('=' * 80) - # Don't raise here, just log the error so the migration can continue - else: - _logger.info('✓ Successfully verified: constraint product_product_default_code_uniq has been dropped') - - # Commit the transaction explicitly - try: - cr.commit() - _logger.info('✓ Transaction committed successfully') - except Exception as commit_error: - _logger.error(f'Error committing transaction: {commit_error}') - # Some Odoo versions handle commits automatically, so this might not be an error - _logger.info('Note: Some Odoo versions handle commits automatically') - - except Exception as e: - _logger.error(f'Error dropping constraint product_product_default_code_uniq: {e}') - _logger.error(f'Exception type: {type(e).__name__}') - import traceback - _logger.error(traceback.format_exc()) - raise - - # Final diagnostic check - _logger.info('=' * 80) - _logger.info('FINAL DIAGNOSTIC CHECK:') - try: - cr.execute(""" - SELECT c.conname, c.contype - FROM pg_constraint c - JOIN pg_class t ON c.conrelid = t.oid - WHERE c.conname = 'product_product_default_code_uniq' - AND t.relname = 'product_product' - """) - final_constraint = cr.fetchone() - if final_constraint: - _logger.warning(f'⚠️ Constraint still exists: {final_constraint[0]} (type: {final_constraint[1]})') - _logger.warning('This may require manual intervention or the constraint may be recreated by another module.') - else: - _logger.info('✓ Constraint successfully removed - verification confirmed') - except Exception as diag_error: - _logger.warning(f'Could not perform final diagnostic check: {diag_error}') - - _logger.info('=' * 80) - _logger.info('Finished pre-migration: dropping product_product.default_code unique constraint') - diff --git a/src/product_sequence/17.0.1.0.0/end-migration.py b/src/product_sequence/17.0.1.0.0/end-migration.py new file mode 100644 index 000000000..ce2dd8eb6 --- /dev/null +++ b/src/product_sequence/17.0.1.0.0/end-migration.py @@ -0,0 +1,47 @@ +import logging + +_logger = logging.getLogger(__name__) + +CONSTRAINT_NAME = 'product_product_default_code_uniq' +TABLE_NAME = 'product_product' +CONSTRAINT_EXISTS_SQL = """ + SELECT 1 + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + JOIN pg_namespace n ON t.relnamespace = n.oid + WHERE c.conname = %s AND t.relname = %s AND n.nspname = 'public' +""" +DUPLICATES_SQL = """ + SELECT default_code, COUNT(*) AS qty + FROM product_product + WHERE default_code IS NOT NULL AND default_code != '' + GROUP BY default_code + HAVING COUNT(*) > 1 +""" + + +def migrate(cr, version): + """Recreate the unique constraint on product_product.default_code.""" + _logger.info('=' * 80) + _logger.info('POST-MIGRATION: Restoring %s on %s.default_code', CONSTRAINT_NAME, TABLE_NAME) + _logger.info('=' * 80) + + cr.execute(CONSTRAINT_EXISTS_SQL, (CONSTRAINT_NAME, TABLE_NAME)) + if cr.fetchone(): + _logger.info('Constraint %s already exists. Nothing to do.', CONSTRAINT_NAME) + return + + cr.execute(DUPLICATES_SQL) + duplicates = cr.fetchall() + if duplicates: + sample = ', '.join(f'"{code}" ({qty}x)' for code, qty in duplicates[:5]) + if len(duplicates) > 5: + sample += f', ... (+{len(duplicates) - 5})' + _logger.error('Cannot recreate %s; duplicate default_code values detected: %s', CONSTRAINT_NAME, sample) + raise Exception('Resolve duplicate product_product.default_code values before rerunning the migration.') + + _logger.info('Dropping constraint %s (if it exists) and recreating it.', CONSTRAINT_NAME) + cr.execute(f"ALTER TABLE {TABLE_NAME} DROP CONSTRAINT IF EXISTS {CONSTRAINT_NAME}") + cr.execute(f"ALTER TABLE {TABLE_NAME} ADD CONSTRAINT {CONSTRAINT_NAME} UNIQUE (default_code)") + _logger.info('Constraint %s restored successfully.', CONSTRAINT_NAME) + diff --git a/src/product_sequence/17.0.1.0.0/post-remove_constraint.py b/src/product_sequence/17.0.1.0.0/post-remove_constraint.py new file mode 100644 index 000000000..b7c738180 --- /dev/null +++ b/src/product_sequence/17.0.1.0.0/post-remove_constraint.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from odoo.addons.base.maintenance.migrations import util +import logging + +_logger = logging.getLogger(__name__) + +CONSTRAINT_NAME = 'product_product_default_code_uniq' + + +def migrate(cr, version): + """Drop the product default_code unique constraint when needed.""" + _logger.info('=' * 80) + _logger.info('PRE-MIGRATION: Dropping %s constraint', CONSTRAINT_NAME) + _logger.info('=' * 80) + + if not util.module_installed(cr, 'product_code_unique'): + _logger.info('Module product_code_unique is not installed. Nothing to do.') + return + + cr.execute( + """ + SELECT COUNT(*) + FROM product_product + WHERE default_code IS NULL OR default_code = '' + """ + ) + missing_codes = cr.fetchone()[0] + + if not missing_codes: + _logger.info('No products without default_code found. Skipping constraint drop.') + return + + _logger.info('Dropping constraint %s to allow temporary duplicates.', CONSTRAINT_NAME) + cr.execute( + """ + ALTER TABLE product_product + DROP CONSTRAINT IF EXISTS product_product_default_code_uniq + """ + ) + _logger.info('Constraint %s dropped (if it existed).', CONSTRAINT_NAME) From f730d4c21235f4aeac0cd8ee89a559c48c5fef04 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Thu, 13 Nov 2025 11:18:13 -0400 Subject: [PATCH 11/16] imp(product_sequence): loggers not needed deleted --- src/product_sequence/17.0.1.0.0/end-migration.py | 2 -- src/product_sequence/17.0.1.0.0/post-remove_constraint.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/product_sequence/17.0.1.0.0/end-migration.py b/src/product_sequence/17.0.1.0.0/end-migration.py index ce2dd8eb6..cf15d30ba 100644 --- a/src/product_sequence/17.0.1.0.0/end-migration.py +++ b/src/product_sequence/17.0.1.0.0/end-migration.py @@ -22,9 +22,7 @@ def migrate(cr, version): """Recreate the unique constraint on product_product.default_code.""" - _logger.info('=' * 80) _logger.info('POST-MIGRATION: Restoring %s on %s.default_code', CONSTRAINT_NAME, TABLE_NAME) - _logger.info('=' * 80) cr.execute(CONSTRAINT_EXISTS_SQL, (CONSTRAINT_NAME, TABLE_NAME)) if cr.fetchone(): diff --git a/src/product_sequence/17.0.1.0.0/post-remove_constraint.py b/src/product_sequence/17.0.1.0.0/post-remove_constraint.py index b7c738180..79e0d7017 100644 --- a/src/product_sequence/17.0.1.0.0/post-remove_constraint.py +++ b/src/product_sequence/17.0.1.0.0/post-remove_constraint.py @@ -10,9 +10,7 @@ def migrate(cr, version): """Drop the product default_code unique constraint when needed.""" - _logger.info('=' * 80) _logger.info('PRE-MIGRATION: Dropping %s constraint', CONSTRAINT_NAME) - _logger.info('=' * 80) if not util.module_installed(cr, 'product_code_unique'): _logger.info('Module product_code_unique is not installed. Nothing to do.') From edc38c94b6e2d211b3d11fb4e887dc5a61e8f85d Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Thu, 13 Nov 2025 11:24:01 -0400 Subject: [PATCH 12/16] imp(product_sequence): pre-remove_constraint.py replaced by post-remove_constraint.py --- .../{post-remove_constraint.py => pre-remove_constraint.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/product_sequence/17.0.1.0.0/{post-remove_constraint.py => pre-remove_constraint.py} (100%) diff --git a/src/product_sequence/17.0.1.0.0/post-remove_constraint.py b/src/product_sequence/17.0.1.0.0/pre-remove_constraint.py similarity index 100% rename from src/product_sequence/17.0.1.0.0/post-remove_constraint.py rename to src/product_sequence/17.0.1.0.0/pre-remove_constraint.py From baf3efc8edc4f011a01efce91c48cc19568b677f Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Wed, 3 Dec 2025 10:24:53 -0400 Subject: [PATCH 13/16] imp (upgrade-utils): modules added to uninstall list not needed for Luminatti v17 --- .../17.0.1.0.0/pre-modules-uninstall.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py b/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py index d3196e896..933e14472 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py @@ -87,6 +87,27 @@ def uninstall_modules(cr): 'product_warehouse_quantity', 'sale_discount_limit', 'sales_product_warehouse_quantity', + 'account_invoice_migration_scripts', + 'alan_customize', + 'config_interface', + 'database_cleanup', + 'dev_sale_product_stock_restrict', + 'interface_invoicing', + 'ncf_sale', + 'negative_stock_sale', + 'non_moving_product_ept', + 'payment_backend_refund', + 'product_hide_sale_cost_price', + 'protocol_message', + 'qztray', + 'qztray_base', + 'qztray_location_labels', + 'qztray_partner_labels', + 'qztray_product_inventory', + 'qztray_product_labels', + 'qztray_product_purchase', + 'required_requested_date', + 'stock_inventory_chatter' ] for module_name in modules_to_uninstall: From ffc42a2238bed59f9541361797d69aee8b72e7c9 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Wed, 3 Dec 2025 10:39:56 -0400 Subject: [PATCH 14/16] imp (upgrade-utils): function added to clean qztray module data --- .../17.0.1.0.0/pre-modules-uninstall.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py b/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py index 933e14472..d9191b2de 100644 --- a/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py +++ b/src/l10n_do_banks/17.0.1.0.0/pre-modules-uninstall.py @@ -5,6 +5,47 @@ _logger = logging.getLogger(__name__) +def _cleanup_qztray_data(cr): + """Remove qztray printer records that block keypair deletion. + + Some databases have a NOT NULL constraint on ``qztray_printer.keypair_id`` + while the foreign key uses ``ON DELETE SET NULL``. When uninstalling the + qztray modules, the upgrade utility tries to delete records from + ``qztray_keypair``, which triggers ``SET NULL`` on ``qztray_printer`` and + crashes on the NOT NULL constraint. + + We proactively delete the printer records that reference any keypair so the + module uninstall can proceed. + """ + # Solo ejecutamos esta limpieza si el módulo qztray_printer (o qztray) + # está instalado en la base de datos. + if not ( + util.module_installed(cr, "qztray_printer") + or util.module_installed(cr, "qztray") + ): + _logger.debug( + "qztray_printer/qztray no instalados; se omite la limpieza previa." + ) + return + + try: + cr.execute( + """ + DELETE FROM qztray_printer + WHERE keypair_id IN (SELECT id FROM qztray_keypair) + """ + ) + _logger.info("Deleted qztray_printer records referencing qztray_keypair.") + except Exception: + # If tables don't exist or deletion fails, ignore and let normal + # uninstall logic handle it. + _logger.debug( + "qztray_printer/qztray_keypair tables not found or deletion failed, " + "continuing with module uninstall.", + exc_info=True, + ) + + def uninstall_modules(cr): """ Script to uninstall modules that are no longer needed or compatible with version 17.0. @@ -117,4 +158,5 @@ def uninstall_modules(cr): def migrate(cr, version): + _cleanup_qztray_data(cr) uninstall_modules(cr) From eeffe7325f36f9100fc6c171b0ecb1c835ceb247 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Wed, 3 Dec 2025 11:05:36 -0400 Subject: [PATCH 15/16] imp(upgrade_utils): delete access function improved and new assets to delete added --- src/l10n_do_banks/17.0.1.0.0/end-migration.py | 67 ++++++++----------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/end-migration.py b/src/l10n_do_banks/17.0.1.0.0/end-migration.py index 9677aad55..4b14ff7be 100644 --- a/src/l10n_do_banks/17.0.1.0.0/end-migration.py +++ b/src/l10n_do_banks/17.0.1.0.0/end-migration.py @@ -5,48 +5,39 @@ _logger = logging.getLogger(__name__) -def delete_advanced_web_domain_widget_assets(cr): +def delete_custom_assets(cr): """ - Script to delete advanced_web_domain_widget assets. - """ - env = api.Environment(cr, SUPERUSER_ID, {}) - assets = env['ir.asset'].search([('name', 'like', 'advanced_web_domain_widget.assets_backend%')]) - for asset in assets: - asset.unlink() - _logger.info("Advanced Web Domain Widget assets deleted") + Script to delete several custom backend assets: + - advanced_web_domain_widget + - ks_dashboard_ninja + - report_xlsx + - web_m2x_options + - alan_customize + - qztray_base + - ncf_manager + - interface_invoicing + - protocol_message - -def delete_ks_dashboard_ninja_assets(cr): - """ - Script to delete ks_dashboard_ninja assets. """ env = api.Environment(cr, SUPERUSER_ID, {}) - assets = env['ir.asset'].search([('name', 'like', 'ks_dashboard_ninja.assets_backend%')]) - for asset in assets: - asset.unlink() - _logger.info("Dashboard Ninja assets deleted") - -def delete_report_xlsx_assets(cr): - """ - Script to delete report_xlsx assets. - """ - env = api.Environment(cr, SUPERUSER_ID, {}) - assets = env['ir.asset'].search([('name', 'like', 'report_xlsx.assets_backend%')]) - for asset in assets: - asset.unlink() - _logger.info("Report xlsx assets deleted") - - -def delete_web_m2x_options_assets(cr): - """ - Script to delete web_m2x_options assets. - """ - env = api.Environment(cr, SUPERUSER_ID, {}) - assets = env['ir.asset'].search([('name', 'like', 'web_m2x_options.assets_backend%')]) - for asset in assets: - asset.unlink() - _logger.info("Web m2x options assets deleted") + assets_to_delete = [ + ('advanced_web_domain_widget.assets_backend%', "Advanced Web Domain Widget assets deleted"), + ('ks_dashboard_ninja.assets_backend%', "Dashboard Ninja assets deleted"), + ('report_xlsx.assets_backend%', "Report xlsx assets deleted"), + ('web_m2x_options.assets_backend%', "Web m2x options assets deleted"), + ('alan_customize.%', "Alan Customize assets deleted"), + ('qztray_base.assets_backend%', "QZ Tray Base assets deleted"), + ('ncf_manager.assets_backend%', "NCF Manager assets deleted"), + ('interface_invoicing.%', "Interface Invoicing assets deleted"), + ('protocol_message.%', "Protocol Message assets deleted"), + ] + + for name_pattern, log_message in assets_to_delete: + assets = env['ir.asset'].search([('name', 'like', name_pattern)]) + for asset in assets: + asset.unlink() + _logger.info(log_message) def deactivate_studio_views(cr): @@ -122,7 +113,7 @@ def deactivate_automated_actions(cr): def migrate(cr, version): - delete_advanced_web_domain_widget_assets(cr) + delete_custom_assets(cr) deactivate_studio_views(cr) deactivate_automated_actions(cr) From 12beebf5972f31098501bf220e71edca725661d9 Mon Sep 17 00:00:00 2001 From: gerald-pcg Date: Wed, 3 Dec 2025 16:08:42 -0400 Subject: [PATCH 16/16] imp(upgrade_utils): custom assets added to delete_custom_assets function --- src/l10n_do_banks/17.0.1.0.0/end-migration.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/l10n_do_banks/17.0.1.0.0/end-migration.py b/src/l10n_do_banks/17.0.1.0.0/end-migration.py index 4b14ff7be..8fb33cc74 100644 --- a/src/l10n_do_banks/17.0.1.0.0/end-migration.py +++ b/src/l10n_do_banks/17.0.1.0.0/end-migration.py @@ -17,6 +17,8 @@ def delete_custom_assets(cr): - ncf_manager - interface_invoicing - protocol_message + - dgii_reports + - l10n_do_ecommerce """ env = api.Environment(cr, SUPERUSER_ID, {}) @@ -26,11 +28,14 @@ def delete_custom_assets(cr): ('ks_dashboard_ninja.assets_backend%', "Dashboard Ninja assets deleted"), ('report_xlsx.assets_backend%', "Report xlsx assets deleted"), ('web_m2x_options.assets_backend%', "Web m2x options assets deleted"), - ('alan_customize.%', "Alan Customize assets deleted"), + ('alan_customize%', "Alan Customize assets deleted"), ('qztray_base.assets_backend%', "QZ Tray Base assets deleted"), ('ncf_manager.assets_backend%', "NCF Manager assets deleted"), ('interface_invoicing.%', "Interface Invoicing assets deleted"), ('protocol_message.%', "Protocol Message assets deleted"), + ('dgii_reports%', "DGII Reports assets deleted"), + ('l10n_do_ecommerce%', "Ecommerce assets deleted"), + ('web_editor%', "Custom assets deleted"), ] for name_pattern, log_message in assets_to_delete: