[FIX] resource: Find intervals in the resource timezone
With a user with timezone "Europe/Brussels", add a slot on the gantt view where the preceding day is a working day. The default starting time will be the previous day. Reason: the default starting time is midnight, adjusted to the closest attendance interval in the employee's calendar. However, midnight in "Europe/Brussels" is actually 10pm UTC the previous day. The method adjusting the datetime to the closest attendance only checks within the corresponding day. Two ways to fix this: 1) the business code calling `_adjust_to_calendar` should timezone the datetimes itself. To avoid similar bugs in the future, the method should be modified to only accept timezoned datetimes. 2) Change `_adjust_to_calendar` to convert datetimes it was given to the resource's timezone and consider the dates in those converted datetimes. Option 2 is chosen because it seems to take care of the problem at a lower level which allows the business code to not think about those timezone details. While this is technically an API change (in a stable release), the method was introduced recently by b058ca1 as a fix for an already broken method. closes odoo/odoo#53582 X-original-commit: c6cecac60360a1d8b0c62ee155a818a3a28505e1 Related: odoo/enterprise#11399 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
852594473d
commit
103feca347
@@ -3,7 +3,6 @@
|
||||
|
||||
from collections import defaultdict
|
||||
import math
|
||||
import logging
|
||||
from datetime import datetime, time, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from dateutil.rrule import rrule, DAILY, WEEKLY
|
||||
@@ -20,8 +19,6 @@ from odoo.tools.float_utils import float_round
|
||||
from odoo.tools import date_utils, float_utils
|
||||
from .resource_mixin import timezone_datetime
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# Default hour per day value. The one should
|
||||
# only be used when the one from the calendar
|
||||
# is not available.
|
||||
@@ -475,15 +472,16 @@ class ResourceCalendar(models.Model):
|
||||
def interval_dt(interval):
|
||||
return interval[1 if match_end else 0]
|
||||
|
||||
if not dt.tzinfo or search_range and not (search_range[0].tzinfo and search_range[1].tzinfo):
|
||||
raise ValueError('Provided datetimes needs to be timezoned')
|
||||
dt = dt.astimezone(timezone(self.tz))
|
||||
|
||||
if not search_range:
|
||||
range_start = dt + relativedelta(hour=0, minute=0, second=0)
|
||||
range_end = dt + relativedelta(days=1, hour=0, minute=0, second=0)
|
||||
else:
|
||||
range_start, range_end = search_range
|
||||
|
||||
if not (dt.tzinfo and range_start.tzinfo and range_end.tzinfo):
|
||||
raise ValueError('Provided datetimes needs to be timezoned')
|
||||
|
||||
if not range_start <= dt <= range_end:
|
||||
return None
|
||||
work_intervals = sorted(
|
||||
@@ -778,7 +776,8 @@ class ResourceResource(models.Model):
|
||||
for resource in self:
|
||||
calendar_start = resource.calendar_id._get_closest_work_time(start, resource=resource)
|
||||
search_range = None
|
||||
if calendar_start and start.date() == end.date():
|
||||
tz = timezone(resource.tz)
|
||||
if calendar_start and start.astimezone(tz).date() == end.astimezone(tz).date():
|
||||
# Make sure to only search end after start
|
||||
search_range = (
|
||||
start,
|
||||
|
||||
@@ -438,6 +438,10 @@ class TestCalendar(TestResourceCommon):
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt, resource=self.john.resource_id)
|
||||
self.assertEqual(calendar_dt, start, "It should have taken john's specific attendances")
|
||||
|
||||
dt = datetime_tz(2020, 4, 4, 1, 0, 0, tzinfo='UTC') # The next day in UTC, but still the 3rd in john's timezone (America/Los_Angeles)
|
||||
start = datetime_tz(2020, 4, 3, 16, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt, resource=self.john.resource_id)
|
||||
self.assertEqual(calendar_dt, start, "It should have found the attendance on the 3rd April")
|
||||
|
||||
class TestResMixin(TestResourceCommon):
|
||||
|
||||
@@ -487,6 +491,21 @@ class TestResMixin(TestResourceCommon):
|
||||
datetime_tz(2020, 4, 3, 13, 0, 0, tzinfo=self.john.tz),
|
||||
))
|
||||
|
||||
def test_adjust_calendar_timezone_after(self):
|
||||
# Calendar:
|
||||
# Tuesdays 8-16
|
||||
# Fridays 8-13 and 16-23
|
||||
tz = 'Europe/Brussels'
|
||||
self.john.tz = tz
|
||||
result = self.john._adjust_to_calendar(
|
||||
datetime(2020, 4, 2, 23, 0, 0), # The previous day in UTC, but the 3rd in Europe/Brussels
|
||||
datetime(2020, 4, 3, 20, 0, 0),
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
datetime(2020, 4, 3, 6, 0, 0),
|
||||
datetime(2020, 4, 3, 21, 0, 0),
|
||||
), "It should have found a starting time the 3rd")
|
||||
|
||||
def test_work_days_data(self):
|
||||
# Looking at Jean's calendar
|
||||
|
||||
|
||||
Reference in New Issue
Block a user