19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:34 +01:00
parent 5faf7397c5
commit 2696f14ed7
721 changed files with 220375 additions and 91221 deletions

View file

@ -1,17 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from datetime import timedelta
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.tools import plaintext2html
_logger = logging.getLogger(__name__)
from odoo.tools.sql import SQL
class AlarmManager(models.AbstractModel):
class CalendarAlarm_Manager(models.AbstractModel):
_name = 'calendar.alarm_manager'
_description = 'Event Alarm Manager'
@ -23,7 +19,9 @@ class AlarmManager(models.AbstractModel):
result = {}
delta_request = """
SELECT
rel.calendar_event_id, max(alarm.duration_minutes) AS max_delta,min(alarm.duration_minutes) AS min_delta
rel.calendar_event_id,
max(alarm.duration_minutes) AS max_delta,
min(alarm.duration_minutes) AS min_delta
FROM
calendar_alarm_calendar_event_rel AS rel
LEFT JOIN calendar_alarm AS alarm ON alarm.id = rel.calendar_alarm_id
@ -31,30 +29,24 @@ class AlarmManager(models.AbstractModel):
GROUP BY rel.calendar_event_id
"""
base_request = """
SELECT
cal.id,
cal.start - interval '1' minute * calcul_delta.max_delta AS first_alarm,
CASE
WHEN cal.recurrency THEN rrule.until - interval '1' minute * calcul_delta.min_delta
ELSE cal.stop - interval '1' minute * calcul_delta.min_delta
END as last_alarm,
cal.start as first_event_date,
CASE
WHEN cal.recurrency THEN rrule.until
ELSE cal.stop
END as last_event_date,
calcul_delta.min_delta,
calcul_delta.max_delta,
rrule.rrule AS rule
FROM
calendar_event AS cal
RIGHT JOIN calcul_delta ON calcul_delta.calendar_event_id = cal.id
LEFT JOIN calendar_recurrence as rrule ON rrule.id = cal.recurrence_id
"""
SELECT
cal.id,
cal.start - interval '1' minute * calcul_delta.max_delta AS first_alarm,
cal.stop - interval '1' minute * calcul_delta.min_delta AS last_alarm,
cal.start AS first_meeting,
cal.stop AS last_meeting,
calcul_delta.min_delta,
calcul_delta.max_delta
FROM
calendar_event AS cal
INNER JOIN calcul_delta ON calcul_delta.calendar_event_id = cal.id
WHERE cal.active = True
"""
filter_user = """
RIGHT JOIN calendar_event_res_partner_rel AS part_rel ON part_rel.calendar_event_id = cal.id
AND part_rel.res_partner_id IN %s
INNER JOIN calendar_event_res_partner_rel AS part_rel
ON part_rel.calendar_event_id = cal.id
AND part_rel.res_partner_id IN %s
WHERE cal.active = True
"""
# Add filter on alarm type
@ -62,7 +54,7 @@ class AlarmManager(models.AbstractModel):
# Add filter on partner_id
if partners:
base_request += filter_user
base_request = base_request.replace("WHERE cal.active = True", filter_user)
tuple_params += (tuple(partners.ids), )
# Upper bound on first_alarm of requested events
@ -81,15 +73,15 @@ class AlarmManager(models.AbstractModel):
tuple_params += (seconds,)
self.env.flush_all()
self._cr.execute("""
self.env.cr.execute("""
WITH calcul_delta AS (%s)
SELECT *
FROM ( %s WHERE cal.active = True ) AS ALL_EVENTS
WHERE ALL_EVENTS.first_alarm < %s
AND ALL_EVENTS.last_event_date > (now() at time zone 'utc')
FROM ( %s ) AS ALL_EVENTS
WHERE ALL_EVENTS.first_alarm < %s
AND ALL_EVENTS.last_alarm > (now() at time zone 'utc')
""" % (delta_request, base_request, first_alarm_max_value), tuple_params)
for event_id, first_alarm, last_alarm, first_meeting, last_meeting, min_duration, max_duration, rule in self._cr.fetchall():
for event_id, first_alarm, last_alarm, first_meeting, last_meeting, min_duration, max_duration in self.env.cr.fetchall():
result[event_id] = {
'event_id': event_id,
'first_alarm': first_alarm,
@ -98,14 +90,13 @@ class AlarmManager(models.AbstractModel):
'last_meeting': last_meeting,
'min_duration': min_duration,
'max_duration': max_duration,
'rrule': rule
}
# determine accessible events
events = self.env['calendar.event'].browse(result)
result = {
key: result[key]
for key in set(events._filter_access_rules('read').ids)
for key in events._filtered_access('read').ids
}
return result
@ -147,7 +138,7 @@ class AlarmManager(models.AbstractModel):
To be overriden on inherited modules
adding extra conditions to extract only the unsynced events
"""
return ""
return SQL("")
def _get_events_by_alarm_to_notify(self, alarm_type):
"""
@ -159,21 +150,28 @@ class AlarmManager(models.AbstractModel):
design. The attendees receive an invitation for any new event
already.
"""
lastcall = self.env.context.get('lastcall', False) or fields.date.today() - relativedelta(weeks=1)
self.env.cr.execute('''
SELECT "alarm"."id", "event"."id"
FROM "calendar_event" AS "event"
JOIN "calendar_alarm_calendar_event_rel" AS "event_alarm_rel"
ON "event"."id" = "event_alarm_rel"."calendar_event_id"
JOIN "calendar_alarm" AS "alarm"
ON "event_alarm_rel"."calendar_alarm_id" = "alarm"."id"
WHERE
"alarm"."alarm_type" = %s
AND "event"."active"
AND "event"."start" - CAST("alarm"."duration" || ' ' || "alarm"."interval" AS Interval) >= %s
AND "event"."start" - CAST("alarm"."duration" || ' ' || "alarm"."interval" AS Interval) < now() at time zone 'utc'
AND "event"."stop" > now() at time zone 'utc'
''' + self._get_notify_alert_extra_conditions(), [alarm_type, lastcall])
lastcall = self.env.context.get('lastcall', False) or fields.Date.today() - timedelta(weeks=1)
# TODO MASTER: remove context and add a proper parameter
extra_conditions = self.with_context(alarm_type=alarm_type)._get_notify_alert_extra_conditions()
now = fields.Datetime.now()
self.env.cr.execute(SQL("""
SELECT alarm.id, event.id
FROM calendar_event AS event
JOIN calendar_alarm_calendar_event_rel AS event_alarm_rel
ON event.id = event_alarm_rel.calendar_event_id
JOIN calendar_alarm AS alarm
ON event_alarm_rel.calendar_alarm_id = alarm.id
WHERE alarm.alarm_type = %s
AND event.active
AND event.start - CAST(alarm.duration || ' ' || alarm.interval AS Interval) >= %s
AND event.start - CAST(alarm.duration || ' ' || alarm.interval AS Interval) < %s
%s
""",
alarm_type,
lastcall,
now,
extra_conditions,
))
events_by_alarm = {}
for alarm_id, event_id in self.env.cr.fetchall():
@ -187,21 +185,24 @@ class AlarmManager(models.AbstractModel):
if not events_by_alarm:
return
# force_send limit should apply to the total nb of attendees, not per alarm
force_send_limit = int(self.env['ir.config_parameter'].sudo().get_param('mail.mail_force_send_limit', 100))
event_ids = list(set(event_id for event_ids in events_by_alarm.values() for event_id in event_ids))
events = self.env['calendar.event'].browse(event_ids)
attendees = events.attendee_ids.filtered(lambda a: a.state != 'declined')
now = fields.Datetime.now()
attendees = events.filtered(lambda e: e.stop > now).attendee_ids.filtered(lambda a: a.state != 'declined')
alarms = self.env['calendar.alarm'].browse(events_by_alarm.keys())
for alarm in alarms:
alarm_attendees = attendees.filtered(lambda attendee: attendee.event_id.id in events_by_alarm[alarm.id])
alarm_attendees.with_context(
mail_notify_force_send=True,
calendar_template_ignore_recurrence=True,
mail_notify_author=True
)._send_mail_to_attendees(
alarm_attendees.with_context(calendar_template_ignore_recurrence=True)._notify_attendees(
alarm.mail_template_id,
force_send=True
force_send=len(attendees) <= force_send_limit,
notify_author=True,
)
events._setup_event_recurrent_alarms(events_by_alarm)
@api.model
def get_next_notif(self):
partner = self.env.user.partner_id
@ -245,13 +246,10 @@ class AlarmManager(models.AbstractModel):
def _notify_next_alarm(self, partner_ids):
""" Sends through the bus the next alarm of given partners """
notifications = []
users = self.env['res.users'].search([
('partner_id', 'in', tuple(partner_ids)),
('groups_id', 'in', self.env.ref('base.group_user').ids),
('group_ids', 'in', self.env.ref('base.group_user').ids),
])
for user in users:
notif = self.with_user(user).with_context(allowed_company_ids=user.company_ids.ids).get_next_notif()
notifications.append([user.partner_id, 'calendar.alarm', notif])
if len(notifications) > 0:
self.env['bus.bus']._sendmany(notifications)
user._bus_send("calendar.alarm", notif)