From f6be42514e73e618f1702646581814dab0b054d5 Mon Sep 17 00:00:00 2001 From: arpi-odoo Date: Mon, 27 Oct 2025 15:41:17 +0100 Subject: [PATCH 1/8] [FIX] hr: new contract button invisible --- .../src/components/button_new_contract/button_new_contract.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr/static/src/components/button_new_contract/button_new_contract.xml b/addons/hr/static/src/components/button_new_contract/button_new_contract.xml index 8abdd473fe7e2..0c640d5b88d4c 100644 --- a/addons/hr/static/src/components/button_new_contract/button_new_contract.xml +++ b/addons/hr/static/src/components/button_new_contract/button_new_contract.xml @@ -3,7 +3,7 @@ + t-ref="datetime-picker-target-new-contract" t-if="props.record.resId">New Contract From 08e116215e2eeacd46ac16661762db931b9740d8 Mon Sep 17 00:00:00 2001 From: arpi-odoo Date: Tue, 28 Oct 2025 10:12:15 +0100 Subject: [PATCH 2/8] [IMP] hr_skills: cleanup certification reporting --- ...hr_employee_certification_report_views.xml | 24 +++++++++++++++++++ .../report/hr_employee_skill_report_views.xml | 2 +- addons/hr_skills/views/hr_views.xml | 8 ++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/addons/hr_skills/report/hr_employee_certification_report_views.xml b/addons/hr_skills/report/hr_employee_certification_report_views.xml index 779796d0449fe..079ef1f790fd9 100644 --- a/addons/hr_skills/report/hr_employee_certification_report_views.xml +++ b/addons/hr_skills/report/hr_employee_certification_report_views.xml @@ -44,4 +44,28 @@ + + + Certification + hr.employee.certification.report + + list,pivot + { + 'search_default_employee': 1, + } + +

+

+ This report will give you an overview of the certification per Employee. + Create them in configuration and add them on the Employee. +

+
+
+ + diff --git a/addons/hr_skills/report/hr_employee_skill_report_views.xml b/addons/hr_skills/report/hr_employee_skill_report_views.xml index 86b997da5ffbb..bddca497eb6ef 100644 --- a/addons/hr_skills/report/hr_employee_skill_report_views.xml +++ b/addons/hr_skills/report/hr_employee_skill_report_views.xml @@ -89,6 +89,6 @@ id="hr_employee_skill_inventory_report_menu" name="Skills Inventory" action="hr_employee_skill_report_action" - parent="hr.hr_menu_hr_reports" + parent="hr_skills.hr_employee_skill_report_menu" sequence="15"/> diff --git a/addons/hr_skills/views/hr_views.xml b/addons/hr_skills/views/hr_views.xml index a9409c02c7582..bfe56b1245eb0 100644 --- a/addons/hr_skills/views/hr_views.xml +++ b/addons/hr_skills/views/hr_views.xml @@ -595,7 +595,7 @@ hr.employee.skill certifications [('is_certification', '=', True), ('company_id', 'in', allowed_company_ids)] - {'show_employee': True} + {'show_employee': True, 'search_default_group_by_type': 1} list,form + + Date: Wed, 29 Oct 2025 14:08:21 +0100 Subject: [PATCH 3/8] [FIX] hr_holidays: fix traceback leave type --- addons/hr_holidays/models/hr_leave_type.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index bf7e17f16f79b..736424b06169b 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -274,14 +274,14 @@ def _search_max_leaves(self, operator, value): return [('id', 'in', valid_leaves)] def _search_virtual_remaining_leaves(self, operator, value): - def is_valid(leave_type): - return not leave_type.requires_allocation or op(leave_type.virtual_remaining_leaves, value) op = PY_OPERATORS.get(operator) if not op: return NotImplemented if operator != 'in': value = float(value) leave_types = self.env['hr.leave.type'].search([]) + def is_valid(leave_type): + return not leave_type.requires_allocation or op(leave_type.virtual_remaining_leaves) return [('id', 'in', leave_types.filtered(is_valid).ids)] @api.depends_context('employee_id', 'default_employee_id', 'leave_date_from', 'default_date_from') From 6dd6ab8ec9df6c4700685a11107b59dace427c40 Mon Sep 17 00:00:00 2001 From: arpi-odoo Date: Wed, 29 Oct 2025 14:44:15 +0100 Subject: [PATCH 4/8] [FIX] hr: fix delete version --- addons/hr/models/hr_employee.py | 3 +-- addons/hr/tests/test_hr_version.py | 21 --------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/addons/hr/models/hr_employee.py b/addons/hr/models/hr_employee.py index 87bd2596145bc..0a0900f3f897d 100644 --- a/addons/hr/models/hr_employee.py +++ b/addons/hr/models/hr_employee.py @@ -548,8 +548,7 @@ def _compute_legal_name(self): @api.depends('current_version_id') @api.depends_context('version_id') def _compute_version_id(self): - context_version_id = self.env.context.get('version_id', False) - context_version = self.env['hr.version'].browse(context_version_id).exists() if context_version_id else self.env['hr.version'] + context_version = self.env['hr.version'].browse(self.env.context.get('version_id', False)) for employee in self: if context_version.employee_id == self: diff --git a/addons/hr/tests/test_hr_version.py b/addons/hr/tests/test_hr_version.py index 44330dfe9e5f3..d75fd71800740 100644 --- a/addons/hr/tests/test_hr_version.py +++ b/addons/hr/tests/test_hr_version.py @@ -523,27 +523,6 @@ def test_multi_edit_other_and_contract_date_sync(self): self.assertEqual(version.job_id.id, jobB.id) self.assertEqual(version.contract_date_end, date(2020, 9, 30)) - def test_delete_version(self): - employee = self.env['hr.employee'].create({ - 'name': 'John Doe', - 'date_version': '2020-01-01', - }) - v1 = employee.version_id - v2 = employee.create_version({ - 'date_version': '2021-01-01', - }) - v3 = employee.create_version({ - 'date_version': '2022-01-01', - }) - self.assertEqual(employee.current_version_id, v3) - - v3.unlink() - self.assertEqual(employee.current_version_id, v2) - v1.unlink() - self.assertEqual(employee.current_version_id, v2) - with self.assertRaises(ValidationError): - v2.unlink() - def test_multi_edit_multi_employees_no_contract(self): """ Test the multi-edit when there is one version per employee, without contract From 7306cb724ecaa4762ce906cdf60bbf37299f527f Mon Sep 17 00:00:00 2001 From: arpi-odoo Date: Wed, 29 Oct 2025 14:54:31 +0100 Subject: [PATCH 5/8] [FIX] hr: fix various bugs --- addons/hr_holidays/models/hr_leave_type.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index 736424b06169b..bbfa0a8be4a5c 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -280,6 +280,7 @@ def _search_virtual_remaining_leaves(self, operator, value): if operator != 'in': value = float(value) leave_types = self.env['hr.leave.type'].search([]) + def is_valid(leave_type): return not leave_type.requires_allocation or op(leave_type.virtual_remaining_leaves) return [('id', 'in', leave_types.filtered(is_valid).ids)] From 490ac6e5fdee1e3d6d65c4160facd26fe49340d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9lanie=20Peyrat=20=28mepe=29?= Date: Thu, 27 Nov 2025 11:21:02 +0100 Subject: [PATCH 6/8] [FIX] hr_holidays: fix accrual day selection --- .../accrual_level/accrual_levels.scss | 4 +- .../components/day_selection/day_selection.js | 41 ------------------- .../views/hr_leave_accrual_views.xml | 8 ++-- 3 files changed, 6 insertions(+), 47 deletions(-) delete mode 100644 addons/hr_holidays/static/src/components/day_selection/day_selection.js diff --git a/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss b/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss index 9cec634e1b2dd..5ea81942c9be6 100644 --- a/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss +++ b/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss @@ -10,7 +10,7 @@ } .o_accrual { - .o_field_accrual, .o_field_selection, .o_field_day_selection, .o_field_filterable_selection { + .o_field_accrual, .o_field_selection, .o_field_filterable_selection { width: fit-content !important; &:not(.o_readonly_modifier) > *:first-child { @@ -20,7 +20,7 @@ field-sizing: content; } - &:not(.o_field_selection, .o_field_day_selection, .o_field_filterable_selection) > *:first-child { + &:not(.o_field_selection, .o_field_filterable_selection) > *:first-child { max-width: 8ch; } } diff --git a/addons/hr_holidays/static/src/components/day_selection/day_selection.js b/addons/hr_holidays/static/src/components/day_selection/day_selection.js deleted file mode 100644 index 05288069a543b..0000000000000 --- a/addons/hr_holidays/static/src/components/day_selection/day_selection.js +++ /dev/null @@ -1,41 +0,0 @@ -import { registry } from "@web/core/registry"; -import { SelectionField, selectionField } from "@web/views/fields/selection/selection_field"; - -export class DaySelectionField extends SelectionField { - static props = { - ...SelectionField.props, - monthField: String, - }; - /** - * @override - * return the available days in the carryover_month - * e.g. February -> [1, 29], april -> [1, 30] - */ - get options() { - let options = super.options; - const carryover_month = this.props.record.data[this.props.monthField]; - // lastDay is the last day of the current_month for the leap year 2020 - const lastDay = new Date(2020, carryover_month, 0).getDate(); - options = options.filter((option) => option[0] <= lastDay); - return options; - } -} - -export const daySelectionField = { - ...selectionField, - component: DaySelectionField, - extractProps({ attrs }) { - return { - ...selectionField.extractProps(...arguments), - monthField: attrs.month_field, - }; - }, - fieldDependencies: ({ attrs }) => [ - { - name: attrs.month_field, - type: "selection", - }, - ], -}; - -registry.category("fields").add("day_selection", daySelectionField); diff --git a/addons/hr_holidays/views/hr_leave_accrual_views.xml b/addons/hr_holidays/views/hr_leave_accrual_views.xml index d70951f190add..53e2df20c6ff2 100644 --- a/addons/hr_holidays/views/hr_leave_accrual_views.xml +++ b/addons/hr_holidays/views/hr_leave_accrual_views.xml @@ -36,17 +36,17 @@ on the - + of and the - + of on the - + of @@ -192,7 +192,7 @@ options="{'links': {'other': 'carryover_custom_date'}, 'observe': 'carryover'}"/> : the - of Date: Mon, 1 Dec 2025 15:27:08 +0100 Subject: [PATCH 7/8] [IMP] empty From bc1ecc97261927010c755f550e57fa3ba9337073 Mon Sep 17 00:00:00 2001 From: "Hazem (haabo)" Date: Fri, 5 Dec 2025 15:43:49 +0100 Subject: [PATCH 8/8] [FIX] hr_holidays: dynamic accrual days Changed the {day} dropdown to adapt to the selected month. It was previously set to 31 for all months which allowed the user to select the 31st on a 28 or 30 day month for example. task-5392293 --- .../day_month_selection.js | 32 +++++++++++ .../hr_holidays/tests/test_expiring_leaves.py | 57 +++++++++++++++++++ .../views/hr_leave_accrual_views.xml | 8 +-- 3 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 addons/hr_holidays/static/src/components/day_month_selection/day_month_selection.js diff --git a/addons/hr_holidays/static/src/components/day_month_selection/day_month_selection.js b/addons/hr_holidays/static/src/components/day_month_selection/day_month_selection.js new file mode 100644 index 0000000000000..f96120efaa895 --- /dev/null +++ b/addons/hr_holidays/static/src/components/day_month_selection/day_month_selection.js @@ -0,0 +1,32 @@ +import { registry } from "@web/core/registry"; +import { SelectionField, selectionField } from "@web/views/fields/selection/selection_field"; + +export class DayMonthSelectionField extends SelectionField { + static props = { + ...SelectionField.props, + selectedMonth: { type: String, optional: true }, + }; + + + get options() { + const selectedMonth = this.props.record.data[this.props.selectedMonth]; + // The year 2024 is used as a default year as it's a leap year so it will alow us to select the 29th of February (to be more generic) + const date = new Date(2024, selectedMonth, 0); + const days = date.getDate(); + let newChoicesList = Array.from({length: days}, (_, i) => [(i + 1).toString(), (i + 1).toString()]) + return newChoicesList; + } + +} + +export const dayMonthSelectionField = { + ...selectionField, + component: DayMonthSelectionField, + extractProps: (fieldInfo, dynamicInfo) => { + const props = selectionField.extractProps(fieldInfo, dynamicInfo); + props.selectedMonth = fieldInfo.attrs.selected_month; + return props; + }, +}; + +registry.category("fields").add("day_month_selection", dayMonthSelectionField); diff --git a/addons/hr_holidays/tests/test_expiring_leaves.py b/addons/hr_holidays/tests/test_expiring_leaves.py index 90d4232e33b94..d35c73d9d2909 100644 --- a/addons/hr_holidays/tests/test_expiring_leaves.py +++ b/addons/hr_holidays/tests/test_expiring_leaves.py @@ -809,3 +809,60 @@ def test_carried_over_days_expiration_date_2(self): # Assert the number of expiring leaves self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'], 1) + + def test_leap_year_in_accrual_plan(self): + """ + This test case aims to assert that the system correctly handles recurring plan dates that are set on 29th of February + + - We create an accrual plan with a carryover date of 29/2 as well as first_month accrual cutoff set to 29/2 + - We create a new testing accrual plan on 1/8/2023 to be followed by a leap year + - We test the correct accrual on 29/2/2024 to check that the 10 days have been accrued correctly + - We test the correct accrual on 1/7/2024 to check that it doesn't affect the other cycle in a biyearly setup + - Finally, we test the correct accrual on 28/2/2025 to check the number of days was accrued correctly even though + it's a normal year with 1 day less than a leap one + """ + accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({ + 'name': 'Accrual Plan For Test', + 'can_be_carryover': True, + 'carryover_date': 'other', + 'carryover_day': 29, + 'carryover_month': '2', + 'level_ids': [(0, 0, { + 'added_value_type': 'day', + 'milestone_date': 'creation', + 'start_type': 'day', + 'added_value': 10, + 'frequency': 'biyearly', + 'first_month': '2', + 'first_month_day': 29, + 'second_month': '7', + 'second_month_day': 1, + 'action_with_unused_accruals': 'all', + 'accrual_validity': True, + 'accrual_validity_type': 'month', + 'accrual_validity_count': 5, + })], + }) + with freeze_time('2023-07-01'): + allocation = self.env['hr.leave.allocation'].sudo().with_context(tracking_disable=True).create({ + 'name': 'Accrual allocation for employee', + 'accrual_plan_id': accrual_plan.id, + 'employee_id': self.employee_emp.id, + 'holiday_status_id': self.leave_type.id, + 'number_of_days': 0, + 'allocation_type': 'accrual', + }) + allocation.action_approve() + + with freeze_time('2023-07-01'): + allocation._update_accrual() + self.assertEqual(allocation.number_of_days, 0) + with freeze_time('2024-02-29'): + allocation._update_accrual() + self.assertEqual(allocation.number_of_days, 10) + with freeze_time('2025-01-01'): + allocation._update_accrual() + self.assertEqual(allocation.number_of_days, 20) + with freeze_time('2025-02-28'): + allocation._update_accrual() + self.assertEqual(allocation.number_of_days, 30) diff --git a/addons/hr_holidays/views/hr_leave_accrual_views.xml b/addons/hr_holidays/views/hr_leave_accrual_views.xml index 53e2df20c6ff2..ca117ceda936c 100644 --- a/addons/hr_holidays/views/hr_leave_accrual_views.xml +++ b/addons/hr_holidays/views/hr_leave_accrual_views.xml @@ -36,17 +36,17 @@ on the - + of and the - + of on the - + of @@ -192,7 +192,7 @@ options="{'links': {'other': 'carryover_custom_date'}, 'observe': 'carryover'}"/> : the - of