From 4a616e1ab12eb12cf3076b7f6b05c96321e7b613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Hennecart?= Date: Mon, 10 Feb 2020 11:46:30 +0000 Subject: [PATCH] [IMP] calendar(_sms): add template selection on reminders The purpose is to allow the user to have the opportunity to manage what is going to be send as reminders. He can now access to the template or create a new one when the type of reminder is email or sms. In case of a simple notification, a new text field is added to add custom content. Task ID-2191254 COM PR odoo/odoo#68443 UPG PR odoo/upgrade#2313 Signed-off-by: Thibault Delavallee (tde) --- addons/calendar/__manifest__.py | 2 +- addons/calendar/data/calendar_data.xml | 4 +- addons/calendar/models/calendar_alarm.py | 14 +++++++ .../calendar/models/calendar_alarm_manager.py | 36 ++++++++++++----- addons/calendar/views/calendar_views.xml | 40 ++++++++++++------- addons/calendar_sms/__manifest__.py | 1 + addons/calendar_sms/models/calendar_alarm.py | 15 ++++++- .../models/calendar_alarm_manager.py | 11 ++++- addons/calendar_sms/models/calendar_event.py | 4 +- addons/calendar_sms/views/calendar_views.xml | 12 ++++++ .../hr_holidays/tests/test_company_leave.py | 2 +- .../tests/test_performance.py | 2 +- 12 files changed, 110 insertions(+), 33 deletions(-) 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 - -
- + 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()