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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docsource/modules180-190.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Module coverage 18.0 -> 19.0
+---------------------------------------------------+----------------------+-------------------------------------------------+
| |del| hr_contract | | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
| hr_expense | | |
| hr_expense |Done | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
| hr_fleet | | |
+---------------------------------------------------+----------------------+-------------------------------------------------+
Expand Down
73 changes: 73 additions & 0 deletions openupgrade_scripts/scripts/hr_expense/19.0.2.1/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2026 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from openupgradelib import openupgrade


def hr_expense_account_move_id(env):
"""
Fill account.move#expense_ids from expense_sheet_id
"""
env.cr.execute(
"""
UPDATE hr_expense
SET account_move_id=account_move.id
FROM
account_move
WHERE
account_move.expense_sheet_id=hr_expense.former_sheet_id
"""
)


def hr_expense_approval_fields(env):
"""
Fill hr.expense#approval_{date,state} and manager_id from hr.expense.sheet
"""
env.cr.execute(
"""
UPDATE hr_expense
SET
approval_date=hr_expense_sheet.approval_date,
approval_state=CASE
WHEN hr_expense_sheet.approval_state = 'approve' THEN 'approved'
WHEN hr_expense_sheet.approval_state = 'cancel' THEN 'refused'
WHEN hr_expense_sheet.approval_state = 'submit' THEN 'submitted'
END,
manager_id=hr_expense_sheet.user_id,
department_id=hr_expense_sheet.department_id
FROM
hr_expense_sheet
WHERE
hr_expense.former_sheet_id=hr_expense_sheet.id
"""
)


def hr_expense_state(env):
"""
Recompute hr.expense#state for records in states 'approved', 'done', 'reported'
"""
env.cr.execute(
"SELECT id FROM hr_expense WHERE state IN ('approved', 'done', 'reported')"
)
records = env["hr.expense"].browse(_id for (_id,) in env.cr.fetchall())
records._compute_state()


deleted_xmlids = [
"hr_expense.hr_expense_report_comp_rule",
"hr_expense.ir_rule_hr_expense_sheet_approver",
"hr_expense.ir_rule_hr_expense_sheet_employee",
"hr_expense.ir_rule_hr_expense_sheet_employee_not_draft",
"hr_expense.ir_rule_hr_expense_sheet_manager",
]


@openupgrade.migrate()
def migrate(env, version):
openupgrade.load_data(env, "hr_expense", "19.0.2.1/noupdate_changes.xml")
hr_expense_account_move_id(env)
hr_expense_approval_fields(env)
hr_expense_state(env)
openupgrade.delete_records_safely_by_xml_id(env, deleted_xmlids)
24 changes: 24 additions & 0 deletions openupgrade_scripts/scripts/hr_expense/19.0.2.1/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2026 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from openupgradelib import openupgrade

added_fields = [
("manager_id", "hr.expense", "hr_expense", "many2one", None, "hr_expense"),
("department_id", "hr.expense", "hr_expense", "many2one", None, "hr_expense"),
]

renamed_fields = [
("hr.expense", "hr_expense", "sheet_id", "former_sheet_id"),
]

copied_columns = {
"hr_expense": [("state", None, None)],
}


@openupgrade.migrate()
def migrate(env, version):
openupgrade.rename_fields(env, renamed_fields)
openupgrade.copy_columns(env.cr, copied_columns)
openupgrade.add_fields(env, added_fields)
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---Models in module 'hr_expense'---
obsolete model hr.expense.sheet
new model hr.expense.post.wizard [transient]

# NOTHING TO DO

---Fields in module 'hr_expense'---
hr_expense / account.move / expense_ids (one2many) : NEW relation: hr.expense
hr_expense / account.move / expense_sheet_id (many2one) : DEL relation: hr.expense.sheet
hr_expense / hr.expense / account_move_id (many2one) : NEW relation: account.move

# DONE: filled from account.move#expense_sheet_id

hr_expense / hr.expense / accounting_date (date) : DEL

# NOTHING TO DO

hr_expense / hr.expense / approval_date (datetime) : NEW
hr_expense / hr.expense / approval_state (selection) : NEW selection_keys: ['approved', 'refused', 'submitted']
hr_expense / hr.expense / department_id (many2one) : NEW relation: hr.department, isfunction: function, stored

# DONE: filled from hr.expense.sheet

hr_expense / hr.expense / former_sheet_id (integer) : NEW

# DONE: renamed from sheet_id

hr_expense / hr.expense / manager_id (many2one) : NEW relation: res.users, isfunction: function, stored

# DONE: filled from hr.expense.sheet

hr_expense / hr.expense / payment_method_line_id (many2one): NEW relation: account.payment.method.line, hasdefault: compute

# NOTHING TO DO

hr_expense / hr.expense / sheet_id (many2one) : DEL relation: hr.expense.sheet

# DONE: renamed to former_sheet_id

hr_expense / hr.expense / split_expense_origin_id (many2one): NEW relation: hr.expense

# NOTHING TO DO

hr_expense / hr.expense / state (selection) : selection_keys added: [in_payment, paid, posted], removed: [done, reported]

# DONE: recompute state for records in states 'done', 'reported'

hr_expense / hr.expense / untaxed_amount (float) : NEW isfunction: function, stored
hr_expense / hr.expense.sheet / account_move_ids (one2many) : DEL relation: account.move
hr_expense / hr.expense.sheet / accounting_date (date) : DEL
hr_expense / hr.expense.sheet / activity_ids (one2many) : DEL relation: mail.activity
hr_expense / hr.expense.sheet / amount_residual (float) : DEL
hr_expense / hr.expense.sheet / approval_date (datetime) : DEL
hr_expense / hr.expense.sheet / approval_state (selection) : DEL selection_keys: ['approve', 'cancel', 'submit']
hr_expense / hr.expense.sheet / attachment_ids (one2many) : DEL relation: ir.attachment
hr_expense / hr.expense.sheet / company_id (many2one) : DEL relation: res.company, required
hr_expense / hr.expense.sheet / currency_id (many2one) : DEL relation: res.currency
hr_expense / hr.expense.sheet / department_id (many2one) : DEL relation: hr.department
hr_expense / hr.expense.sheet / employee_id (many2one) : DEL relation: hr.employee, required
hr_expense / hr.expense.sheet / employee_journal_id (many2one): DEL relation: account.journal
hr_expense / hr.expense.sheet / expense_line_ids (one2many) : DEL relation: hr.expense
hr_expense / hr.expense.sheet / journal_id (many2one) : DEL relation: account.journal
hr_expense / hr.expense.sheet / message_follower_ids (one2many): DEL relation: mail.followers
hr_expense / hr.expense.sheet / message_ids (one2many) : DEL relation: mail.message
hr_expense / hr.expense.sheet / message_main_attachment_id (many2one): DEL relation: ir.attachment
hr_expense / hr.expense.sheet / name (char) : DEL required
hr_expense / hr.expense.sheet / payment_method_line_id (many2one): DEL relation: account.payment.method.line
hr_expense / hr.expense.sheet / payment_state (selection) : DEL selection_keys: function
hr_expense / hr.expense.sheet / rating_ids (one2many) : DEL relation: rating.rating
hr_expense / hr.expense.sheet / state (selection) : DEL required, selection_keys: ['approve', 'cancel', 'done', 'draft', 'post', 'submit']
hr_expense / hr.expense.sheet / total_amount (float) : DEL
hr_expense / hr.expense.sheet / total_tax_amount (float) : DEL
hr_expense / hr.expense.sheet / untaxed_amount (float) : DEL
hr_expense / hr.expense.sheet / user_id (many2one) : DEL relation: res.users
hr_expense / hr.expense.sheet / website_message_ids (one2many): DEL relation: mail.message
hr_expense / res.company / expense_outstanding_account_id (many2one): DEL relation: account.account

# NOTHING TO DO

---XML records in module 'hr_expense'---
NEW ir.actions.act_window: hr_expense.action_hr_expense_department_filtered
NEW ir.actions.act_window: hr_expense.action_hr_expense_department_to_approve
NEW ir.actions.act_window: hr_expense.hr_expense_actions_to_process
DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_all
DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_all_all
DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_department_filtered
DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_department_to_approve
DEL ir.actions.act_window: hr_expense.action_hr_expense_sheet_my_all
DEL ir.actions.act_window.view: hr_expense.action_hr_expense_sheet_my_all_kanban
DEL ir.actions.act_window.view: hr_expense.action_hr_expense_sheet_my_all_tree
NEW ir.actions.report: hr_expense.action_report_expense_img
NEW ir.actions.report: hr_expense.action_report_hr_expense
DEL ir.actions.report: hr_expense.action_report_expense_sheet_img
DEL ir.actions.report: hr_expense.action_report_hr_expense_sheet
NEW ir.cron: hr_expense.ir_cron_send_submitted_expenses_mail
NEW ir.model.access: hr_expense.access_hr_expense_accountant
NEW ir.model.access: hr_expense.access_hr_expense_post
DEL ir.model.access: hr_expense.access_account_journal_employee
DEL ir.model.access: hr_expense.access_hr_expense_sheet_employee
DEL ir.model.access: hr_expense.access_hr_expense_sheet_manager
DEL ir.model.access: hr_expense.access_hr_expense_sheet_user
DEL ir.model.access: hr_expense.access_product_product_hr_expense_user
DEL ir.model.access: hr_expense.access_product_template_hr_expense_user
DEL ir.model.access: hr_expense.access_uom_uom_hr_expense_user
DEL ir.model.constraint: hr_expense.constraint_hr_expense_sheet_journal_id_required_posted
NEW ir.rule: hr_expense.ir_rule_hr_expense_split_accountant (noupdate)
NEW ir.rule: hr_expense.ir_rule_hr_expense_split_approver (noupdate)
NEW ir.rule: hr_expense.ir_rule_hr_expense_split_employee (noupdate)
NEW ir.rule: hr_expense.ir_rule_hr_expense_split_manager (noupdate)
NEW ir.rule: hr_expense.ir_rule_hr_expense_split_user (noupdate)

# NOTHING TO DO

DEL ir.rule: hr_expense.hr_expense_report_comp_rule (noupdate)
DEL ir.rule: hr_expense.ir_rule_hr_expense_sheet_approver (noupdate)
DEL ir.rule: hr_expense.ir_rule_hr_expense_sheet_employee (noupdate)
DEL ir.rule: hr_expense.ir_rule_hr_expense_sheet_employee_not_draft (noupdate)
DEL ir.rule: hr_expense.ir_rule_hr_expense_sheet_manager (noupdate)

# DONE: deleted in post-migration

NEW ir.ui.menu: hr_expense.menu_hr_expense_expenses_to_process
DEL ir.ui.menu: hr_expense.menu_hr_expense_report
DEL ir.ui.menu: hr_expense.menu_hr_expense_sheet_my_reports
NEW ir.ui.view: hr_expense.hr_employee_search_view
NEW ir.ui.view: hr_expense.hr_expense_post_wizard_view
NEW ir.ui.view: hr_expense.hr_expense_template_submitted_expenses (noupdate)
NEW ir.ui.view: hr_expense.hr_expense_view_search_with_panel
NEW ir.ui.view: hr_expense.report_expense
NEW ir.ui.view: hr_expense.report_expense_img
NEW ir.ui.view: hr_expense.view_move_list_expense
DEL ir.ui.view: hr_expense.hr_expense_sheet_view_activity
DEL ir.ui.view: hr_expense.hr_expense_sheet_view_search
DEL ir.ui.view: hr_expense.hr_expense_sheet_view_search_with_panel
DEL ir.ui.view: hr_expense.report_expense_sheet
DEL ir.ui.view: hr_expense.report_expense_sheet_img
DEL ir.ui.view: hr_expense.res_users_view_form_preferences
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_dashboard_tree_header
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_form
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_graph
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_kanban
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_kanban_header
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_kanban_no_header
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_pivot
DEL ir.ui.view: hr_expense.view_hr_expense_sheet_tree
NEW res.groups.privilege: hr_expense.res_groups_privilege_expenses

# NOTHING TO DO
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
env = locals().get("env")
env.ref("hr_expense.team_building_sheet").action_submit_sheet()
env.ref("hr_expense.office_furniture_sheet").action_submit_sheet()
env.ref("hr_expense.office_furniture_sheet").action_approve_expense_sheets()
env.ref("hr_expense.customer_meeting_sheet").action_submit_sheet()
env.ref("hr_expense.customer_meeting_sheet").action_approve_expense_sheets()
env.ref("hr_expense.customer_meeting_sheet").action_sheet_move_post()
action = env.ref("hr_expense.customer_meeting_sheet").action_register_payment()
wizard = env[action["res_model"]].with_context(**action["context"]).create({})
wizard.action_create_payments()
env.cr.commit()
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from odoo.tests import TransactionCase

from odoo.addons.openupgrade_framework import openupgrade_test


@openupgrade_test
class TestHrExpenseMigration(TransactionCase):
def test_hr_expense_state(self):
"""
Test that expenses have the expected state after migration
"""
self.assertEqual(
self.env.ref("hr_expense.hotel_bill_expense").state,
"draft",
)
self.assertEqual(
self.env.ref("hr_expense.pizzas_bill_expense").state,
"submitted",
)
self.assertEqual(
self.env.ref("hr_expense.chair_bill_expense").state,
"posted",
)
self.assertEqual(
self.env.ref("hr_expense.travel_demo_by_car_expense").state,
"paid",
)
Loading