summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/hr_holidays/models/hr_leave.py34
-rw-r--r--addons/hr_holidays/tests/test_leave_requests.py56
2 files changed, 74 insertions, 16 deletions
diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py
index fb97d73eb84..a63fd51641e 100644
--- a/addons/hr_holidays/models/hr_leave.py
+++ b/addons/hr_holidays/models/hr_leave.py
@@ -8,7 +8,7 @@ import math
from collections import namedtuple
-from datetime import datetime, time
+from datetime import datetime, date, timedelta, time
from dateutil.rrule import rrule, DAILY
from pytz import timezone, UTC
@@ -356,6 +356,9 @@ class HolidaysRequest(models.Model):
# find last attendance coming before last_day
attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= self.request_date_to.weekday()), attendances[-1] if attendances else default_value)
+ compensated_request_date_from = self.request_date_from
+ compensated_request_date_to = self.request_date_to
+
if self.request_unit_half:
if self.request_date_from_period == 'am':
hour_from = float_to_time(attendance_from.hour_from)
@@ -369,12 +372,15 @@ class HolidaysRequest(models.Model):
elif self.request_unit_custom:
hour_from = self.date_from.time()
hour_to = self.date_to.time()
+ compensated_request_date_from = self._adjust_date_based_on_tz(self.request_date_from, hour_from)
+ compensated_request_date_to = self._adjust_date_based_on_tz(self.request_date_to, hour_to)
else:
hour_from = float_to_time(attendance_from.hour_from)
hour_to = float_to_time(attendance_to.hour_to)
- self.date_from = timezone(self.tz).localize(datetime.combine(self.request_date_from, hour_from)).astimezone(UTC).replace(tzinfo=None)
- self.date_to = timezone(self.tz).localize(datetime.combine(self.request_date_to, hour_to)).astimezone(UTC).replace(tzinfo=None)
+ self.date_from = timezone(self.tz).localize(datetime.combine(compensated_request_date_from, hour_from)).astimezone(UTC).replace(tzinfo=None)
+ self.date_to = timezone(self.tz).localize(datetime.combine(compensated_request_date_to, hour_to)).astimezone(UTC).replace(tzinfo=None)
+
self._onchange_leave_dates()
@api.onchange('request_unit_half')
@@ -571,6 +577,28 @@ class HolidaysRequest(models.Model):
return {'days': hours / (today_hours or HOURS_PER_DAY), 'hours': hours}
+ def _adjust_date_based_on_tz(self, leave_date, hour):
+ """ request_date_{from,to} are local to the user's tz but hour_{from,to} are in UTC.
+
+ In some cases they are combined (assuming they are in the same tz) as a datetime. When
+ that happens it's possible we need to adjust one of the dates. This function adjust the
+ date, so that it can be passed to datetime().
+
+ E.g. a leave in US/Pacific for one day:
+ - request_date_from: 1st of Jan
+ - request_date_to: 1st of Jan
+ - hour_from: 15:00 (7:00 local)
+ - hour_to: 03:00 (19:00 local) <-- this happens on the 2nd of Jan in UTC
+ """
+ user_tz = timezone(self.env.user.tz if self.env.user.tz else 'UTC')
+ request_date_to_utc = UTC.localize(datetime.combine(leave_date, hour)).astimezone(user_tz).replace(tzinfo=None)
+ if request_date_to_utc.date() < leave_date:
+ return leave_date + timedelta(days=1)
+ elif request_date_to_utc.date() > leave_date:
+ return leave_date - timedelta(days=1)
+ else:
+ return leave_date
+
####################################################
# ORM Overrides methods
####################################################
diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py
index a0cbcc0c79f..fd9652d6d9a 100644
--- a/addons/hr_holidays/tests/test_leave_requests.py
+++ b/addons/hr_holidays/tests/test_leave_requests.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, date
from dateutil.relativedelta import relativedelta
+from pytz import timezone, UTC
from odoo import fields
from odoo.exceptions import ValidationError
@@ -366,23 +367,52 @@ class TestLeaveRequests(TestHrHolidaysCommon):
leave1.action_validate()
self.assertEqual(leave1.number_of_hours_display, 24)
- @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
- def test_leave_defaults_with_timezones(self):
- """ Make sure that leaves start with correct defaults for non-UTC timezones """
- self.user_employee.tz = 'US/Pacific'
+ def _test_leave_with_tz(self, tz, local_date_from, local_date_to, number_of_days):
+ self.user_employee.tz = tz
+ tz = timezone(tz)
# Mimic what is done by the calendar widget when clicking on a day. It
# will take the local datetime from 7:00 to 19:00 and then convert it
# to UTC before sending it. Values here are for PST (UTC -8) and
# represent a leave on 2019/1/1 from 7:00 to 19:00 local time.
- webclient_values = {
- 'date_from': datetime(2019, 1, 1, 15, 0, 0),
- 'date_to': datetime(2019, 1, 2, 3, 0), # note that this is the next day in UTC
+ values = {
+ 'date_from': tz.localize(local_date_from).astimezone(UTC).replace(tzinfo=None),
+ 'date_to': tz.localize(local_date_to).astimezone(UTC).replace(tzinfo=None), # note that this can be the next day in UTC
}
- values = self.env['hr.leave'].sudo(self.user_employee_id)._default_get_request_parameters(webclient_values)
+ values.update(self.env['hr.leave'].with_user(self.user_employee_id)._default_get_request_parameters(values))
+
+ # Dates should be local to the user
+ self.assertEqual(values['request_date_from'], local_date_from.date())
+ self.assertEqual(values['request_date_to'], local_date_to.date())
+
+ values.update({
+ 'name': 'Test',
+ 'employee_id': self.employee_emp_id,
+ 'holiday_status_id': self.holidays_type_1.id,
+ })
+ leave = self.env['hr.leave'].with_user(self.user_employee_id).new(values)
+ leave._onchange_request_parameters()
+ self.assertEqual(leave.number_of_days, number_of_days)
- # Dates should be local to the user, they do not and cannot be
- # converted using the user's timezone.
- first_of_january = date(2019, 1, 1)
- self.assertEqual(values['request_date_from'], first_of_january)
- self.assertEqual(values['request_date_to'], first_of_january)
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_leave_defaults_with_timezones(self):
+ """ Make sure that leaves start with correct defaults for non-UTC timezones """
+ timezones_to_test = ('UTC', 'Pacific/Midway', 'US/Pacific', 'Asia/Taipei', 'Pacific/Kiritimati') # UTC, UTC -11, UTC -8, UTC +8, UTC +14
+
+ # January 2020
+ # Su Mo Tu We Th Fr Sa
+ # 1 2 3 4
+ # 5 6 7 8 9 10 11
+ # 12 13 14 15 16 17 18
+ # 19 20 21 22 23 24 25
+ # 26 27 28 29 30 31
+ local_date_from = datetime(2019, 1, 1, 7, 0, 0)
+ local_date_to = datetime(2019, 1, 1, 19, 0, 0)
+ for tz in timezones_to_test:
+ self._test_leave_with_tz(tz, local_date_from, local_date_to, 1)
+
+ # We, Th, Fr, Mo, Tu, We => 6 days
+ local_date_from = datetime(2019, 1, 1, 7, 0, 0)
+ local_date_to = datetime(2019, 1, 8, 19, 0, 0)
+ for tz in timezones_to_test:
+ self._test_leave_with_tz(tz, local_date_from, local_date_to, 6)