diff --git a/docsource/modules180-190.rst b/docsource/modules180-190.rst index ca4b8068ab5b..39e855b4fabf 100644 --- a/docsource/modules180-190.rst +++ b/docsource/modules180-190.rst @@ -168,7 +168,7 @@ Module coverage 18.0 -> 19.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | hr |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| hr_attendance | | | +| hr_attendance |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ | hr_calendar | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ diff --git a/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/post-migration.py b/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/post-migration.py new file mode 100644 index 000000000000..7f72a211047d --- /dev/null +++ b/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/post-migration.py @@ -0,0 +1,17 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + +_deleted_xmlids = [ + "hr_attendance.hr_attendance_overtime_rule_employee_company", + "hr_attendance.hr_attendance_rule_attendance_officer_overtime_restrict", + "hr_attendance.hr_attendance_rule_attendance_overtime_admin", + "hr_attendance.hr_attendance_rule_attendance_overtime_simple_user", +] + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data(env, "hr_attendance", "19.0.2.0/noupdate_changes.xml") + openupgrade.delete_records_safely_by_xml_id(env, _deleted_xmlids) diff --git a/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/pre-migration.py b/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/pre-migration.py new file mode 100644 index 000000000000..1d7c306259f1 --- /dev/null +++ b/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/pre-migration.py @@ -0,0 +1,216 @@ +# Copyright 2026 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + +_renamed_models = [ + ("hr.attendance.overtime", "hr.attendance.overtime.line"), +] + +_renamed_tables = [ + ("hr_attendance_overtime", "hr_attendance_overtime_line"), +] + +_renamed_fields = [ + ("hr.attendance", "hr_attendance", "in_city", "in_location"), + ("hr.attendance", "hr_attendance", "out_city", "out_location"), + ( + "hr.attendance.overtime.line", + "hr_attendance_overtime_line", + "duration_real", + "manual_duration", + ), +] + +SQL_EMPLOYEE2TZ = """ +( + SELECT + hr_employee.id employee_id, + COALESCE( + MIN(resource_calendar.tz), + MIN(resource_resource.tz), + MIN(company_resource_calendar.tz) + ) zone + FROM + hr_employee + JOIN + resource_resource + ON + hr_employee.resource_id=resource_resource.id + LEFT JOIN + resource_calendar + ON + resource_resource.calendar_id=resource_calendar.id + JOIN + res_company + ON + hr_employee.company_id=res_company.id + LEFT JOIN + resource_calendar company_resource_calendar + ON + res_company.resource_calendar_id=company_resource_calendar.id + GROUP BY + hr_employee.id +) employee2zone +""" + + +def hr_attendance_date(env): + """ + Pre-fill hr.attendance#date + """ + openupgrade.add_fields( + env, [("date", "hr.attendance", "hr_attendance", "date", None, "hr_attendance")] + ) + env.cr.execute( + f""" + UPDATE + hr_attendance + SET date=(check_in AT TIME ZONE COALESCE(employee2zone.zone, 'utc'))::date + FROM + {SQL_EMPLOYEE2TZ} + WHERE + employee2zone.employee_id=hr_attendance.employee_id + AND date IS NULL + """ + ) + + +def hr_attendance_overtime_line_fields(env): + """ + Pre-fill new fields of hr.attendance.overtime.line + """ + openupgrade.add_fields( + env, + [ + ( + "status", + "hr.attendance.overtime.line", + "hr_attendance_overtime_line", + "char", + None, + "hr_attendance", + "approved", + ), + ( + "time_start", + "hr.attendance.overtime.line", + "hr_attendance_overtime_line", + "datetime", + None, + "hr_attendance", + ), + ( + "time_stop", + "hr.attendance.overtime.line", + "hr_attendance_overtime_line", + "datetime", + None, + "hr_attendance", + ), + ], + ) + # date is required in v19, fill with create_date if empty, possibly wrong + env.cr.execute( + f""" + UPDATE hr_attendance_overtime_line SET date=( + create_date AT TIME ZONE COALESCE(employee2zone.zone, 'utc') + )::date + FROM + {SQL_EMPLOYEE2TZ} + WHERE + employee2zone.employee_id=hr_attendance_overtime_line.employee_id + AND + date IS NULL + """ + ) + # time_start, time_stop need to match some check_in, check_out from hr_attendance + env.cr.execute( + """ + UPDATE + hr_attendance_overtime_line + SET + time_start=hr_attendance.check_in, + time_stop=hr_attendance.check_out + FROM + hr_attendance + WHERE + hr_attendance.employee_id=hr_attendance_overtime_line.employee_id + AND + hr_attendance.date=hr_attendance_overtime_line.date + """ + ) + # for companies with manager approval, set status from state on attendance + env.cr.execute( + """ + UPDATE + hr_attendance_overtime_line + SET + status=hr_attendance.overtime_status + FROM + hr_employee, res_company, hr_attendance + WHERE + hr_attendance.check_in=hr_attendance_overtime_line.time_start + AND + hr_attendance.employee_id=hr_attendance_overtime_line.employee_id + AND + hr_attendance_overtime_line.employee_id=hr_employee.id + AND + hr_employee.company_id=res_company.id + AND + res_company.attendance_overtime_validation = 'by_manager' + """ + ) + + +def hr_attendance_validated_overtime_hours(env): + """ + Pre-fill hr.attendance#validated_overtime_hours + """ + openupgrade.add_fields( + env, + [ + ( + "validated_overtime_hours", + "hr.attendance", + "hr_attendance", + "float", + None, + "hr_attendance", + ) + ], + ) + # validated_overtime_hours is the sum of approved overtime lines + env.cr.execute( + """ + UPDATE + hr_attendance + SET + validated_overtime_hours=grouped_overtime_lines.sum_duration + FROM + ( + SELECT + employee_id, time_start, SUM(manual_duration) sum_duration + FROM + hr_attendance_overtime_line + WHERE + status='approved' + GROUP BY + employee_id, time_start + ) grouped_overtime_lines + WHERE + grouped_overtime_lines.employee_id=hr_attendance.employee_id AND + grouped_overtime_lines.time_start=hr_attendance.check_in + """ + ) + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_models(env.cr, _renamed_models) + openupgrade.rename_tables(env.cr, _renamed_tables) + openupgrade.rename_fields(env, _renamed_fields) + # order matters + hr_attendance_date(env) + hr_attendance_overtime_line_fields(env) + hr_attendance_validated_overtime_hours(env) diff --git a/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/upgrade_analysis_work.txt new file mode 100644 index 000000000000..02f89e338ce9 --- /dev/null +++ b/openupgrade_scripts/scripts/hr_attendance/19.0.2.0/upgrade_analysis_work.txt @@ -0,0 +1,175 @@ +---Models in module 'hr_attendance'--- +obsolete model hr.attendance.overtime (renamed to hr.attendance.overtime.line) +new model hr.attendance.overtime.line (renamed from hr.attendance.overtime) + +# DONE: renamed in pre-migration + +new model hr.attendance.overtime.rule +new model hr.attendance.overtime.ruleset + +# NOTHING TO DO + +---Fields in module 'hr_attendance'--- +hr_attendance / hr.attendance / date (date) : NEW required, isfunction: function, stored + +# DONE: precreated + +hr_attendance / hr.attendance / in_city (char) : DEL + +# DONE: renamed to in_location + +hr_attendance / hr.attendance / in_country_name (char) : DEL + +# NOTHING TO DO + +hr_attendance / hr.attendance / in_location (char) : NEW + +# DONE: renamed from in_city + +hr_attendance / hr.attendance / linked_overtime_ids (many2many): NEW relation: hr.attendance.overtime.line, hasdefault: compute, stored: False + +# NOTHING TO DO + +hr_attendance / hr.attendance / out_city (char) : DEL + +# DONE: renamed to in_location + +hr_attendance / hr.attendance / out_country_name (char) : DEL + +# NOTHING TO DO + +hr_attendance / hr.attendance / out_location (char) : NEW + +# DONE: renamed from out_city + +hr_attendance / hr.attendance / validated_overtime_hours (float): now a function + +# DONE: computed in pre-migration + +hr_attendance / hr.attendance.overtime / _order : _order is now 'time_start' ('date desc') +hr_attendance / hr.attendance.overtime / adjustment (boolean) : DEL + +# NOTHING TO DO + +hr_attendance / hr.attendance.overtime / date (date) : now required + +# DONE: filled in pre-migration if unset + +hr_attendance / hr.attendance.overtime / duration_real (float) : DEL + +# DONE: renamed to manual_duration + +hr_attendance / hr.attendance.overtime.line / amount_rate (float) : NEW required, hasdefault: default + +# NOTHING TO DO + +hr_attendance / hr.attendance.overtime.line / manual_duration (float) : NEW hasdefault: compute + +# DONE: renamed from duration_real + +hr_attendance / hr.attendance.overtime.line / rule_ids (many2many) : NEW relation: hr.attendance.overtime.rule + +# NOTHING TO DO + +hr_attendance / hr.attendance.overtime.line / status (selection) : NEW required, selection_keys: ['approved', 'refused', 'to_approve'], hasdefault: compute + +# DONE: precreated with value approved + +hr_attendance / hr.attendance.overtime.line / time_start (datetime) : NEW +hr_attendance / hr.attendance.overtime.line / time_stop (datetime) : NEW + +# DONE: set to check_in, check_out of some attendance of same employee on same date + +hr_attendance / hr.attendance.overtime.rule / amount_rate (float) : NEW hasdefault: default +hr_attendance / hr.attendance.overtime.rule / base_off (selection) : NEW required, selection_keys: ['quantity', 'timing'], hasdefault: default +hr_attendance / hr.attendance.overtime.rule / description (html) : NEW +hr_attendance / hr.attendance.overtime.rule / employee_tolerance (float) : NEW +hr_attendance / hr.attendance.overtime.rule / employer_tolerance (float) : NEW +hr_attendance / hr.attendance.overtime.rule / expected_hours (float) : NEW +hr_attendance / hr.attendance.overtime.rule / expected_hours_from_contract (boolean): NEW hasdefault: default +hr_attendance / hr.attendance.overtime.rule / name (char) : NEW required +hr_attendance / hr.attendance.overtime.rule / paid (boolean) : NEW +hr_attendance / hr.attendance.overtime.rule / quantity_period (selection) : NEW selection_keys: ['day', 'week'], hasdefault: default +hr_attendance / hr.attendance.overtime.rule / resource_calendar_id (many2one): NEW relation: resource.calendar +hr_attendance / hr.attendance.overtime.rule / ruleset_id (many2one) : NEW relation: hr.attendance.overtime.ruleset, required +hr_attendance / hr.attendance.overtime.rule / sequence (integer) : NEW hasdefault: default +hr_attendance / hr.attendance.overtime.rule / timing_start (float) : NEW hasdefault: default +hr_attendance / hr.attendance.overtime.rule / timing_stop (float) : NEW hasdefault: default +hr_attendance / hr.attendance.overtime.rule / timing_type (selection) : NEW selection_keys: ['leave', 'non_work_days', 'schedule', 'work_days'], hasdefault: default +hr_attendance / hr.attendance.overtime.ruleset / active (boolean) : NEW hasdefault: default +hr_attendance / hr.attendance.overtime.ruleset / company_id (many2one) : NEW relation: res.company, hasdefault: default +hr_attendance / hr.attendance.overtime.ruleset / country_id (many2one) : NEW relation: res.country, hasdefault: default +hr_attendance / hr.attendance.overtime.ruleset / description (html) : NEW +hr_attendance / hr.attendance.overtime.ruleset / name (char) : NEW required +hr_attendance / hr.attendance.overtime.ruleset / rate_combination_mode (selection): NEW required, selection_keys: ['max', 'sum'], hasdefault: default +hr_attendance / hr.attendance.overtime.ruleset / rule_ids (one2many) : NEW relation: hr.attendance.overtime.rule +hr_attendance / hr.employee / overtime_ids (one2many) : relation is now 'hr.attendance.overtime.line' ('hr.attendance.overtime') [nothing to do] +hr_attendance / hr.version / ruleset_id (many2one) : NEW relation: hr.attendance.overtime.ruleset, hasdefault: default +hr_attendance / res.company / attendance_device_tracking (boolean): NEW hasdefault: default + +# NOTHING TO DO + +---XML records in module 'hr_attendance'--- +NEW hr.attendance.overtime.rule: hr_attendance.hr_attendance_overtime_employee_schedule_rule (noupdate) +NEW hr.attendance.overtime.rule: hr_attendance.hr_attendance_overtime_non_working_days_rule (noupdate) +NEW hr.attendance.overtime.ruleset: hr_attendance.hr_attendance_default_ruleset (noupdate) +NEW ir.actions.act_window: hr_attendance.hr_attendance_overtime_rule_action +NEW ir.actions.act_window: hr_attendance.hr_attendance_overtime_ruleset_action +DEL ir.actions.act_window: hr_attendance.hr_attendance_overtime_action +DEL ir.actions.client: hr_attendance.hr_attendance_action_install_kiosk_pwa +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_line_officer +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_line_own_reader +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_rule_admin +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_rule_officer +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_rule_user +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_ruleset_admin +NEW ir.model.access: hr_attendance.access_hr_attendance_overtime_ruleset_hr_manager +NEW ir.model.access: hr_attendance.access_hr_attendance_own_reader +DEL ir.model.access: hr_attendance.access_hr_attendance_admin_overtime +DEL ir.model.access: hr_attendance.access_hr_attendance_officer_overtime +DEL ir.model.access: hr_attendance.access_hr_attendance_overtime_user +NEW ir.model.constraint: hr_attendance.constraint_hr_attendance_overtime_line_overtime_start_before_end +NEW ir.model.constraint: hr_attendance.constraint_hr_attendance_overtime_rule_timing_start_is_hour +NEW ir.model.constraint: hr_attendance.constraint_hr_attendance_overtime_rule_timing_stop_is_hour +NEW ir.rule: hr_attendance.hr_attendance_overtime_line_rule_admin (noupdate) +NEW ir.rule: hr_attendance.hr_attendance_overtime_line_rule_employee_company (noupdate) +NEW ir.rule: hr_attendance.hr_attendance_overtime_line_rule_officer (noupdate) +NEW ir.rule: hr_attendance.hr_attendance_overtime_line_rule_own_reader (noupdate) +NEW ir.rule: hr_attendance.hr_attendance_overtime_ruleset_rule (noupdate) +NEW ir.rule: hr_attendance.hr_attendance_overtime_ruleset_rule_group_hr_attendance_manager (noupdate) + +# NOTHING TO DO + +DEL ir.rule: hr_attendance.hr_attendance_overtime_rule_employee_company (noupdate) +DEL ir.rule: hr_attendance.hr_attendance_rule_attendance_officer_overtime_restrict (noupdate) +DEL ir.rule: hr_attendance.hr_attendance_rule_attendance_overtime_admin (noupdate) +DEL ir.rule: hr_attendance.hr_attendance_rule_attendance_overtime_simple_user (noupdate) + +# DONE: deleted in post-migration + +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_attendance_reporting +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_configuration +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_employee +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_onboarding +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_overtime_rulesets +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_overview +NEW ir.ui.menu: hr_attendance.menu_hr_attendance_view_dashboard +DEL ir.ui.menu: hr_attendance.menu_hr_attendance_view_attendances +NEW ir.ui.view: hr_attendance.hr_attendance_employee_simple_form_view +NEW ir.ui.view: hr_attendance.hr_attendance_overtime_rule_view_form +NEW ir.ui.view: hr_attendance.hr_attendance_overtime_rule_view_list +NEW ir.ui.view: hr_attendance.hr_attendance_overtime_ruleset_view_filter +NEW ir.ui.view: hr_attendance.hr_attendance_overtime_ruleset_view_form +NEW ir.ui.view: hr_attendance.hr_attendance_overtime_ruleset_view_list +NEW ir.ui.view: hr_attendance.hr_employee_public_view_form +NEW ir.ui.view: hr_attendance.hr_employee_search_view +DEL ir.ui.view: hr_attendance.hr_attendance_overtime_view_pivot +DEL ir.ui.view: hr_attendance.hr_attendance_validated_hours_employee_simple_tree_view +DEL ir.ui.view: hr_attendance.hr_user_view_form +DEL ir.ui.view: hr_attendance.view_attendance_overtime_graph +DEL ir.ui.view: hr_attendance.view_attendance_overtime_search +DEL ir.ui.view: hr_attendance.view_attendance_overtime_tree +NEW res.groups: hr_attendance.group_hr_attendance_user +NEW res.groups.privilege: hr_attendance.res_groups_privilege_attendances + +# NOTHING TO DO