Odoo18-Base/addons/hr_calendar/models/calendar_event.py
2025-01-06 10:57:38 +07:00

84 lines
4.1 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from pytz import UTC
from odoo import api, fields, models
from odoo.addons.resource.models.utils import Intervals, sum_intervals, timezone_datetime
class CalendarEvent(models.Model):
_inherit = "calendar.event"
unavailable_partner_ids = fields.Many2many('res.partner', compute='_compute_unavailable_partner_ids')
@api.depends('partner_ids', 'start', 'stop', 'allday')
def _compute_unavailable_partner_ids(self):
complete_events = self.filtered(
lambda event: event.start and event.stop and (event.stop > event.start or (event.stop >= event.start and event.allday)) and event.partner_ids)
incomplete_event = self - complete_events
incomplete_event.unavailable_partner_ids = []
if not complete_events:
return
event_intervals = complete_events._get_events_interval()
for event, event_interval in event_intervals.items():
# Event_interval is empty when an allday event contains at least one day where the company is closed
if not event_interval:
event.unavailable_partner_ids = event.partner_ids
continue
start = event_interval._items[0][0]
stop = event_interval._items[-1][1]
schedule_by_partner = event.partner_ids._get_schedule(start, stop, merge=False)
event.unavailable_partner_ids = event._check_employees_availability_for_event(
schedule_by_partner, event_interval)
@api.model
def get_unusual_days(self, date_from, date_to=None):
return self.env.user.employee_id._get_unusual_days(date_from, date_to)
def _get_events_interval(self):
"""
This method will returned an Intervals object that represent the event's interval based of its parameters.
If an event is scheduled for the entire day, its interval will correspond to the work interval defined by the
company's calendar.
If an allday event is scheduled on a day when the company is closed, the interval of this event will be empty.
"""
start = min(self.mapped('start')).replace(hour=0, minute=0, second=0, tzinfo=UTC)
stop = max(self.mapped('stop')).replace(hour=23, minute=59, second=59, tzinfo=UTC)
company_calendar = self.env.company.resource_calendar_id
global_interval = company_calendar._work_intervals_batch(start, stop)[False]
interval_by_event = {}
for event in self:
if event.allday:
# Avoid allday event with a duration of 0
allday_event_interval = Intervals([(
event.start.replace(hour=0, minute=0, second=0, tzinfo=UTC),
event.stop.replace(hour=23, minute=59, second=59, tzinfo=UTC),
self.env['resource.calendar']
)])
if any(not (Intervals([(
event.start.replace(hour=0, minute=0, second=0, tzinfo=UTC) + relativedelta(days=i),
event.start.replace(hour=23, minute=59, second=59, tzinfo=UTC) + relativedelta(days=i),
self.env['resource.calendar']
)]) & global_interval) for i in range(0, (event.stop_date - event.start_date).days + 1)):
interval_by_event[event] = Intervals([])
else:
interval_by_event[event] = allday_event_interval & global_interval
else:
interval_by_event[event] = Intervals([(
timezone_datetime(event.start),
timezone_datetime(event.stop),
self.env['resource.calendar']
)])
return interval_by_event
def _check_employees_availability_for_event(self, schedule_by_partner, event_interval):
unavailable_partners = []
for partner, schedule in schedule_by_partner.items():
common_interval = schedule & event_interval
if sum_intervals(common_interval) != sum_intervals(event_interval):
unavailable_partners.append(partner.id)
return unavailable_partners