diff --git a/docsource/modules150-160.rst b/docsource/modules150-160.rst index c009cf1d5fa4..62b1c7e16133 100644 --- a/docsource/modules150-160.rst +++ b/docsource/modules150-160.rst @@ -170,7 +170,7 @@ Module coverage 15.0 -> 16.0 +-------------------------------------------------+----------------------+-------------------------------------------------+ | hr_contract | Done | | +-------------------------------------------------+----------------------+-------------------------------------------------+ -| hr_expense | | | +| hr_expense | Done | | +-------------------------------------------------+----------------------+-------------------------------------------------+ | hr_fleet | | | +-------------------------------------------------+----------------------+-------------------------------------------------+ diff --git a/openupgrade_scripts/scripts/hr_expense/16.0.2.0/post-migration.py b/openupgrade_scripts/scripts/hr_expense/16.0.2.0/post-migration.py new file mode 100644 index 000000000000..67a0a5d31fc7 --- /dev/null +++ b/openupgrade_scripts/scripts/hr_expense/16.0.2.0/post-migration.py @@ -0,0 +1,73 @@ +# Copyright 2023 Tecnativa - Víctor Martínez +# Copyright 2024 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openupgradelib import openupgrade + + +def _hr_expense_process(env): + """Set amount_tax_company by ORM only when different currency.""" + env.cr.execute( + """ + SELECT he.id + FROM hr_expense he + JOIN res_company rc ON rc.id = he.company_id + WHERE rc.currency_id = he.currency_id + """ + ) + env["hr.expense"].browse([x[0] for x in env.cr.fetchall()])._compute_amount_tax() + + +def _hr_expense_sheet_process(env): + """With the hr.expense values already defined, we can now set the missing data.""" + openupgrade.logged_query( + env.cr, + """ + UPDATE hr_expense_sheet SET total_amount_taxes = ( + SELECT SUM(amount_tax_company) + FROM hr_expense + WHERE sheet_id = hr_expense_sheet.id + ) + """, + ) + openupgrade.logged_query( + env.cr, + """ + UPDATE hr_expense_sheet + SET untaxed_amount = (total_amount - total_amount_taxes) + """, + ) + + +def _hr_expense_analytic_tag(env): + """If table exists and there are any record with no distribution (used just as tag), + we set the module hr_expense_analytic_tag to be installed. + """ + if openupgrade.table_exists(env.cr, "account_analytic_tag_hr_expense_rel"): + env.cr.execute( + """SELECT COUNT(*) + FROM account_analytic_tag_hr_expense_rel rel + JOIN account_analytic_tag aat ON rel.account_analytic_tag_id = aat.id + WHERE NOT aat.active_analytic_distribution + """, + ) + if env.cr.fetchone()[0]: + openupgrade.logged_query( + env.cr, + """UPDATE ir_module_module + SET state = 'to install' + WHERE name = 'hr_expense_analytic_tag'""", + ) + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data(env.cr, "hr_expense", "16.0.2.0/noupdate_changes.xml") + _hr_expense_process(env) + _hr_expense_sheet_process(env) + _hr_expense_analytic_tag(env) + openupgrade.set_xml_ids_noupdate_value( + env, + "hr_expense", + ["product_product_no_cost"], + True, + ) diff --git a/openupgrade_scripts/scripts/hr_expense/16.0.2.0/pre-migration.py b/openupgrade_scripts/scripts/hr_expense/16.0.2.0/pre-migration.py new file mode 100644 index 000000000000..cb397ef6ba51 --- /dev/null +++ b/openupgrade_scripts/scripts/hr_expense/16.0.2.0/pre-migration.py @@ -0,0 +1,108 @@ +# Copyright 2023 Tecnativa - Víctor Martínez +# Copyright 2024 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openupgradelib import openupgrade, openupgrade_160 + + +def _hr_expense_table_new_columns(env): + """Custom process to create new columns: + - amount_tax: (total_amount - untaxed_amount). It will be similar enough. + - amount_tax_company: the same value as amount_tax if same currency, and let + for post-migration for different currency. + """ + if not openupgrade.column_exists(env.cr, "hr_expense", "amount_tax"): + openupgrade.logged_query( + env.cr, + "ALTER TABLE hr_expense ADD COLUMN amount_tax numeric", + ) + openupgrade.logged_query( + env.cr, "UPDATE hr_expense SET amount_tax = (total_amount - untaxed_amount)" + ) + if not openupgrade.column_exists(env.cr, "hr_expense", "amount_tax_company"): + openupgrade.logged_query( + env.cr, + "ALTER TABLE hr_expense ADD COLUMN amount_tax_company numeric", + ) + openupgrade.logged_query( + env.cr, + """UPDATE hr_expense he + SET amount_tax_company = amount_tax + FROM res_company rc + WHERE rc.id = he.company_id AND rc.currency_id = he.currency_id + """, + ) + + +def _hr_expense_sheet_table_new_columns(env): + """Pre-create columns to be processed on post-migration.""" + openupgrade.logged_query( + env.cr, + """ALTER TABLE hr_expense_sheet + ADD COLUMN IF NOT EXISTS total_amount_taxes numeric""", + ) + openupgrade.logged_query( + env.cr, + """ALTER TABLE hr_expense_sheet + ADD COLUMN IF NOT EXISTS untaxed_amount numeric""", + ) + + +def _fast_fill_analytic_distribution_on_hr_expense(env): + if not openupgrade.column_exists(env.cr, "hr_expense", "analytic_distribution"): + openupgrade.logged_query( + env.cr, + """ + ALTER TABLE hr_expense + ADD COLUMN IF NOT EXISTS analytic_distribution jsonb; + """, + ) + openupgrade_160.fill_analytic_distribution( + env, + table="hr_expense", + m2m_rel="account_analytic_tag_hr_expense_rel", + m2m_column1="hr_expense_id", + ) + + +def _convert_expense_sheet_entries_to_invoices(env): + """From v16 onwards, account_move related to hr_expense_sheet are created with + move_type = "in_invoice" as per + https://github.com/odoo/odoo/blob/87f4667d81f7d8a40dcf225f3daf1a9e2795680d/ + addons/hr_expense/models/hr_expense.py#L1297 + on contrary than on v15, which was "entry". + + Then, in _compute_payment_state from account.move it will check if the move_type is + "entry", putting payment_state = "not_paid" for that cases, no matter the + reconcilation: + https://github.com/odoo/odoo/blob/87f4667d81f7d8a40dcf225f3daf1a9e2795680d/ + addons/account/models/account_move.py#L926 + + As the sheet payment state is taken from the move's payment_state, we need to + switch all the existing expenses entries to "in_invoice". + """ + openupgrade.logged_query( + env.cr, + """ + UPDATE account_move am + SET move_type = 'in_invoice' + FROM hr_expense_sheet hes + WHERE hes.account_move_id = am.id AND am.move_type <> 'in_invoice' + """, + ) + + +_xmlid_renames = [ + ( + "hr_expense.product_product_zero_cost", + "hr_expense.product_product_no_cost", + ), +] + + +@openupgrade.migrate() +def migrate(env, version): + _hr_expense_table_new_columns(env) + _hr_expense_sheet_table_new_columns(env) + _fast_fill_analytic_distribution_on_hr_expense(env) + _convert_expense_sheet_entries_to_invoices(env) + openupgrade.rename_xmlids(env.cr, _xmlid_renames) diff --git a/openupgrade_scripts/scripts/hr_expense/16.0.2.0/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/hr_expense/16.0.2.0/upgrade_analysis_work.txt new file mode 100644 index 000000000000..c56c54c559d4 --- /dev/null +++ b/openupgrade_scripts/scripts/hr_expense/16.0.2.0/upgrade_analysis_work.txt @@ -0,0 +1,71 @@ +---Models in module 'hr_expense'--- +new model hr.expense.split [transient] +new model hr.expense.split.wizard [transient] +# NOTHING TO DO + +---Fields in module 'hr_expense'--- + +hr_expense / hr.expense / amount_tax (float) : NEW isfunction: function, stored +# DONE: pre-migration: set value + +hr_expense / hr.expense / amount_tax_company (float) : NEW isfunction: function, stored +# DONE: pre-migration: pre-create column for same currency +# DONE: post-migration: set value by ORM for different currency + +hr_expense / hr.expense / analytic_account_id (many2one): DEL relation: account.analytic.account +hr_expense / hr.expense / analytic_distribution_stored_char (char): NEW isfunction: function, stored +# DONE: pre-migration: fast fill analytic_distribution + +hr_expense / hr.expense / analytic_tag_ids (many2many) : DEL relation: account.analytic.tag +# DONE: post-migration: if there are tags with no distribution, we plan OCA module hr_expense_analytic_tag to be installed + +hr_expense / hr.expense.sheet / total_amount_taxes (float) : NEW isfunction: function, stored +hr_expense / hr.expense.sheet / untaxed_amount (float) : NEW isfunction: function, stored +# DONE: pre-migration: pre-create columns +# DONE: post-migration: set values by SQL + +hr_expense / account.analytic.applicability / business_domain (False) : NEW selection_keys: ['bill', 'expense', 'general', 'invoice'], mode: modify +hr_expense / account.move / expense_sheet_id (one2many) : NEW relation: hr.expense.sheet +hr_expense / hr.expense / name (char) : not a function anymore +hr_expense / hr.expense / unit_amount (float) : not a function anymore +hr_expense / hr.expense.sheet / payment_state (selection) : selection_keys is now 'function' ('['in_payment', 'invoicing_legacy', 'not_paid', 'paid', 'partial', 'reversed']') +# NOTHING TO DO + +hr_expense / res.company / company_expense_journal_id (many2one): NEW relation: account.journal +hr_expense / res.company / expense_journal_id (many2one) : NEW relation: account.journal +# NOTHING TO DO: Preference values for being set by company, but if none selected, the previous default ones are used. + +---XML records in module 'hr_expense'--- +DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_all_to_approve +DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_all_to_pay +DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_all_to_post +DEL ir.actions.act_window: hr_expense.hr_expense_actions_my_unsubmitted +DEL ir.actions.act_window.view: hr_expense.hr_expense_actions_all_kanban +DEL ir.actions.act_window.view: hr_expense.hr_expense_actions_my_unsubmitted_kanban +DEL ir.actions.act_window.view: hr_expense.hr_expense_actions_my_unsubmitted_tree +NEW ir.model.access: hr_expense.access_hr_expense_split_manager +NEW ir.model.access: hr_expense.access_hr_expense_split_wizard_manager +NEW ir.rule: hr_expense.ir_rule_hr_expense_sheet_employee_not_draft (noupdate) +DEL ir.ui.menu: hr_expense.menu_hr_expense_my_expenses_to_submit +DEL ir.ui.menu: hr_expense.menu_hr_expense_sheet_all +DEL ir.ui.menu: hr_expense.menu_hr_expense_sheet_all_to_approve +DEL ir.ui.menu: hr_expense.menu_hr_expense_sheet_all_to_pay +DEL ir.ui.menu: hr_expense.menu_hr_expense_sheet_all_to_post +NEW ir.ui.view: hr_expense.hr_expense_sheet_view_search_with_panel +NEW ir.ui.view: hr_expense.hr_expense_split +NEW ir.ui.view: hr_expense.product_product_expense_categories_tree_view +NEW ir.ui.view: hr_expense.view_move_form_inherit_expense +NEW ir.ui.view: hr_expense.view_product_hr_expense_form +DEL ir.ui.view: hr_expense.view_hr_expense_sheet_dashboard_tree +NEW product.product: hr_expense.expense_product_communication (noupdate) +NEW product.product: hr_expense.expense_product_gift (noupdate) +NEW product.product: hr_expense.expense_product_meal (noupdate) +NEW product.product: hr_expense.expense_product_mileage (noupdate) +NEW product.product: hr_expense.expense_product_travel_accommodation (noupdate) +DEL product.product: hr_expense.product_product_fixed_cost (noupdate) +# NOTHING TO DO + +DEL product.product: hr_expense.product_product_zero_cost +NEW product.product: hr_expense.product_product_no_cost (noupdate) +# DONE: pre-migration: renamed product_product_zero_cost -> product_product_no_cost +# DONE: post-migration: switch noupdate after updating new data