diff --git a/addons/calendar/__manifest__.py b/addons/calendar/__manifest__.py
index 2b1340d36e2..20466920cd4 100644
--- a/addons/calendar/__manifest__.py
+++ b/addons/calendar/__manifest__.py
@@ -26,9 +26,9 @@ If you need to manage your meetings, you should install the CRM module.
'security/ir.model.access.csv',
'security/calendar_security.xml',
'data/calendar_cron.xml',
+ 'data/mail_template_data.xml',
'data/calendar_data.xml',
'data/mail_data_various.xml',
- 'data/mail_template_data.xml',
'views/mail_activity_views.xml',
'views/calendar_templates.xml',
'views/calendar_views.xml',
diff --git a/addons/calendar/data/calendar_data.xml b/addons/calendar/data/calendar_data.xml
index 124cd1abfb3..037117e6852 100644
--- a/addons/calendar/data/calendar_data.xml
+++ b/addons/calendar/data/calendar_data.xml
@@ -36,12 +36,14 @@
hours
email
+
Email - 6 Hours
hours
email
+
-
\ No newline at end of file
+
diff --git a/addons/calendar/models/calendar_alarm.py b/addons/calendar/models/calendar_alarm.py
index 7fcb5ba0aad..2ea15fd017b 100644
--- a/addons/calendar/models/calendar_alarm.py
+++ b/addons/calendar/models/calendar_alarm.py
@@ -21,6 +21,12 @@ class Alarm(models.Model):
'Duration in minutes', store=True,
search='_search_duration_minutes', compute='_compute_duration_minutes',
help="Duration in minutes")
+ mail_template_id = fields.Many2one(
+ 'mail.template', string="Email Template",
+ domain=[('model', 'in', ['calendar.attendee'])],
+ compute='_compute_mail_template_id', readonly=False, store=True,
+ help="Template used to render mail reminder content.")
+ body = fields.Text("Additional Message", help="Additional message that would be sent with the notification for the reminder")
@api.depends('interval', 'duration')
def _compute_duration_minutes(self):
@@ -34,6 +40,14 @@ class Alarm(models.Model):
else:
alarm.duration_minutes = 0
+ @api.depends('alarm_type', 'mail_template_id')
+ def _compute_mail_template_id(self):
+ for alarm in self:
+ if alarm.alarm_type == 'email' and not alarm.mail_template_id:
+ alarm.mail_template_id = self.env['ir.model.data'].xmlid_to_res_id('calendar.calendar_template_meeting_reminder')
+ elif alarm.alarm_type != 'email' or not alarm.mail_template_id:
+ alarm.mail_template_id = False
+
def _search_duration_minutes(self, operator, value):
return [
'|', '|',
diff --git a/addons/calendar/models/calendar_alarm_manager.py b/addons/calendar/models/calendar_alarm_manager.py
index 45af762a7a4..7babd72d105 100644
--- a/addons/calendar/models/calendar_alarm_manager.py
+++ b/addons/calendar/models/calendar_alarm_manager.py
@@ -5,6 +5,7 @@ import logging
from datetime import timedelta
from odoo import api, fields, models
+from odoo.tools import plaintext2html
_logger = logging.getLogger(__name__)
@@ -135,7 +136,7 @@ class AlarmManager(models.AbstractModel):
})
return result
- def _get_events_to_notify(self, ttype):
+ def _get_events_by_alarm_to_notify(self, alarm_type):
"""
Get the events with an alarm of the given type between the cron
last call and now.
@@ -146,7 +147,7 @@ class AlarmManager(models.AbstractModel):
already.
"""
self.env.cr.execute('''
- SELECT "event"."id"
+ 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"
@@ -157,20 +158,33 @@ class AlarmManager(models.AbstractModel):
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'
- )''', [ttype, self.env.context['lastcall']])
+ )''', [alarm_type, self.env.context['lastcall']])
- ids = [row[0] for row in self.env.cr.fetchall()]
- return self.env['calendar.event'].browse(ids)
+ events_by_alarm = {}
+ for alarm_id, event_id in self.env.cr.fetchall():
+ events_by_alarm.setdefault(alarm_id, list()).append(event_id)
+ return events_by_alarm
@api.model
def _send_reminder(self):
# Executed via cron
- events = self._get_events_to_notify('email')
+ events_by_alarm = self._get_events_by_alarm_to_notify('email')
+ if not events_by_alarm:
+ return
+
+ 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')
- attendees.with_context(calendar_template_ignore_recurrence=True)._send_mail_to_attendees(
- self.env.ref('calendar.calendar_template_meeting_reminder', raise_if_not_found=False),
- force_send=True,
- )
+ 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
+ )._send_mail_to_attendees(
+ alarm.mail_template_id,
+ force_send=True
+ )
@api.model
def get_next_notif(self):
@@ -198,6 +212,8 @@ class AlarmManager(models.AbstractModel):
if alarm.alarm_type == 'notification':
message = meeting.display_time
+ if alarm.body:
+ message += '
%s
' % plaintext2html(alarm.body)
delta = alert['notify_at'] - fields.Datetime.now()
delta = delta.seconds + delta.days * 3600 * 24
diff --git a/addons/calendar/views/calendar_views.xml b/addons/calendar/views/calendar_views.xml
index 545dd732253..0ec4a0bac11 100644
--- a/addons/calendar/views/calendar_views.xml
+++ b/addons/calendar/views/calendar_views.xml
@@ -23,7 +23,7 @@
calendar.alarm.tree
calendar.alarm
-
+
@@ -33,23 +33,32 @@
- calendar.alarm.form
- calendar.alarm
-
-
@@ -57,6 +66,7 @@
Calendar Alarm
calendar.alarm
+ tree,form
diff --git a/addons/calendar_sms/__manifest__.py b/addons/calendar_sms/__manifest__.py
index efcff3de620..5799c4c2432 100644
--- a/addons/calendar_sms/__manifest__.py
+++ b/addons/calendar_sms/__manifest__.py
@@ -3,6 +3,7 @@
{
'name': "Calendar - SMS",
+ 'version': "1.1",
'summary': 'Send text messages as event reminders',
'description': "Send text messages as event reminders",
'category': 'Hidden',
diff --git a/addons/calendar_sms/models/calendar_alarm.py b/addons/calendar_sms/models/calendar_alarm.py
index 3c83268bba7..d69c1b17406 100644
--- a/addons/calendar_sms/models/calendar_alarm.py
+++ b/addons/calendar_sms/models/calendar_alarm.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import fields, models
+from odoo import api, fields, models
class CalendarAlarm(models.Model):
@@ -10,3 +10,16 @@ class CalendarAlarm(models.Model):
alarm_type = fields.Selection(selection_add=[
('sms', 'SMS Text Message')
], ondelete={'sms': 'set default'})
+ sms_template_id = fields.Many2one(
+ 'sms.template', string="SMS Template",
+ domain=[('model', 'in', ['calendar.event'])],
+ compute='_compute_sms_template_id', readonly=False, store=True,
+ help="Template used to render SMS reminder content.")
+
+ @api.depends('alarm_type', 'mail_template_id')
+ def _compute_sms_template_id(self):
+ for alarm in self:
+ if alarm.alarm_type == 'sms' and not alarm.sms_template_id:
+ alarm.sms_template_id = self.env['ir.model.data'].xmlid_to_res_id('calendar_sms.sms_template_data_calendar_reminder')
+ elif alarm.alarm_type != 'sms' or not alarm.sms_template_id:
+ alarm.sms_template_id = False
diff --git a/addons/calendar_sms/models/calendar_alarm_manager.py b/addons/calendar_sms/models/calendar_alarm_manager.py
index 8857c3c7607..bd2f4418c70 100644
--- a/addons/calendar_sms/models/calendar_alarm_manager.py
+++ b/addons/calendar_sms/models/calendar_alarm_manager.py
@@ -12,4 +12,13 @@ class AlarmManager(models.AbstractModel):
""" Cron method, overridden here to send SMS reminders as well
"""
super()._send_reminder()
- self._get_events_to_notify(ttype='sms')._do_sms_reminder()
+ alarms_by_event = self._get_events_by_alarm_to_notify(ttype='sms')
+ if not alarms_by_event:
+ return
+
+ events = self.env['calendar.event'].browse(list(alarms_by_event.keys()))
+ attendees = events.attendee_ids.filtered(lambda a: a.state != 'declined')
+ for event_id in alarms_by_event.keys():
+ event_alarms = attendees.event_id.alarm_ids.filtered(lambda alarm: alarm.id in alarms_by_event.get(event_id, []))
+ for alarm in event_alarms:
+ events.browse(event_id)._do_sms_reminder(alarm)
diff --git a/addons/calendar_sms/models/calendar_event.py b/addons/calendar_sms/models/calendar_event.py
index 172c209c20d..deaca1e0dfb 100644
--- a/addons/calendar_sms/models/calendar_event.py
+++ b/addons/calendar_sms/models/calendar_event.py
@@ -13,11 +13,11 @@ class CalendarEvent(models.Model):
"""
return self.mapped('attendee_ids').filtered(lambda att: att.state != 'declined').mapped('partner_id')
- def _do_sms_reminder(self):
+ def _do_sms_reminder(self, alarm):
""" Send an SMS text reminder to attendees that haven't declined the event """
for event in self:
event._message_sms_with_template(
- template_xmlid='calendar_sms.sms_template_data_calendar_reminder',
+ template=alarm.sms_template_id,
template_fallback=_("Event reminder: %(name)s, %(time)s.", name=event.name, time=event.display_time),
partner_ids=self._sms_get_default_partners().ids,
put_in_queue=False
diff --git a/addons/calendar_sms/views/calendar_views.xml b/addons/calendar_sms/views/calendar_views.xml
index ea7def35501..f5d8eda5284 100644
--- a/addons/calendar_sms/views/calendar_views.xml
+++ b/addons/calendar_sms/views/calendar_views.xml
@@ -1,6 +1,18 @@
+
+ calendar.alarm.view.form.inherit.calendar.sms
+ calendar.alarm
+
+
+
+
+
+
+
+
Send SMS
diff --git a/addons/hr_holidays/tests/test_company_leave.py b/addons/hr_holidays/tests/test_company_leave.py
index 81d20fe8f06..31d3b9cf38b 100644
--- a/addons/hr_holidays/tests/test_company_leave.py
+++ b/addons/hr_holidays/tests/test_company_leave.py
@@ -313,7 +313,7 @@ class TestCompanyLeave(TransactionCase):
})
company_leave._compute_date_from_to()
- with self.assertQueryCount(__system__=737, admin=865):
+ with self.assertQueryCount(__system__=739, admin=865):
# Original query count: 1987
# Without tracking/activity context keys: 5154
company_leave.action_validate()
diff --git a/addons/hr_work_entry_holidays/tests/test_performance.py b/addons/hr_work_entry_holidays/tests/test_performance.py
index cad64988199..cd823539586 100644
--- a/addons/hr_work_entry_holidays/tests/test_performance.py
+++ b/addons/hr_work_entry_holidays/tests/test_performance.py
@@ -31,7 +31,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
self.richard_emp.generate_work_entries(date(2018, 1, 1), date(2018, 1, 2))
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
- with self.assertQueryCount(__system__=91, admin=99):
+ with self.assertQueryCount(__system__=92, admin=99):
leave.action_validate()
leave.action_refuse()