From 2119995fb7b8a22b89427f956a2b039ba048bfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Thu, 27 Feb 2025 11:08:01 +0100 Subject: [PATCH] [FIX] hr_holidays_natural_period: Define the correct number of days in some use cases. Example use case: - Calendar Monday 16:00-21:00 - Calendar Tuesday-Friday: 09:00-14:00 - Absence from Monday to Sunday in natural day - Number of days: 7 TT55243 --- hr_holidays_natural_period/models/hr_leave.py | 43 +++++++++++++++- .../tests/test_hr_leave.py | 51 ++++++++++++++++++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/hr_holidays_natural_period/models/hr_leave.py b/hr_holidays_natural_period/models/hr_leave.py index 2d7fe792..8ee81cbe 100644 --- a/hr_holidays_natural_period/models/hr_leave.py +++ b/hr_holidays_natural_period/models/hr_leave.py @@ -1,12 +1,27 @@ -# Copyright 2020-2024 Tecnativa - Víctor Martínez +# Copyright 2020-2025 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import api, models +from odoo.addons.hr_holidays.models.hr_leave import DummyAttendance + class HrLeave(models.Model): _inherit = "hr.leave" + def _compute_date_from_to(self): + """Call the compute with the corresponding context to be used later in the + _get_attendances() method. + """ + for holiday in self: + holiday = holiday.with_context( + natural_period=bool( + holiday.holiday_status_id.request_unit == "natural_day" + ) + ) + super(HrLeave, holiday)._compute_date_from_to() + return + def _get_number_of_days(self, date_from, date_to, employee_id): instance = self.with_context( natural_period=bool(self.holiday_status_id.request_unit == "natural_day") @@ -15,6 +30,32 @@ def _get_number_of_days(self, date_from, date_to, employee_id): date_from, date_to, employee_id ) + def _get_attendances(self, employee, request_date_from, request_date_to): + """Overwrite the attendance_to obtained if the leave is natural day, + specifically we define hour_to with the value of attendance_from.hour_from. + Example use case: + attendance_from.hour_from=20:00 + attendance_to.hour_to=14:00 + + This would cause the number of days to be displayed to be incorrect, so "force" + hour_to=20:00 so that the value is correct. + """ + (attendance_from, attendance_to) = super()._get_attendances( + employee, request_date_from, request_date_to + ) + if ( + self.env.context.get("natural_period") + and attendance_to.hour_to < attendance_from.hour_from + ): + attendance_to = DummyAttendance( + attendance_to.hour_from, + attendance_from.hour_from, + attendance_to.dayofweek, + attendance_to.day_period, + attendance_to.week_type, + ) + return (attendance_from, attendance_to) + @api.model_create_multi def create(self, vals_list): """Only in UX an incorrect value is set, recalculate. diff --git a/hr_holidays_natural_period/tests/test_hr_leave.py b/hr_holidays_natural_period/tests/test_hr_leave.py index cd6be330..13b5740d 100644 --- a/hr_holidays_natural_period/tests/test_hr_leave.py +++ b/hr_holidays_natural_period/tests/test_hr_leave.py @@ -1,4 +1,4 @@ -# Copyright 2020-2023 Tecnativa - Víctor Martínez +# Copyright 2020-2025 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from freezegun import freeze_time @@ -77,7 +77,7 @@ def _create_hr_leave(self, leave_type, date_from, date_to): return leave_form.save() @users("test-user") - def test_hr_leave_natural_day(self): + def test_hr_leave_natural_day_01(self): leave_allocation = self._create_leave_allocation(self.leave_type, 5) leave_allocation.action_confirm() leave_allocation.sudo().action_validate() @@ -92,6 +92,53 @@ def test_hr_leave_natural_day(self): self.assertEqual(leave.number_of_days, 4.0) self.assertEqual(leave.number_of_days_display, 4.0) + @users("test-user") + def test_hr_leave_natural_day_02(self): + attendances = [ + (0, 16, 21), + (1, 9, 14), + (2, 9, 14), + (3, 9, 14), + (4, 9, 14), + ] + calendar = ( + self.env["resource.calendar"] + .sudo() + .create( + { + "name": "Test calendar", + "tz": "Europe/Brussels", + "attendance_ids": [ + ( + 0, + 0, + { + "name": index, + "dayofweek": str(att[0]), + "hour_from": att[1], + "hour_to": att[2], + }, + ) + for index, att in enumerate(attendances) + ], + } + ) + ) + self.employee.resource_calendar_id = calendar + leave_allocation = self._create_leave_allocation(self.leave_type, 7) + leave_allocation.action_confirm() + leave_allocation.sudo().action_validate() + res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1] + self.assertEqual(res_leave_type["remaining_leaves"], "7") + self.assertEqual(res_leave_type["virtual_remaining_leaves"], "7") + self.assertEqual(res_leave_type["max_leaves"], "7") + self.assertEqual(res_leave_type["leaves_taken"], "0") + self.assertEqual(res_leave_type["virtual_leaves_taken"], "0") + self.assertEqual(res_leave_type["request_unit"], "natural_day") + leave = self._create_hr_leave(self.leave_type, "2023-01-02", "2023-01-08") + self.assertEqual(leave.number_of_days, 7.0) + self.assertEqual(leave.number_of_days_display, 7.0) + @users("test-user") def test_hr_leave_day(self): leave_allocation = self._create_leave_allocation(self.leave_type_day, 5)