diff --git a/addons/calendar/data/calendar_cron.xml b/addons/calendar/data/calendar_cron.xml index 6502fffde90..abc885058dd 100644 --- a/addons/calendar/data/calendar_cron.xml +++ b/addons/calendar/data/calendar_cron.xml @@ -6,7 +6,7 @@ Calendar: Event Reminder code - model._send_reminder_email() + model._send_reminder() 1 diff --git a/addons/calendar/models/calendar_alarm_manager.py b/addons/calendar/models/calendar_alarm_manager.py index 2503245aede..bcebf12992a 100644 --- a/addons/calendar/models/calendar_alarm_manager.py +++ b/addons/calendar/models/calendar_alarm_manager.py @@ -135,9 +135,16 @@ class AlarmManager(models.AbstractModel): }) return result - @api.model - def _send_reminder_email(self, partners=None): - # Executed via cron + def _get_events_to_notify(self, ttype): + """ + Get the events with an alarm of the given type between the cron + last call and now. + + Please note that all new reminders created since the cron last + call with an alarm prior to the cron last call are skipped by + design. The attendees receive an invitation for any new event + already. + """ self.env.cr.execute(''' SELECT "event"."id" FROM "calendar_event" AS "event" @@ -146,20 +153,20 @@ class AlarmManager(models.AbstractModel): JOIN "calendar_alarm" AS "alarm" ON "event_alarm_rel"."calendar_alarm_id" = "alarm"."id" WHERE ( - "alarm"."alarm_type" = 'email' + "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' - )''', [self.env.context['lastcall']]) + )''', [ttype, self.env.context['lastcall']]) - domain = [ - ("event_id", "in", self.env.cr.fetchall()), - ("state", "!=", "declined"), - ] - if partners is not None: - domain.append(("partner_id", "in", partners.ids)) + ids = [row[0] for row in self.env.cr.fetchall()] + return self.env['calendar.event'].browse(ids) - attendees = self.env["calendar.attendee"].search(domain) + @api.model + def _send_reminder(self): + # Executed via cron + events = self._get_events_to_notify('email') + attendees = events.attendee_ids.filtered(lambda a: a.state != 'declined') attendees._send_mail_to_attendees( 'calendar.calendar_template_meeting_reminder', force_send=True, diff --git a/addons/calendar/models/calendar_event.py b/addons/calendar/models/calendar_event.py index 3a48a8c314e..703fb6a4e94 100644 --- a/addons/calendar/models/calendar_event.py +++ b/addons/calendar/models/calendar_event.py @@ -688,14 +688,23 @@ class Meeting(models.Model): return True + def _get_trigger_alarm_types(self): + return ['email'] + def _setup_alarms(self): - """ Trigger the cron in the future for every email reminder """ + """ Schedule cron triggers for future events """ cron = self.env.ref('calendar.ir_cron_scheduler_alarm') + alarm_manager = self.env['calendar.alarm_manager'] + alarm_types = self._get_trigger_alarm_types() + for event in self: - for alarm in (alarm for alarm in event.alarm_ids if alarm.alarm_type == 'email'): - cron._trigger(at=event.start-timedelta(minutes=alarm.duration_minutes)) + for alarm in (alarm for alarm in event.alarm_ids if alarm.alarm_type in alarm_types): + at = event.start - timedelta(minutes=alarm.duration_minutes) + if not cron.lastcall or at > cron.lastcall: + # Don't trigger for past alarms, they would be skipped by design + cron._trigger(at=at) if any(alarm.alarm_type == 'notification' for alarm in event.alarm_ids): - self.env['calendar.alarm_manager']._notify_next_alarm(event.partner_ids.ids) + alarm_manager._notify_next_alarm(event.partner_ids.ids) @api.model_create_multi def create(self, vals_list): diff --git a/addons/calendar/tests/test_event_notifications.py b/addons/calendar/tests/test_event_notifications.py index 611b4b36630..c13d71fa28c 100644 --- a/addons/calendar/tests/test_event_notifications.py +++ b/addons/calendar/tests/test_event_notifications.py @@ -169,4 +169,4 @@ class TestEventNotifications(TransactionCase, MailCase): 'message_type': 'user_notification', 'subtype': 'mail.mt_note', }): - self.env['calendar.alarm_manager'].with_context(lastcall=now - relativedelta(minutes=15))._send_reminder_email(self.partner) + self.env['calendar.alarm_manager'].with_context(lastcall=now - relativedelta(minutes=15))._send_reminder() diff --git a/addons/calendar_sms/models/calendar.py b/addons/calendar_sms/models/calendar.py index 433c72184ef..5c42e2754a2 100644 --- a/addons/calendar_sms/models/calendar.py +++ b/addons/calendar_sms/models/calendar.py @@ -27,6 +27,9 @@ class CalendarEvent(models.Model): put_in_queue=False ) + def _get_trigger_alarm_types(self): + return super()._get_trigger_alarm_types() + ['sms'] + class CalendarAlarm(models.Model): _inherit = 'calendar.alarm' @@ -40,33 +43,8 @@ class AlarmManager(models.AbstractModel): _inherit = 'calendar.alarm_manager' @api.model - def get_next_mail(self): + def _send_reminder(self): """ Cron method, overridden here to send SMS reminders as well """ - result = super(AlarmManager, self).get_next_mail() - - cron = self.env.ref('calendar.ir_cron_scheduler_alarm', raise_if_not_found=False) - if not cron: - # Like the super method, do nothing if cron doesn't exist anymore - return result - - now = fields.Datetime.to_string(fields.Datetime.now()) - last_sms_cron = cron.lastcall - - interval_to_second = { - "weeks": 7 * 24 * 60 * 60, - "days": 24 * 60 * 60, - "hours": 60 * 60, - "minutes": 60, - "seconds": 1 - } - - cron_interval = cron.interval_number * interval_to_second[cron.interval_type] - events_data = self._get_next_potential_limit_alarm('sms', seconds=cron_interval) - - for event in self.env['calendar.event'].browse(events_data): - max_delta = events_data[event.id]['max_duration'] - event_start = fields.Datetime.from_string(event.start) - for alert in self.do_check_alarm_for_one_date(event_start, event, max_delta, 0, 'sms', after=last_sms_cron, missing=True): - event.browse(alert['event_id'])._do_sms_reminder() - return result + super()._send_reminder() + self._get_events_to_notify(ttype='sms')._do_sms_reminder()