-
-
Notifications
You must be signed in to change notification settings - Fork 670
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Carmen Bianca BAKKER <[email protected]>
- Loading branch information
1 parent
5440859
commit 75568a3
Showing
4 changed files
with
191 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ | |
# SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
from . import models | ||
from .hooks import post_load_hook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,5 @@ | |
"data": [ | ||
"views/resource_calendar_views.xml", | ||
], | ||
"demo": [], | ||
"qweb": [], | ||
"post_load": "post_load_hook", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
# SPDX-FileCopyrightText: 2024 Coop IT Easy SC | ||
# SPDX-FileCopyrightText: Odoo SA | ||
# | ||
# SPDX-License-Identifier: AGPL-3.0-or-later AND LGPL-3.0-or-later | ||
|
||
# flake8: noqa | ||
|
||
import datetime | ||
import itertools | ||
from collections import defaultdict | ||
|
||
from dateutil.rrule import DAILY, rrule | ||
from pytz import timezone, utc | ||
|
||
from odoo.osv import expression | ||
|
||
from odoo.addons.resource.models.resource import ( | ||
Intervals, | ||
ResourceCalendar, | ||
float_to_time, | ||
) | ||
|
||
|
||
def post_load_hook(): | ||
if not hasattr(ResourceCalendar, "_attendance_intervals_batch_original"): | ||
ResourceCalendar._attendance_intervals_batch_original = ( | ||
ResourceCalendar._attendance_intervals_batch | ||
) | ||
|
||
# fmt: off | ||
def _new_attendance_intervals_batch(self, start_dt, end_dt, resources=None, domain=None, tz=None): | ||
assert start_dt.tzinfo and end_dt.tzinfo | ||
self.ensure_one() | ||
|
||
if not resources: | ||
resources = self.env['resource.resource'] | ||
resources_list = [resources] | ||
else: | ||
resources_list = list(resources) + [self.env['resource.resource']] | ||
resource_ids = [r.id for r in resources_list] | ||
domain = domain if domain is not None else [] | ||
domain = expression.AND([domain, [ | ||
('calendar_id', '=', self.id), | ||
('resource_id', 'in', resource_ids), | ||
('display_type', '=', False), | ||
]]) | ||
|
||
attendances = self.env['resource.calendar.attendance'].search(domain) | ||
# Since we only have one calendar to take in account | ||
# Group resources per tz they will all have the same result | ||
resources_per_tz = defaultdict(list) | ||
for resource in resources_list: | ||
resources_per_tz[tz or timezone((resource or self).tz)].append(resource) | ||
# Resource specific attendances | ||
attendance_per_resource = defaultdict(lambda: self.env['resource.calendar.attendance']) | ||
# Calendar attendances per day of the week | ||
# * 7 days per week * 2 for two week calendars | ||
attendances_per_day = [self.env['resource.calendar.attendance']] * 7 * 2 | ||
weekdays = set() | ||
for attendance in attendances: | ||
if attendance.resource_id: | ||
attendance_per_resource[attendance.resource_id] |= attendance | ||
weekday = int(attendance.dayofweek) | ||
weekdays.add(weekday) | ||
if self.two_weeks_calendar: | ||
weektype = int(attendance.week_type) | ||
attendances_per_day[weekday + 7 * weektype] |= attendance | ||
else: | ||
attendances_per_day[weekday] |= attendance | ||
attendances_per_day[weekday + 7] |= attendance | ||
|
||
start = start_dt.astimezone(utc) | ||
end = end_dt.astimezone(utc) | ||
bounds_per_tz = { | ||
tz: (start_dt.astimezone(tz), end_dt.astimezone(tz)) | ||
for tz in resources_per_tz.keys() | ||
} | ||
# Use the outer bounds from the requested timezones | ||
for tz, bounds in bounds_per_tz.items(): | ||
start = min(start, bounds[0].replace(tzinfo=utc)) | ||
end = max(end, bounds[1].replace(tzinfo=utc)) | ||
# Generate once with utc as timezone | ||
days = rrule(DAILY, start.date(), until=end.date(), byweekday=weekdays) | ||
ResourceCalendarAttendance = self.env['resource.calendar.attendance'] | ||
base_result = [] | ||
per_resource_result = defaultdict(list) | ||
for day in days: | ||
# begin change | ||
if not self._day_in_calendar(day): | ||
continue | ||
# end change | ||
week_type = ResourceCalendarAttendance.get_week_type(day) | ||
attendances = attendances_per_day[day.weekday() + 7 * week_type] | ||
for attendance in attendances: | ||
if (attendance.date_from and day.date() < attendance.date_from) or\ | ||
(attendance.date_to and attendance.date_to < day.date()): | ||
continue | ||
day_from = datetime.combine(day, float_to_time(attendance.hour_from)) | ||
day_to = datetime.combine(day, float_to_time(attendance.hour_to)) | ||
if attendance.resource_id: | ||
per_resource_result[attendance.resource_id].append((day_from, day_to, attendance)) | ||
else: | ||
base_result.append((day_from, day_to, attendance)) | ||
|
||
# Copy the result localized once per necessary timezone | ||
# Strictly speaking comparing start_dt < time or start_dt.astimezone(tz) < time | ||
# should always yield the same result. however while working with dates it is easier | ||
# if all dates have the same format | ||
result_per_tz = { | ||
tz: [(max(bounds_per_tz[tz][0], tz.localize(val[0])), | ||
min(bounds_per_tz[tz][1], tz.localize(val[1])), | ||
val[2]) | ||
for val in base_result] | ||
for tz in resources_per_tz.keys() | ||
} | ||
result_per_resource_id = dict() | ||
for tz, resources in resources_per_tz.items(): | ||
res = result_per_tz[tz] | ||
res_intervals = Intervals(res) | ||
for resource in resources: | ||
if resource in per_resource_result: | ||
resource_specific_result = [(max(bounds_per_tz[tz][0], tz.localize(val[0])), min(bounds_per_tz[tz][1], tz.localize(val[1])), val[2]) | ||
for val in per_resource_result[resource]] | ||
result_per_resource_id[resource.id] = Intervals(itertools.chain(res, resource_specific_result)) | ||
else: | ||
result_per_resource_id[resource.id] = res_intervals | ||
return result_per_resource_id | ||
|
||
# fmt: on | ||
|
||
def _day_in_calendar(self, day): | ||
return True | ||
|
||
ResourceCalendar._attendance_intervals_batch = _new_attendance_intervals_batch | ||
ResourceCalendar._day_in_calendar = _day_in_calendar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters