From 3653533b0b77889812ec2c3ad172534b592adb2e Mon Sep 17 00:00:00 2001 From: Don Kendall Date: Wed, 23 Sep 2026 17:06:23 -0400 Subject: [PATCH] [20.0][OU-ADD] point_of_sale: migration scripts ### post-migration.py pos.payment.method.type was computed in 19.0 and is stored in 20.0. 19.0 derived it on the fly and kept no column, so the stored column 20.0 adds starts empty against a required field -- the upgrade reports "Constraint not added: column type of relation pos_payment_method contains null values" and carries on without the constraint. The rule is 19.0's own _compute_type, which reads the journal rather than anything on the method itself:: if pm.journal_id.type in {'cash', 'bank'}: pm.type = pm.journal_id.type else: pm.type = 'pay_later' A method with no journal falls to pay_later, which is why the join is outer. Not is_cash_count: that agrees with the journal on a cash method but is a different field, and 20.0 drops it. Assisted-by: Claude Opus 5 --- docsource/modules190-200.rst | 2 +- .../20.0.1.0.2/noupdate_changes.xml | 6 + .../20.0.1.0.2/post-migration.py | 45 +++ .../20.0.1.0.2/upgrade_analysis.txt | 372 ++++++++++++++++++ .../tests/data_point_of_sale_migration.py | 45 +++ .../tests/test_point_of_sale_migration.py | 51 +++ 6 files changed, 520 insertions(+), 1 deletion(-) create mode 100644 openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/noupdate_changes.xml create mode 100644 openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/post-migration.py create mode 100644 openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/upgrade_analysis.txt create mode 100644 openupgrade_scripts/scripts/point_of_sale/tests/data_point_of_sale_migration.py create mode 100644 openupgrade_scripts/scripts/point_of_sale/tests/test_point_of_sale_migration.py diff --git a/docsource/modules190-200.rst b/docsource/modules190-200.rst index 8da46d62bbc..012c5298060 100644 --- a/docsource/modules190-200.rst +++ b/docsource/modules190-200.rst @@ -812,7 +812,7 @@ Module coverage 19.0 -> 20.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | phone_validation | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| point_of_sale | | | +| point_of_sale |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ | portal | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ diff --git a/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/noupdate_changes.xml b/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/noupdate_changes.xml new file mode 100644 index 00000000000..bf2db5d3be0 --- /dev/null +++ b/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/noupdate_changes.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/post-migration.py b/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/post-migration.py new file mode 100644 index 00000000000..5f73efa2cd6 --- /dev/null +++ b/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/post-migration.py @@ -0,0 +1,45 @@ +# Copyright 2026 Don Kendall +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + + +def _fill_payment_method_type(env): + """pos.payment.method.type was computed in 19.0 and is stored in 20.0. + + 19.0 derived it on the fly and kept no column, so the stored column 20.0 + adds starts empty against a required field -- the upgrade reports + "Constraint not added: column type of relation pos_payment_method contains + null values" and carries on without the constraint. + + The rule is 19.0's own _compute_type, which reads the journal rather than + anything on the method itself:: + + if pm.journal_id.type in {'cash', 'bank'}: + pm.type = pm.journal_id.type + else: + pm.type = 'pay_later' + + A method with no journal falls to pay_later, which is why the join is outer. + Not is_cash_count: that agrees with the journal on a cash method but is a + different field, and 20.0 drops it. + """ + openupgrade.logged_query( + env.cr, + """ + UPDATE pos_payment_method m + SET type = CASE + WHEN j.type IN ('cash', 'bank') THEN j.type + ELSE 'pay_later' + END + FROM pos_payment_method m2 + LEFT JOIN account_journal j ON j.id = m2.journal_id + WHERE m.id = m2.id AND m.type IS NULL + """, + ) + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data(env, "point_of_sale", "20.0.1.0.2/noupdate_changes.xml") + _fill_payment_method_type(env) diff --git a/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/upgrade_analysis.txt b/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/upgrade_analysis.txt new file mode 100644 index 00000000000..3480a933241 --- /dev/null +++ b/openupgrade_scripts/scripts/point_of_sale/20.0.1.0.2/upgrade_analysis.txt @@ -0,0 +1,372 @@ +---Models in module 'point_of_sale'--- +obsolete model pos.close.session.wizard [transient] +model pos.pack.operation.lot (moved to pos_stock) +new model pos.order.receipt [abstract] +new model pos.prep.line +new model pos.prep.order +new model pos.price.inclusion.wizard [transient] +new model pos.snooze +---Fields in module 'point_of_sale'--- +point_of_sale / account.bank.statement / pos_session_id (one2many) : NEW relation: pos.session +point_of_sale / account.move / pos_session_correction_id (many2one): NEW relation: pos.session +point_of_sale / account.move / pos_session_ids (one2many) : not stored anymore +point_of_sale / account.move / pos_session_ids (one2many) : now a function +point_of_sale / account.move / pos_session_refunds_id (many2one): NEW relation: pos.session +point_of_sale / account.move / pos_session_sales_id (many2one): NEW relation: pos.session +point_of_sale / account.move.line / pos_order_line_id (many2one) : NEW relation: pos.order.line +point_of_sale / pos.bill / name (char) : now required +point_of_sale / pos.category / active (boolean) : NEW hasdefault: default +point_of_sale / pos.category / complete_name (char) : NEW isfunction: function, stored +point_of_sale / pos.category / pos_config_ids (many2many) : previously in module pos_self_order +point_of_sale / pos.config / auto_validate_electronic_payment (boolean): NEW hasdefault: default +point_of_sale / pos.config / auto_validate_terminal_payment (boolean): DEL +point_of_sale / pos.config / closing_journal_id (many2one) : NEW relation: account.journal, required, hasdefault: compute +point_of_sale / pos.config / custom_email (char) : NEW +point_of_sale / pos.config / custom_logo (binary) : NEW attachment: True +point_of_sale / pos.config / custom_logo_name (char) : NEW +point_of_sale / pos.config / custom_phone (char) : NEW +point_of_sale / pos.config / custom_receipt_address (char) : NEW +point_of_sale / pos.config / custom_website (char) : NEW +point_of_sale / pos.config / default_partner_id (many2one) : NEW relation: res.partner, required, hasdefault: default +point_of_sale / pos.config / email (char) : NEW hasdefault: compute +point_of_sale / pos.config / epson_printer_ip (char) : DEL +point_of_sale / pos.config / iface_cashdrawer (boolean) : DEL +point_of_sale / pos.config / iface_electronic_scale (boolean): DEL +point_of_sale / pos.config / iface_print_skip_screen (boolean): DEL +point_of_sale / pos.config / iface_print_via_proxy (boolean): DEL +point_of_sale / pos.config / iface_printbill (boolean) : previously in module pos_restaurant +point_of_sale / pos.config / iface_scan_via_proxy (boolean): DEL +point_of_sale / pos.config / invoice_journal_id (many2one) : DEL relation: account.journal +point_of_sale / pos.config / is_closing_entry_by_product (boolean): DEL +point_of_sale / pos.config / is_header_or_footer (boolean) : DEL +point_of_sale / pos.config / is_order_printer (boolean) : DEL +point_of_sale / pos.config / is_posbox (boolean) : DEL +point_of_sale / pos.config / logo (binary) : NEW attachment: True, hasdefault: compute +point_of_sale / pos.config / name (char) : now translatable +point_of_sale / pos.config / order_edit_tracking (boolean) : DEL +point_of_sale / pos.config / phone (char) : NEW hasdefault: compute +point_of_sale / pos.config / picking_policy (selection) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.config / picking_type_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.config / pos_snooze_ids (one2many) : NEW relation: pos.snooze +point_of_sale / pos.config / preparation_devices (boolean) : NEW +point_of_sale / pos.config / preparation_printer_ids (many2many): NEW relation: pos.printer +point_of_sale / pos.config / printer_ids (many2many) : DEL relation: pos.printer +point_of_sale / pos.config / proxy_ip (char) : DEL +point_of_sale / pos.config / receipt_address (char) : NEW hasdefault: compute +point_of_sale / pos.config / receipt_printer_ids (many2many): NEW relation: pos.printer +point_of_sale / pos.config / route_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.config / session_closing_daily_hour (float): NEW hasdefault: default +point_of_sale / pos.config / session_closing_mode (selection): NEW selection_keys: ['closing', 'daily'] +point_of_sale / pos.config / set_tip_after_payment (boolean): previously in module pos_restaurant +point_of_sale / pos.config / ship_later (boolean) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.config / tip_percentage_1 (integer) : NEW hasdefault: default +point_of_sale / pos.config / tip_percentage_2 (integer) : NEW hasdefault: default +point_of_sale / pos.config / tip_percentage_3 (integer) : NEW hasdefault: default +point_of_sale / pos.config / use_closing_entry_by_product (boolean): NEW +point_of_sale / pos.config / use_custom_receipt_info (boolean): NEW hasdefault: default +point_of_sale / pos.config / use_download_invoice (boolean): NEW +point_of_sale / pos.config / use_header_or_footer (boolean): NEW +point_of_sale / pos.config / use_order_printer (boolean) : NEW +point_of_sale / pos.config / warehouse_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.config / website (char) : NEW hasdefault: compute +point_of_sale / pos.order / defer_invoice_pdf (boolean) : NEW +point_of_sale / pos.order / failed_pickings (boolean) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order / last_order_preparation_change (char): DEL +point_of_sale / pos.order / picking_count (integer) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order / picking_ids (one2many) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order / picking_type_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order / prep_order_ids (one2many) : NEW relation: pos.prep.order +point_of_sale / pos.order / print_history (json) : NEW +point_of_sale / pos.order / shipping_date (date) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order / stock_reference_ids (many2many): module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order / website_message_ids (one2many): DEL relation: mail.message +point_of_sale / pos.order.line / pack_lot_ids (one2many) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.order.line / prep_line_ids (one2many) : NEW relation: pos.prep.line +point_of_sale / pos.pack.operation.lot / _order : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.pack.operation.lot / display_name (char) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.pack.operation.lot / lot_name (char) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.pack.operation.lot / order_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.pack.operation.lot / pos_order_line_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.pack.operation.lot / product_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.payment / amount_currency (float) : NEW +point_of_sale / pos.payment / currency_rate (float) : is now stored +point_of_sale / pos.payment / currency_rate (float) : not related anymore +point_of_sale / pos.payment / foreign_currency_id (many2one): NEW relation: res.currency +point_of_sale / pos.payment / qr_code (char) : NEW +point_of_sale / pos.payment.method / account_bank_statement_id (many2one): NEW relation: account.bank.statement +point_of_sale / pos.payment.method / company_id (many2one) : now required +point_of_sale / pos.payment.method / currency_ids (many2many) : NEW relation: res.currency +point_of_sale / pos.payment.method / custom_image (binary) : NEW attachment: True +point_of_sale / pos.payment.method / image (binary) : not stored anymore +point_of_sale / pos.payment.method / image (binary) : now a function +point_of_sale / pos.payment.method / is_cash_count (boolean) : DEL +point_of_sale / pos.payment.method / payment_provider (selection) : NEW selection_keys: function +point_of_sale / pos.payment.method / split_transactions (boolean) : DEL +point_of_sale / pos.payment.method / type (selection) : is now stored +point_of_sale / pos.payment.method / type (selection) : not a function anymore +point_of_sale / pos.payment.method / type (selection) : now required +point_of_sale / pos.payment.method / use_payment_terminal (selection): DEL selection_keys: function +point_of_sale / pos.prep.line / attribute_value_ids (many2many): NEW relation: product.template.attribute.value +point_of_sale / pos.prep.line / cancelled (float) : NEW hasdefault: default +point_of_sale / pos.prep.line / combo_line_ids (one2many) : NEW relation: pos.prep.line +point_of_sale / pos.prep.line / combo_parent_id (many2one) : NEW relation: pos.prep.line, isfunction: function, stored +point_of_sale / pos.prep.line / pos_order_line_id (many2one) : NEW relation: pos.order.line +point_of_sale / pos.prep.line / prep_order_id (many2one) : NEW relation: pos.prep.order +point_of_sale / pos.prep.line / product_id (many2one) : NEW relation: product.product +point_of_sale / pos.prep.line / quantity (float) : NEW required +point_of_sale / pos.prep.line / uuid (char) : NEW +point_of_sale / pos.prep.order / pos_order_id (many2one) : NEW relation: pos.order +point_of_sale / pos.prep.order / prep_line_ids (one2many) : NEW relation: pos.prep.line +point_of_sale / pos.prep.order / uuid (char) : NEW +point_of_sale / pos.preset / service_fee (boolean) : NEW hasdefault: default +point_of_sale / pos.preset / service_fee_amount (float) : NEW hasdefault: default +point_of_sale / pos.preset / service_fee_based_on (selection): NEW selection_keys: ['post_discount', 'pre_discount'], hasdefault: default +point_of_sale / pos.preset / service_fee_product_id (many2one): NEW relation: product.product, hasdefault: default +point_of_sale / pos.preset / service_fee_type (selection) : NEW selection_keys: ['fixed', 'percent'], hasdefault: default +point_of_sale / pos.printer / company_id (many2one) : DEL relation: res.company, required +point_of_sale / pos.printer / epson_printer_ip (char) : DEL +point_of_sale / pos.printer / is_split_per_product (boolean): NEW +point_of_sale / pos.printer / paper_size (selection) : NEW required, selection_keys: ['58', '80', 'label', 'tm_l100_40', 'tm_p60_60', 'tm_u22_58', 'tm_u22_70', 'tm_u22_76', 'tm_u33_70', 'tm_u33_76'], hasdefault: default +point_of_sale / pos.printer / pos_config_ids (many2many) : table is now 'pos_config_receipt_printer_rel' ('pos_config_printer_rel') +point_of_sale / pos.printer / printer_ip (char) : NEW +point_of_sale / pos.printer / printer_type (selection) : selection_keys removed: [iot] +point_of_sale / pos.printer / proxy_ip (char) : DEL +point_of_sale / pos.printer / timeout (integer) : NEW hasdefault: default +point_of_sale / pos.printer / use_cashdrawer (boolean) : NEW +point_of_sale / pos.printer / use_lna (boolean) : NEW +point_of_sale / pos.printer / use_type (selection) : NEW selection_keys: ['preparation', 'receipt'], hasdefault: default +point_of_sale / pos.session / bank_payment_ids (one2many) : DEL relation: account.payment +point_of_sale / pos.session / bank_statement_id (many2one) : NEW relation: account.bank.statement +point_of_sale / pos.session / cash_journal_id (many2one) : DEL relation: account.journal +point_of_sale / pos.session / cash_real_transaction (float) : DEL +point_of_sale / pos.session / cash_register_balance_end_real (float): DEL +point_of_sale / pos.session / cash_register_balance_start (float): DEL +point_of_sale / pos.session / correction_move_ids (one2many): NEW relation: account.move +point_of_sale / pos.session / failed_pickings (boolean) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.session / move_id (many2one) : DEL relation: account.move +point_of_sale / pos.session / picking_count (integer) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.session / picking_ids (one2many) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.session / refund_move_ids (one2many) : NEW relation: account.move +point_of_sale / pos.session / rescue (boolean) : DEL +point_of_sale / pos.session / sale_move_ids (one2many) : NEW relation: account.move +point_of_sale / pos.session / statement_line_ids (one2many) : DEL relation: account.bank.statement.line +point_of_sale / pos.session / update_stock_at_closing (boolean): module is now 'pos_stock' ('point_of_sale') +point_of_sale / pos.session / website_message_ids (one2many): DEL relation: mail.message +point_of_sale / pos.snooze / end_time (datetime) : NEW +point_of_sale / pos.snooze / pos_config_id (many2one) : NEW relation: pos.config, required +point_of_sale / pos.snooze / product_template_id (many2one): NEW relation: product.template +point_of_sale / pos.snooze / start_time (datetime) : NEW required +point_of_sale / pos.snooze / type (selection) : NEW required, selection_keys: ['product'], hasdefault: default +point_of_sale / product.attribute.custom.value / uuid (char) : NEW +point_of_sale / product.combo / is_upsell (boolean) : NEW hasdefault: default +point_of_sale / res.company / point_of_sale_update_stock_quantities (selection): module is now 'pos_stock' ('point_of_sale') +point_of_sale / stock.picking / pos_order_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / stock.picking / pos_session_id (many2one) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / stock.picking.type / has_stock_reports_to_print (boolean): module is now 'pos_stock' ('point_of_sale') +point_of_sale / stock.reference / pos_order_ids (many2many) : module is now 'pos_stock' ('point_of_sale') +point_of_sale / stock.warehouse / pos_type_id (many2one) : module is now 'pos_stock' ('point_of_sale') +---XML records in module 'point_of_sale'--- +barcode.rule: point_of_sale.barcode_rule_cashier (noupdate switched) +barcode.rule: point_of_sale.barcode_rule_client (noupdate switched) +barcode.rule: point_of_sale.barcode_rule_discount (noupdate switched) +barcode.rule: point_of_sale.barcode_rule_price_two_dec (noupdate switched) +digest.digest: digest.digest_digest_default (noupdate switched) +NEW ir.access: point_of_sale.access_account_bank_statement +NEW ir.access: point_of_sale.access_account_bank_statement_manager +NEW ir.access: point_of_sale.access_account_cash_rounding_pos_user +NEW ir.access: point_of_sale.access_account_journal_pos_user +NEW ir.access: point_of_sale.access_account_payment_method_line_pos_user +NEW ir.access: point_of_sale.access_account_payment_method_pos_user +NEW ir.access: point_of_sale.access_barcode_nomenclature_pos_manager +NEW ir.access: point_of_sale.access_barcode_nomenclature_pos_user +NEW ir.access: point_of_sale.access_barcode_rule_pos_manager +NEW ir.access: point_of_sale.access_barcode_rule_pos_user +NEW ir.access: point_of_sale.access_decimal_precision_user +NEW ir.access: point_of_sale.access_pos_bill +NEW ir.access: point_of_sale.access_pos_config_manager +NEW ir.access: point_of_sale.access_pos_config_system_user +NEW ir.access: point_of_sale.access_pos_config_user +NEW ir.access: point_of_sale.access_pos_confirmation_wizard +NEW ir.access: point_of_sale.access_pos_daily_sales_reports_wizard +NEW ir.access: point_of_sale.access_pos_details_wizard +NEW ir.access: point_of_sale.access_pos_make_invoice +NEW ir.access: point_of_sale.access_pos_make_invoice_manager +NEW ir.access: point_of_sale.access_pos_make_payment +NEW ir.access: point_of_sale.access_pos_note +NEW ir.access: point_of_sale.access_pos_note_manager +NEW ir.access: point_of_sale.access_pos_order +NEW ir.access: point_of_sale.access_pos_order_line +NEW ir.access: point_of_sale.access_pos_payment_method_manager +NEW ir.access: point_of_sale.access_pos_payment_method_user +NEW ir.access: point_of_sale.access_pos_payment_user +NEW ir.access: point_of_sale.access_pos_preset +NEW ir.access: point_of_sale.access_pos_preset_manager +NEW ir.access: point_of_sale.access_pos_price_inclusion_wizard +NEW ir.access: point_of_sale.access_pos_printer +NEW ir.access: point_of_sale.access_pos_printer_manager +NEW ir.access: point_of_sale.access_pos_session_user +NEW ir.access: point_of_sale.access_pos_snooze +NEW ir.access: point_of_sale.access_preparation_line +NEW ir.access: point_of_sale.access_preparation_order +NEW ir.access: point_of_sale.access_product_category_pos_manager +NEW ir.access: point_of_sale.access_product_category_pos_user +NEW ir.access: point_of_sale.access_product_combo_item_pos_user +NEW ir.access: point_of_sale.access_product_combo_pos_user +NEW ir.access: point_of_sale.access_product_pricelist_manager +NEW ir.access: point_of_sale.access_product_pricelist_user +NEW ir.access: point_of_sale.access_product_product +NEW ir.access: point_of_sale.access_product_product_supplierinfo_user +NEW ir.access: point_of_sale.access_product_template_pos_user +NEW ir.access: point_of_sale.access_report_pos_order +NEW ir.access: point_of_sale.access_res_partner_pos_manager +NEW ir.access: point_of_sale.base_user_account_move_line_rule +NEW ir.access: point_of_sale.res_partner_rule_user +NEW ir.access: point_of_sale.res_partner_rule_write_self +NEW ir.access: point_of_sale.rule_invoice_pos_user +NEW ir.access: point_of_sale.rule_pos_bank_statement_line_account_user +NEW ir.access: point_of_sale.rule_pos_bank_statement_line_account_user_1 +NEW ir.access: point_of_sale.rule_pos_bank_statement_line_user +NEW ir.access: point_of_sale.rule_pos_bank_statement_line_user_1 +NEW ir.access: point_of_sale.rule_pos_config_multi_company +NEW ir.access: point_of_sale.rule_pos_multi_company +NEW ir.access: point_of_sale.rule_pos_order_line_multi_company +NEW ir.access: point_of_sale.rule_pos_order_report_multi_company +NEW ir.access: point_of_sale.rule_pos_payment_method_multi_company +NEW ir.access: point_of_sale.rule_pos_payment_multi_company +NEW ir.access: point_of_sale.rule_pos_session_multi_company +ir.actions.act_window: point_of_sale.action_pos_config_kanban (deleted domain) +ir.actions.act_window: point_of_sale.action_pos_config_tree (deleted domain) +ir.actions.act_window: point_of_sale.action_pos_order_line_day (deleted domain) +ir.actions.act_window: point_of_sale.action_pos_sale_graph (deleted domain) +NEW ir.actions.act_window: point_of_sale.pos_product_attribute_action +NEW ir.actions.act_window: point_of_sale.pos_product_combo_choice_action +NEW ir.actions.act_window: point_of_sale.pos_product_pricelist_action +DEL ir.actions.act_window: point_of_sale.product_product_action +DEL ir.actions.act_window: point_of_sale.res_partner_action_edit_pos +ir.actions.client: point_of_sale.action_client_pos_menu (noupdate switched) +ir.actions.client: point_of_sale.action_client_product_menu (noupdate switched) +NEW ir.cron: point_of_sale.ir_cron_pos_auto_order_invoicing +NEW ir.cron: point_of_sale.ir_cron_process_pos_orders +NEW ir.cron: point_of_sale.ir_cron_snooze_cleanup +DEL ir.model.access: point_of_sale.access_account_bank_statement_line +DEL ir.model.access: point_of_sale.access_account_bank_statement_line_manager +DEL ir.model.access: point_of_sale.access_account_cash_rounding_pos_user +DEL ir.model.access: point_of_sale.access_account_journal_entry +DEL ir.model.access: point_of_sale.access_account_journal_item +DEL ir.model.access: point_of_sale.access_account_journal_pos_user +DEL ir.model.access: point_of_sale.access_account_payment_method_line_pos_user +DEL ir.model.access: point_of_sale.access_account_payment_method_pos_user +DEL ir.model.access: point_of_sale.access_barcode_nomenclature_pos_manager +DEL ir.model.access: point_of_sale.access_barcode_nomenclature_pos_user +DEL ir.model.access: point_of_sale.access_barcode_rule_pos_manager +DEL ir.model.access: point_of_sale.access_barcode_rule_pos_user +DEL ir.model.access: point_of_sale.access_decimal_precision_user +DEL ir.model.access: point_of_sale.access_pos_bill +DEL ir.model.access: point_of_sale.access_pos_close_session_wizard +DEL ir.model.access: point_of_sale.access_pos_config_manager +DEL ir.model.access: point_of_sale.access_pos_config_system_user +DEL ir.model.access: point_of_sale.access_pos_config_user +DEL ir.model.access: point_of_sale.access_pos_confirmation_wizard +DEL ir.model.access: point_of_sale.access_pos_confirmation_wizard_manager +DEL ir.model.access: point_of_sale.access_pos_daily_sales_reports_wizard +DEL ir.model.access: point_of_sale.access_pos_details_wizard +DEL ir.model.access: point_of_sale.access_pos_make_invoice +DEL ir.model.access: point_of_sale.access_pos_make_invoice_manager +DEL ir.model.access: point_of_sale.access_pos_make_payment +DEL ir.model.access: point_of_sale.access_pos_note +DEL ir.model.access: point_of_sale.access_pos_note_manager +DEL ir.model.access: point_of_sale.access_pos_order +DEL ir.model.access: point_of_sale.access_pos_order_line +DEL ir.model.access: point_of_sale.access_pos_order_stock_worker +DEL ir.model.access: point_of_sale.access_pos_pack_operation_lot +DEL ir.model.access: point_of_sale.access_pos_payment_method_manager +DEL ir.model.access: point_of_sale.access_pos_payment_method_user +DEL ir.model.access: point_of_sale.access_pos_payment_user +DEL ir.model.access: point_of_sale.access_pos_preset +DEL ir.model.access: point_of_sale.access_pos_preset_manager +DEL ir.model.access: point_of_sale.access_pos_printer +DEL ir.model.access: point_of_sale.access_pos_printer_manager +DEL ir.model.access: point_of_sale.access_pos_session_user +DEL ir.model.access: point_of_sale.access_product_category_pos_manager +DEL ir.model.access: point_of_sale.access_product_category_pos_user +DEL ir.model.access: point_of_sale.access_product_combo_item_pos_user +DEL ir.model.access: point_of_sale.access_product_combo_pos_user +DEL ir.model.access: point_of_sale.access_product_pricelist_manager +DEL ir.model.access: point_of_sale.access_product_pricelist_user +DEL ir.model.access: point_of_sale.access_product_product +DEL ir.model.access: point_of_sale.access_product_product_supplierinfo_user +DEL ir.model.access: point_of_sale.access_product_template_pos_user +DEL ir.model.access: point_of_sale.access_report_pos_order +DEL ir.model.access: point_of_sale.access_stock_location_manager +DEL ir.model.access: point_of_sale.access_stock_move_pos_user +DEL ir.model.access: point_of_sale.access_stock_picking_pos_user +DEL ir.model.access: point_of_sale.access_stock_warehouse_manager +DEL ir.model.access: point_of_sale.access_stock_warehouse_pos_user +ir.model.constraint: point_of_sale.constraint_pos_note_name_unique (changed definition: is now 'False' ('unique (name)')) +ir.model.constraint: point_of_sale.constraint_pos_order_line_unique_uuid (changed definition: is now 'False' ('unique (uuid)')) +ir.model.constraint: point_of_sale.constraint_pos_order_unique_uuid (changed definition: is now 'False' ('unique (uuid)')) +ir.model.constraint: point_of_sale.constraint_pos_payment_unique_uuid (changed definition: is now 'False' ('unique (uuid)')) +DEL ir.rule: point_of_sale.rule_invoice_line_pos_user (noupdate) +DEL ir.rule: point_of_sale.rule_invoice_pos_user (noupdate) +DEL ir.rule: point_of_sale.rule_pos_bank_statement_account_user (noupdate) +DEL ir.rule: point_of_sale.rule_pos_bank_statement_line_account_user (noupdate) +DEL ir.rule: point_of_sale.rule_pos_bank_statement_line_user (noupdate) +DEL ir.rule: point_of_sale.rule_pos_config_multi_company (noupdate) +DEL ir.rule: point_of_sale.rule_pos_multi_company (noupdate) +DEL ir.rule: point_of_sale.rule_pos_order_line_multi_company (noupdate) +DEL ir.rule: point_of_sale.rule_pos_order_report_multi_company (noupdate) +DEL ir.rule: point_of_sale.rule_pos_payment_method_multi_company (noupdate) +DEL ir.rule: point_of_sale.rule_pos_payment_multi_company (noupdate) +DEL ir.rule: point_of_sale.rule_pos_session_multi_company (noupdate) +ir.sequence: point_of_sale.seq_pos_session (noupdate switched) +NEW ir.ui.menu: point_of_sale.menu_pos_printers +DEL ir.ui.menu: point_of_sale.menu_point_of_sale_list +DEL ir.ui.menu: point_of_sale.menu_pos_preparation_printer +DEL ir.ui.menu: point_of_sale.pos_config_menu_action_product_product +NEW ir.ui.view: point_of_sale.company_info_receipt +NEW ir.ui.view: point_of_sale.pos_cash_move_receipt +NEW ir.ui.view: point_of_sale.pos_order_change_receipt +NEW ir.ui.view: point_of_sale.pos_order_change_receipt_line +NEW ir.ui.view: point_of_sale.pos_order_change_receipt_zpl +NEW ir.ui.view: point_of_sale.pos_order_receipt +NEW ir.ui.view: point_of_sale.pos_order_receipt_footer +NEW ir.ui.view: point_of_sale.pos_order_receipt_header +NEW ir.ui.view: point_of_sale.pos_order_receipt_style +NEW ir.ui.view: point_of_sale.pos_orderline_receipt +NEW ir.ui.view: point_of_sale.pos_orderline_receipt_information +NEW ir.ui.view: point_of_sale.pos_sale_details_receipt +NEW ir.ui.view: point_of_sale.pos_sale_details_receipt_product_line +NEW ir.ui.view: point_of_sale.pos_tip_receipt +NEW ir.ui.view: point_of_sale.product_pricelist_view_kanban_pos +NEW ir.ui.view: point_of_sale.uom_uom_view_form +NEW ir.ui.view: point_of_sale.view_partner_form_pos_ui +NEW ir.ui.view: point_of_sale.view_pos_category_search +NEW ir.ui.view: point_of_sale.view_pos_payment_method_kanban +NEW ir.ui.view: point_of_sale.view_pos_price_inclusion_wizard_form +NEW ir.ui.view: point_of_sale.view_pos_printer_kanban +DEL ir.ui.view: point_of_sale.product_product_tree_view +DEL ir.ui.view: point_of_sale.product_template_tree_view +DEL ir.ui.view: point_of_sale.product_uom_form_view_inherit +DEL ir.ui.view: point_of_sale.stock_reference_pos_view_form +DEL ir.ui.view: point_of_sale.view_form_pos_close_session_wizard +mail.template: point_of_sale.email_template_pos_receipt (noupdate switched) +pos.bill: point_of_sale.0_05 (noupdate switched) +pos.bill: point_of_sale.0_10 (noupdate switched) +pos.bill: point_of_sale.0_20 (noupdate switched) +pos.bill: point_of_sale.0_25 (noupdate switched) +pos.bill: point_of_sale.0_50 (noupdate switched) +pos.bill: point_of_sale.100_00 (noupdate switched) +pos.bill: point_of_sale.10_00 (noupdate switched) +pos.bill: point_of_sale.1_00 (noupdate switched) +pos.bill: point_of_sale.200_00 (noupdate switched) +pos.bill: point_of_sale.20_00 (noupdate switched) +pos.bill: point_of_sale.2_00 (noupdate switched) +pos.bill: point_of_sale.50_00 (noupdate switched) +pos.bill: point_of_sale.5_00 (noupdate switched) +product.category: point_of_sale.product_category_food (noupdate switched) +NEW product.product: point_of_sale.product_product_service_fee +product.product: point_of_sale.product_product_tip (noupdate switched) +NEW res.partner: point_of_sale.default_session_closing_partner +uom.uom: uom.product_uom_dozen (noupdate switched) +uom.uom: uom.product_uom_pack_6 (noupdate switched) +uom.uom: uom.product_uom_unit (noupdate switched) diff --git a/openupgrade_scripts/scripts/point_of_sale/tests/data_point_of_sale_migration.py b/openupgrade_scripts/scripts/point_of_sale/tests/data_point_of_sale_migration.py new file mode 100644 index 00000000000..67897741fac --- /dev/null +++ b/openupgrade_scripts/scripts/point_of_sale/tests/data_point_of_sale_migration.py @@ -0,0 +1,45 @@ +env = locals().get("env") + +# pos.payment.method.type is computed in 19.0 and stored + required in 20.0, so +# the column starts empty and has to be derived. 19.0's own _compute_type reads +# the JOURNAL, not anything on the method, and falls through to pay_later when +# the journal is neither cash nor bank -- including when there is none. One +# method per branch. +company = env.company +journals = env["account.journal"] + + +def _journal(kind): + found = journals.search( + [("type", "=", kind), ("company_id", "=", company.id)], limit=1 + ) + return found or journals.create( + { + "name": f"ou19-{kind}", + "code": f"OU{kind[:2].upper()}", + "type": kind, + "company_id": company.id, + } + ) + + +method = env["pos.payment.method"] +method.create( + { + "name": "ou19-pm-cash", + "journal_id": _journal("cash").id, + "company_id": company.id, + } +) +method.create( + { + "name": "ou19-pm-bank", + "journal_id": _journal("bank").id, + "company_id": company.id, + } +) +# No journal at all: this is the branch is_cash_count cannot distinguish, since +# it is False here and False on a bank method alike. +method.create({"name": "ou19-pm-none", "company_id": company.id}) + +env.cr.commit() diff --git a/openupgrade_scripts/scripts/point_of_sale/tests/test_point_of_sale_migration.py b/openupgrade_scripts/scripts/point_of_sale/tests/test_point_of_sale_migration.py new file mode 100644 index 00000000000..00ac495230f --- /dev/null +++ b/openupgrade_scripts/scripts/point_of_sale/tests/test_point_of_sale_migration.py @@ -0,0 +1,51 @@ +from odoo.tests import TransactionCase + +from odoo.addons.openupgrade_framework import openupgrade_test + + +@openupgrade_test +class TestPointOfSaleMigration(TransactionCase): + """Assertions on pos.payment.method.type, now stored and required. + + Every method opens by asserting the record exists. The data snippet runs in + a separate process through the 19.0 shell, and if its commit were lost it + would exit 0 having written nothing -- an assertion whose empty case is a + pass would then report success for a migration that never happened. + """ + + def _method(self, name): + return ( + self.env["pos.payment.method"] + .with_context(active_test=False) + .search([("name", "=", name)]) + ) + + def test_type_follows_the_journal(self): + """19.0's _compute_type reads journal_id.type, so the fill must too.""" + for name, expected in ( + ("ou19-pm-cash", "cash"), + ("ou19-pm-bank", "bank"), + ): + record = self._method(name) + self.assertTrue(record, name) + self.assertEqual(record.type, expected, name) + + def test_a_method_with_no_journal_is_pay_later(self): + """The branch is_cash_count cannot tell from a bank method. + + Both are False there, so a fill keyed off is_cash_count would put bank + and pay_later in the same bucket and be wrong for one of them. + """ + record = self._method("ou19-pm-none") + self.assertTrue(record) + self.assertEqual(record.type, "pay_later") + + def test_no_method_is_left_untyped(self): + """20.0 declares type required, so nothing may be left blank. + + Asserting on the fixtures alone would pass on a fill that only touched + the rows the test itself created. + """ + methods = self.env["pos.payment.method"].with_context(active_test=False) + self.assertTrue(methods.search_count([])) + self.assertEqual(methods.search_count([("type", "=", False)]), 0)