diff --git a/hr_employee_stats_sheet/models/hr_employee_stats.py b/hr_employee_stats_sheet/models/hr_employee_stats.py index 7b6af5d..e03f908 100644 --- a/hr_employee_stats_sheet/models/hr_employee_stats.py +++ b/hr_employee_stats_sheet/models/hr_employee_stats.py @@ -1,7 +1,9 @@ import logging +import pytz from odoo import api, fields, models from datetime import timedelta +from pytz import utc _logger = logging.getLogger(__name__) @@ -169,8 +171,40 @@ class HrEmployeeStats(models.Model): stat.is_public_holiday = False continue stat.dayofweek = int(stat.date.strftime("%u")) - 1 - stat.is_public_holiday = bool(stat.sheet_id.employee_id._get_public_holidays(stat.date, stat.date - timedelta(days=1))) + stat.is_public_holiday = stat._is_public_holiday_accordig_to_employe_tz() + def _convert_to_employee_tz(self, date): + """Convert a UTC datetime to the employee's timezone datetime.""" + self.ensure_one() + if not date: + return None + employee_tz = pytz.timezone(self.employee_id.tz or "UTC") + if date.tzinfo is None: + dt = pytz.utc.localize(date) + return dt.astimezone(employee_tz) + + def _is_public_holiday_accordig_to_employe_tz(self): + self.ensure_one() + if not self.date or not self.employee_id: + return False + #get public holidays for the employee + public_holidays = self.employee_id._get_public_holidays( + self.date, self.date + ) + if not public_holidays: + return False + ph = public_holidays[0] + # Convert public holiday to the employee timezone + ph_datetime_from_tz = self._convert_to_employee_tz(ph.date_from) + ph_datetime_to_tz = self._convert_to_employee_tz(ph.date_to) + # Convert datetime to date + ph_date_from = ph_datetime_from_tz.date() + ph_date_to = ph_datetime_to_tz.date() + # Check if the stat date falls within the public holiday range after conversion in employee tz + if ph_date_from <= self.date <= ph_date_to: + return True + else: + return False def _get_gap_hours(self, total_hours, total_recovery_hours, total_leave_hours, total_planned_hours): self.ensure_one() balance = ( diff --git a/hr_employee_stats_sheet/tests/test_employee_stats.py b/hr_employee_stats_sheet/tests/test_employee_stats.py index 2594b3b..ef94f46 100644 --- a/hr_employee_stats_sheet/tests/test_employee_stats.py +++ b/hr_employee_stats_sheet/tests/test_employee_stats.py @@ -1,7 +1,8 @@ from odoo.tests.common import TransactionCase from odoo.tests import tagged -from datetime import date, timedelta +from datetime import date, timedelta, datetime from odoo.exceptions import UserError +from odoo.fields import Date @tagged("post_install", "-at_install") class TestHrEmployeeStatsRecovery(TransactionCase): @@ -19,6 +20,7 @@ class TestHrEmployeeStatsRecovery(TransactionCase): self.employee = self.env['hr.employee'].create({ 'name': 'Camille', 'user_id': self.user.id, + 'tz': 'Europe/Paris', }) self.base_calendar = self.env['resource.calendar'].create({ 'name': 'Default Calendar', @@ -242,6 +244,33 @@ class TestHrEmployeeStatsRecovery(TransactionCase): recovery_allocation = self.env["hr.leave.allocation"].search([("timesheet_sheet_id","=",timesheet_sheet_2.id)]) self.assertEqual(len(recovery_allocation), 1, "There should be one recovery") + def test_public_holiday(self): + # create a public holiday + self.env["resource.calendar.leaves"].create( + { + "name": "1 mai 2025", + "date_from": datetime(2025,4,30,22,0,0), + "date_to": datetime(2025,5,1,21,0,0), + } + ) + #create 5 stats of 7h each including the public holiday on 1st may + stats = self._create_stats(Date.to_date("2025-04-28"), 5, 7) + for stat in stats: + stat._compute_dayofweek() + stat._compute_hours() + #create 1 timesheet sheet from monday to friday including the public holiday on 1st may + timesheet_sheet = self.env['hr_timesheet.sheet'].create({ + 'employee_id': self.employee.id, + 'date_start': "2025-04-28", + 'date_end': "2025-05-04", + }) + self.assertEqual(timesheet_sheet.timesheet_sheet_gap_hours, 7, "timesheet_sheet_gap_hours should be 7",) + self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 8.75, "timesheet_sheet_recovery_hours should be 8,75",) + + timesheet_sheet.action_generate_recovery_allocation() + recovery_allocation = self.env["hr.leave.allocation"].search([("timesheet_sheet_id","=",timesheet_sheet.id)]) + self.assertEqual(len(recovery_allocation), 1, "Il doit y avoir une allocation de récupération générée") + self.assertEqual(recovery_allocation.number_of_days,1.25, "The recovery allocation should be 1,25 days")