22# Copyright 2022 Tecnativa - Pedro M. Baeza
33# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
44
5+ from datetime import datetime , time , timedelta
6+
57from pytz import UTC
68
79from odoo import api , fields , models
10+ from odoo .osv import expression
811
912from odoo .addons .resource .models .utils import Intervals
1013
@@ -41,6 +44,9 @@ def _calendar_event_busy_intervals(
4144 """Get busy meeting intervals."""
4245 assert start_dt .tzinfo
4346 assert end_dt .tzinfo
47+ interval_tz = start_dt .tzinfo
48+ start_local_date = start_dt .date ()
49+ end_local_date = end_dt .date ()
4450 start_dt , end_dt = (
4551 fields .Datetime .to_string (dt .astimezone (UTC )) for dt in (start_dt , end_dt )
4652 )
@@ -56,8 +62,19 @@ def _calendar_event_busy_intervals(
5662 return Intervals (intervals )
5763 # Simple domain to get all possibly conflicting events in a single
5864 # query; this reduces DB calls and helps the underlying recurring
59- # system (in calendar.event) to work smoothly
60- domain = [("start" , "<=" , end_dt ), ("stop" , ">=" , start_dt )]
65+ # system (in calendar.event) to work smoothly. All-day events are
66+ # stored without start/stop timestamps in some flows, so OR in a
67+ # date-based predicate to catch them too.
68+ domain = expression .OR (
69+ [
70+ [("start" , "<=" , end_dt ), ("stop" , ">=" , start_dt )],
71+ [
72+ ("allday" , "=" , True ),
73+ ("start_date" , "<=" , end_local_date ),
74+ ("stop_date" , ">=" , start_local_date ),
75+ ],
76+ ]
77+ )
6178 # Anyway up to this version, is more performant to restrict as much as possible
6279 # the events to avoid recurrent events.
6380 # TODO: in v14 we should test which approach remains the most performant
@@ -89,15 +106,27 @@ def _calendar_event_busy_intervals(
89106 ):
90107 raise Busy
91108 except Busy :
92- # Add the matched event as a busy interval
109+ # Add the matched event as a busy interval. All-day events
110+ # have no start/stop timestamps in some flows, so derive the
111+ # interval from start_date/stop_date in the analyzer's tz.
112+ if event .allday and event .start_date and event .stop_date :
113+ event_start = interval_tz .localize (
114+ datetime .combine (event .start_date , time .min )
115+ )
116+ event_stop = interval_tz .localize (
117+ datetime .combine (event .stop_date + timedelta (days = 1 ), time .min )
118+ )
119+ else :
120+ event_start = fields .Datetime .context_timestamp (
121+ event , fields .Datetime .to_datetime (event .start )
122+ )
123+ event_stop = fields .Datetime .context_timestamp (
124+ event , fields .Datetime .to_datetime (event .stop )
125+ )
93126 intervals .append (
94127 (
95- fields .Datetime .context_timestamp (
96- event , fields .Datetime .to_datetime (event .start )
97- ),
98- fields .Datetime .context_timestamp (
99- event , fields .Datetime .to_datetime (event .stop )
100- ),
128+ event_start ,
129+ event_stop ,
101130 self .env ["resource.calendar.leaves" ],
102131 )
103132 )
0 commit comments