[MERGE] event(_sms): integrate SMS into event communication scheduler

PURPOSE

Allow the user to schedule SMS communication on events

SPECIFICATION

In event.event form view:
* rename tab 'Email Schedule' to 'Communication'
* add a selection field at the beginning of event_mail_ids with label 'Send'
and values 'Email' and 'SMS Text Message' (required with default value
'Email');
* add support of SMS template and SMS sending in event communication;

Add SMS template data
* Name: Event: Registration (see data)
* Name: Event: Reminder (see data)

Improve registration model
* add a mobile field to the attendees form view: https://nimb.ws/p0VvyS

Technically this task uses the newly-introduced API methods allowing to
send and schedule SMS.

MIGRATION

Some notes on migration

* event.registration: new mobile field in addition to phone, may be void;
* event.type.mail and event.mail: new notification_type field. Existing
rows: set value to 'mail'.

LINKS

Task 1922187

closes odoo/odoo#34705

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Robot Odoo
2019-07-25 17:27:00 +02:00
committed by GitHub
19 changed files with 351 additions and 53 deletions
+9 -3
View File
@@ -364,15 +364,22 @@ class EventRegistration(models.Model):
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'name, create_date desc'
# event
origin = fields.Char(
string='Source Document', readonly=True,
help="Reference of the document that created the registration, for example a sales order")
event_id = fields.Many2one(
'event.event', string='Event', required=True,
readonly=True, states={'draft': [('readonly', False)]})
# attendee
partner_id = fields.Many2one(
'res.partner', string='Contact',
states={'done': [('readonly', True)]})
name = fields.Char(string='Attendee Name', index=True)
email = fields.Char(string='Email')
phone = fields.Char(string='Phone')
mobile = fields.Char(string='Mobile')
# organization
date_open = fields.Datetime(string='Registration Date', readonly=True, default=lambda self: fields.Datetime.now()) # weird crash is directly now
date_closed = fields.Datetime(string='Attended Date', readonly=True)
event_begin_date = fields.Datetime(string="Event Start Date", related='event_id.date_begin', readonly=True)
@@ -384,9 +391,6 @@ class EventRegistration(models.Model):
('draft', 'Unconfirmed'), ('cancel', 'Cancelled'),
('open', 'Confirmed'), ('done', 'Attended')],
string='Status', default='draft', readonly=True, copy=False, tracking=True)
email = fields.Char(string='Email')
phone = fields.Char(string='Phone')
name = fields.Char(string='Attendee Name', index=True)
@api.constrains('event_id', 'state')
def _check_seats_limit(self):
@@ -421,6 +425,7 @@ class EventRegistration(models.Model):
data = {
'name': registration.get('name', partner_id.name),
'phone': registration.get('phone', partner_id.phone),
'mobile': registration.get('mobile', partner_id.mobile),
'email': registration.get('email', partner_id.email),
'partner_id': partner_id.id,
'event_id': event_id and event_id.id or False,
@@ -462,6 +467,7 @@ class EventRegistration(models.Model):
self.name = contact.name or self.name
self.email = contact.email or self.email
self.phone = contact.phone or self.phone
self.mobile = contact.mobile or self.mobile
def _message_get_suggested_recipients(self):
recipients = super(EventRegistration, self)._message_get_suggested_recipients()
+6 -4
View File
@@ -29,6 +29,7 @@ class EventTypeMail(models.Model):
event_type_id = fields.Many2one(
'event.type', string='Event Type',
ondelete='cascade', required=True)
notification_type = fields.Selection([('mail', 'Mail')], string='Send', default='mail', required=True)
interval_nbr = fields.Integer('Interval', default=1)
interval_unit = fields.Selection([
('now', 'Immediately'),
@@ -42,7 +43,7 @@ class EventTypeMail(models.Model):
string='Trigger', default="before_event", required=True)
template_id = fields.Many2one(
'mail.template', string='Email Template',
domain=[('model', '=', 'event.registration')], required=True, ondelete='restrict',
domain=[('model', '=', 'event.registration')], ondelete='restrict',
help='This field contains the template of the mail that will be automatically sent')
@@ -56,6 +57,7 @@ class EventMailScheduler(models.Model):
event_id = fields.Many2one('event.event', string='Event', required=True, ondelete='cascade')
sequence = fields.Integer('Display order')
notification_type = fields.Selection([('mail', 'Mail')], string='Send', default='mail', required=True)
interval_nbr = fields.Integer('Interval', default=1)
interval_unit = fields.Selection([
('now', 'Immediately'),
@@ -69,7 +71,7 @@ class EventMailScheduler(models.Model):
string='Trigger ', default="before_event", required=True)
template_id = fields.Many2one(
'mail.template', string='Email Template',
domain=[('model', '=', 'event.registration')], required=True, ondelete='restrict',
domain=[('model', '=', 'event.registration')], ondelete='restrict',
help='This field contains the template of the mail that will be automatically sent')
scheduled_date = fields.Datetime('Scheduled Sent Mail', compute='_compute_scheduled_date', store=True)
mail_registration_ids = fields.One2many('event.mail.registration', 'scheduler_id')
@@ -113,7 +115,7 @@ class EventMailScheduler(models.Model):
mail.mail_registration_ids.filtered(lambda reg: reg.scheduled_date and reg.scheduled_date <= now).execute()
else:
# Do not send emails if the mailing was scheduled before the event but the event is over
if not mail.mail_sent and (mail.interval_type != 'before_event' or mail.event_id.date_end > now):
if not mail.mail_sent and (mail.interval_type != 'before_event' or mail.event_id.date_end > now) and mail.notification_type == 'mail':
mail.event_id.mail_attendees(mail.template_id.id)
mail.write({'mail_sent': True})
return True
@@ -180,7 +182,7 @@ class EventMailRegistration(models.Model):
def execute(self):
for mail in self:
if mail.registration_id.state in ['open', 'done'] and not mail.mail_sent:
if mail.registration_id.state in ['open', 'done'] and not mail.mail_sent and mail.scheduler_id.notification_type == 'mail':
mail.scheduler_id.template_id.send_mail(mail.registration_id.id)
mail.write({'mail_sent': True})
+30 -23
View File
@@ -6,47 +6,54 @@ from odoo import fields
from odoo.tests import common
class TestEventCommon(common.TransactionCase):
class TestEventCommon(common.SavepointCase):
def setUp(self):
super(TestEventCommon, self).setUp()
# Usefull models
self.Users = self.env['res.users']
self.Event = self.env['event.event']
self.Registration = self.env['event.registration']
self.EventMail = self.env['event.mail']
@classmethod
def setUpClass(cls):
super(TestEventCommon, cls).setUpClass()
# User groups
self.group_employee_id = self.env['ir.model.data'].xmlid_to_res_id('base.group_user')
self.group_event_user_id = self.env['ir.model.data'].xmlid_to_res_id('event.group_event_user')
self.group_event_manager_id = self.env['ir.model.data'].xmlid_to_res_id('event.group_event_manager')
group_system = self.env.ref('base.group_system')
cls.group_employee_id = cls.env.ref('base.group_user').id
cls.group_event_user_id = cls.env.ref('event.group_event_user').id
cls.group_event_manager_id = cls.env.ref('event.group_event_manager').id
cls.group_system_id = cls.env.ref('base.group_system').id
# Test users to use through the various tests
self.user_eventuser = self.Users.with_context({'no_reset_password': True}).create({
cls.user_eventuser = cls.env['res.users'].with_context({'no_reset_password': True}).create({
'name': 'Armande EventUser',
'login': 'Armande',
'email': 'armande.eventuser@example.com',
'tz': 'Europe/Brussels',
'groups_id': [(6, 0, [self.group_employee_id, self.group_event_user_id])]
'groups_id': [(6, 0, [cls.group_employee_id, cls.group_event_user_id])]
})
self.user_eventmanager = self.Users.with_context({'no_reset_password': True}).create({
cls.user_eventmanager = cls.env['res.users'].with_context({'no_reset_password': True}).create({
'name': 'Bastien EventManager',
'login': 'bastien',
'email': 'bastien.eventmanager@example.com',
'tz': 'Europe/Brussels',
'groups_id': [(6, 0, [
self.group_employee_id,
self.group_event_manager_id,
group_system.id])]
cls.group_employee_id,
cls.group_event_manager_id,
cls.group_system_id])]
})
self.event_0 = self.env['event.event'].create({
cls.event_0 = cls.env['event.event'].create({
'name': 'TestEvent',
'auto_confirm': True,
'date_begin': fields.Datetime.to_string(datetime.today() + timedelta(days=1)),
'date_end': fields.Datetime.to_string(datetime.today() + timedelta(days=15)),
'registration_ids': [(0, 0, {
'partner_id': self.user_eventuser.partner_id.id,
})]
})
# set country in order to format belgium numbers
cls.event_0.company_id.write({'country_id': cls.env.ref('base.be').id})
@classmethod
def _create_registrations(cls, event, reg_count):
# create some registrations
registrations = cls.env['event.registration'].create([{
'event_id': event.id,
'name': 'Test Registration %s' % x,
'email': '_test_reg_%s@example.com' % x,
'phone': '04560000%s%s' % (x, x),
} for x in range(0, reg_count)])
return registrations
+9 -7
View File
@@ -19,7 +19,7 @@ class TestEventFlow(TestEventCommon):
def test_00_basic_event_auto_confirm(self):
""" Basic event management with auto confirmation """
# EventUser creates a new event: ok
test_event = self.Event.with_user(self.user_eventmanager).create({
test_event = self.env['event.event'].with_user(self.user_eventmanager).create({
'name': 'TestEvent',
'auto_confirm': True,
'date_begin': datetime.datetime.now() + relativedelta(days=-1),
@@ -30,13 +30,13 @@ class TestEventFlow(TestEventCommon):
self.assertEqual(test_event.state, 'confirm', 'Event: auto_confirmation of event failed')
# EventUser create registrations for this event
test_reg1 = self.Registration.with_user(self.user_eventuser).create({
test_reg1 = self.env['event.registration'].with_user(self.user_eventuser).create({
'name': 'TestReg1',
'event_id': test_event.id,
})
self.assertEqual(test_reg1.state, 'open', 'Event: auto_confirmation of registration failed')
self.assertEqual(test_event.seats_reserved, 1, 'Event: wrong number of reserved seats after confirmed registration')
test_reg2 = self.Registration.with_user(self.user_eventuser).create({
test_reg2 = self.env['event.registration'].with_user(self.user_eventuser).create({
'name': 'TestReg2',
'event_id': test_event.id,
})
@@ -45,7 +45,7 @@ class TestEventFlow(TestEventCommon):
# EventUser create registrations for this event: too much registrations
with self.assertRaises(ValidationError):
self.Registration.with_user(self.user_eventuser).create({
self.env['event.registration'].with_user(self.user_eventuser).create({
'name': 'TestReg3',
'event_id': test_event.id,
})
@@ -70,7 +70,7 @@ class TestEventFlow(TestEventCommon):
""" Avanced event flow: no auto confirmation, manage minimum / maximum
seats, ... """
# EventUser creates a new event: ok
test_event = self.Event.with_user(self.user_eventmanager).create({
test_event = self.env['event.event'].with_user(self.user_eventmanager).create({
'name': 'TestEvent',
'date_begin': datetime.datetime.now() + relativedelta(days=-1),
'date_end': datetime.datetime.now() + relativedelta(days=1),
@@ -81,7 +81,7 @@ class TestEventFlow(TestEventCommon):
'Event: new event should be in draft state, no auto confirmation')
# EventUser create registrations for this event -> no auto confirmation
test_reg1 = self.Registration.with_user(self.user_eventuser).create({
test_reg1 = self.env['event.registration'].with_user(self.user_eventuser).create({
'name': 'TestReg1',
'event_id': test_event.id,
})
@@ -92,7 +92,7 @@ class TestEventFlow(TestEventCommon):
def test_event_access_rights(self):
# EventManager required to create or update events
with self.assertRaises(AccessError):
self.Event.with_user(self.user_eventuser).create({
self.env['event.event'].with_user(self.user_eventuser).create({
'name': 'TestEvent',
'date_begin': datetime.datetime.now() + relativedelta(days=-1),
'date_end': datetime.datetime.now() + relativedelta(days=1),
@@ -114,9 +114,11 @@ class TestEventFlow(TestEventCommon):
event_config.execute()
def test_event_data(self):
self.event_0.write({'registration_ids': [(0, 0, {'partner_id': self.user_eventuser.partner_id.id})]})
self.assertEqual(self.event_0.registration_ids.get_date_range_str(), u'tomorrow')
def test_event_date_range(self):
self.event_0.write({'registration_ids': [(0, 0, {'partner_id': self.user_eventuser.partner_id.id})]})
self.patcher = patch('odoo.addons.event.models.event.fields.Datetime', wraps=Datetime)
self.mock_datetime = self.patcher.start()
+5 -7
View File
@@ -17,7 +17,7 @@ class TestMailSchedule(TestEventCommon):
event_date_begin = now + relativedelta(days=1)
event_date_end = now + relativedelta(days=3)
test_event = self.Event.with_user(self.user_eventmanager).create({
test_event = self.env['event.event'].with_user(self.user_eventmanager).create({
'name': 'TestEventMail',
'auto_confirm': True,
'date_begin': event_date_begin,
@@ -37,19 +37,19 @@ class TestMailSchedule(TestEventCommon):
})
# create some registrations
self.Registration.with_user(self.user_eventuser).create({
self.env['event.registration'].with_user(self.user_eventuser).create({
'event_id': test_event.id,
'name': 'Reg0',
'email': 'reg0@example.com',
})
self.Registration.with_user(self.user_eventuser).create({
self.env['event.registration'].with_user(self.user_eventuser).create({
'event_id': test_event.id,
'name': 'Reg1',
'email': 'reg1@example.com',
})
# check subscription scheduler
schedulers = self.EventMail.search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub')])
schedulers = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub')])
self.assertEqual(len(schedulers), 1, 'event: wrong scheduler creation')
self.assertEqual(schedulers[0].scheduled_date, test_event.create_date, 'event: incorrect scheduled date for checking controller')
@@ -63,7 +63,7 @@ class TestMailSchedule(TestEventCommon):
self.assertTrue(registration.mail_sent, 'event: wrongly confirmed mailing on registration')
# check before event scheduler
schedulers = self.EventMail.search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')])
schedulers = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')])
self.assertEqual(len(schedulers), 1, 'event: wrong scheduler creation')
self.assertEqual(schedulers[0].scheduled_date, event_date_begin + relativedelta(days=-1), 'event: incorrect scheduled date')
@@ -75,5 +75,3 @@ class TestMailSchedule(TestEventCommon):
mails = self.env['mail.mail'].search([('subject', 'ilike', 'TestEventMail'), ('date', '>=', now)], order='date DESC', limit=3)
self.assertEqual(len(mails), 3, 'event: wrong number of reminders in outgoing mail queue')
+13 -8
View File
@@ -60,8 +60,9 @@
<div class="row mt16" attrs="{'invisible': [('use_mail_schedule', '=', False)]}">
<div class="col-lg-9">
<field name="event_type_mail_ids">
<tree string="Email Schedule" editable="bottom">
<field name="template_id"/>
<tree string="Communication" editable="bottom">
<field name="notification_type" invisible="1"/>
<field name="template_id" attrs="{'required': [('notification_type', '=', 'mail')]}"/>
<field name="interval_nbr" attrs="{'readonly':[('interval_unit', '=', 'now')]}"/>
<field name="interval_unit"/>
<field name="interval_type"/>
@@ -253,11 +254,12 @@
</group>
</group>
<notebook>
<page string="Email Schedule" name="event_mail_schedule">
<page string="Communication" name="event_communication">
<field name="event_mail_ids">
<tree string="Email Schedule" editable="bottom">
<tree string="Communication" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="template_id" />
<field name="notification_type" invisible="1"/>
<field name="template_id" attrs="{'required': [('notification_type', '=', 'mail')]}"/>
<field name="interval_nbr" attrs="{'readonly':[('interval_unit','=','now')]}"/>
<field name="interval_unit"/>
<field name="interval_type"/>
@@ -482,8 +484,9 @@
<group string="Attendee" name="attendee">
<field name="partner_id" attrs="{'readonly':[('state', '!=', 'draft')]}"/>
<field name="name"/>
<field name="phone"/>
<field name="email"/>
<field name="phone"/>
<field name="mobile"/>
</group>
<group string="Event Information" name="event">
<field name="event_id" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_open': True, 'no_create': True}"/>
@@ -596,7 +599,8 @@
<group>
<group>
<field name="event_id"/>
<field name="template_id"/>
<field name="notification_type" invisible="1"/>
<field name="template_id" attrs="{'required': [('notification_type', '=', 'mail')]}"/>
<field name="mail_sent"/>
</group>
<group>
@@ -631,7 +635,8 @@
<field name="arch" type="xml">
<tree string="Event Mail Schedulers">
<field name="event_id"/>
<field name="template_id"/>
<field name="notification_type" invisible="1"/>
<field name="template_id" attrs="{'required': [('notification_type', '=', 'mail')]}"/>
<field name="scheduled_date"/>
<field name="mail_sent"/>
<field name="done"/>
+1 -1
View File
@@ -88,7 +88,7 @@
<field name="model">event.event</field>
<field name="inherit_id" ref="event.view_event_form"/>
<field name="arch" type="xml">
<page name="event_mail_schedule" position="before">
<page name="event_communication" position="before">
<page string="Tickets">
<field name="event_ticket_ids" context="{'default_name': name}" mode="tree,kanban">
<tree string="Tickets" editable="bottom">
@@ -33,6 +33,7 @@ class RegistrationEditor(models.TransientModel):
'name': reg.name,
'email': reg.email,
'phone': reg.phone,
'mobile': reg.mobile,
'sale_order_line_id': so_line.id,
})
for count in range(int(so_line.product_uom_qty) - len(existing_registrations)):
@@ -71,6 +72,7 @@ class RegistrationEditorLine(models.TransientModel):
event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket')
email = fields.Char(string='Email')
phone = fields.Char(string='Phone')
mobile = fields.Char(string='Mobile')
name = fields.Char(string='Name', index=True)
def get_registration_data(self):
@@ -81,6 +83,7 @@ class RegistrationEditorLine(models.TransientModel):
'partner_id': self.editor_id.sale_order_id.partner_id.id,
'name': self.name or self.editor_id.sale_order_id.partner_id.name,
'phone': self.phone or self.editor_id.sale_order_id.partner_id.phone,
'mobile': self.mobile or self.editor_id.sale_order_id.partner_id.mobile,
'email': self.email or self.editor_id.sale_order_id.partner_id.email,
'origin': self.editor_id.sale_order_id.name,
'sale_order_id': self.editor_id.sale_order_id.id,
@@ -15,6 +15,7 @@
<field name="event_ticket_id" domain="[('event_id', '=', event_id)]" readonly='1' force_save="1"/>
<field name="name"/>
<field name="email"/>
<field name="mobile"/>
<field name="phone"/>
</tree>
</field>
+5
View File
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import tests
+19
View File
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'SMS on Events',
'version': '1.0',
'category': 'Marketing',
'description': """Schedule SMS in event management""",
'depends': ['event', 'sms'],
'data': [
'data/sms_data.xml',
'views/event_views.xml',
'views/event_mail_views.xml',
],
'demo': [
],
'installable': True,
'auto_install': True
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<odoo><data noupdate="1">
<record id="sms_template_data_event_registration" model="sms.template">
<field name="name">Event: Registration</field>
<field name="model_id" ref="event.model_event_registration"/>
<field name="body">${object.event_id.organizer_id.name or object.event_id.company_id.name or user.env.company_id.name}: We are happy to confirm your registration for the ${object.event_id.name} event.</field>
<field name="lang">${object.partner_id.lang}</field>
</record>
<record id="sms_template_data_event_reminder" model="sms.template">
<field name="name">Event: Reminder</field>
<field name="model_id" ref="event.model_event_registration"/>
<field name="body">${object.event_id.organizer_id.name or object.event_id.company_id.name or user.env.company_id.name}: We are excited to remind you that the ${object.event_id.name} event is starting ${object.get_date_range_str()}. We confirm your registration and hope to meet you there.</field>
<field name="lang">${object.partner_id.lang}</field>
</record>
</data></odoo>
+5
View File
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import event
from . import event_mail
+13
View File
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class Registration(models.Model):
_inherit = 'event.registration'
def _sms_get_number_fields(self):
""" This method returns the fields to use to find the number to use to
send an SMS on a record. """
return ['mobile', 'phone']
+49
View File
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class EventTypeMail(models.Model):
_inherit = 'event.type.mail'
notification_type = fields.Selection(selection_add=[('sms', 'SMS')])
sms_template_id = fields.Many2one(
'sms.template', string='SMS Template',
domain=[('model', '=', 'event.registration')], ondelete='restrict',
help='This field contains the template of the SMS that will be automatically sent')
class EventMailScheduler(models.Model):
_inherit = 'event.mail'
notification_type = fields.Selection(selection_add=[('sms', 'SMS')])
sms_template_id = fields.Many2one(
'sms.template', string='SMS Template',
domain=[('model', '=', 'event.registration')], ondelete='restrict',
help='This field contains the template of the SMS that will be automatically sent')
def execute(self):
for mail in self:
now = fields.Datetime.now()
if mail.interval_type != 'after_sub':
# Do not send SMS if the communication was scheduled before the event but the event is over
if not mail.mail_sent and (mail.interval_type != 'before_event' or mail.event_id.date_end > now) and mail.notification_type == 'sms' and mail.sms_template_id:
self.env['event.registration']._message_sms_schedule_mass(
template=mail.sms_template_id,
active_domain=[('event_id', '=', mail.event_id.id), ('state', '!=', 'cancel')],
mass_keep_log=True
)
mail.write({'mail_sent': True})
return super(EventMailScheduler, self).execute()
class EventMailRegistration(models.Model):
_inherit = 'event.mail.registration'
def execute(self):
for record in self:
if record.registration_id.state in ['open', 'done'] and not record.mail_sent and record.scheduler_id.notification_type == 'sms':
record.registration_id._message_sms_schedule_mass(template=record.scheduler_id.sms_template_id, mass_keep_log=True)
record.write({'mail_sent': True})
return super(EventMailRegistration, self).execute()
+3
View File
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import test_sms_schedule
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
from dateutil.relativedelta import relativedelta
from odoo.addons.event.tests.common import TestEventCommon
from odoo.addons.phone_validation.tools import phone_validation
from odoo.addons.sms.tests.common import MockSMS
from odoo.tools import mute_logger
class TestSMSSchedule(TestEventCommon, MockSMS):
@classmethod
def setUpClass(cls):
super(TestSMSSchedule, cls).setUpClass()
cls.sms_template_sub = cls.env['sms.template'].create({
'name': 'Test subscription',
'model_id': cls.env.ref('event.model_event_registration').id,
'body': '${object.event_id.organizer_id.name} registration confirmation.',
'lang': '${object.partner_id.lang}'
})
cls.sms_template_rem = cls.env['sms.template'].create({
'name': 'Test reminder',
'model_id': cls.env.ref('event.model_event_registration').id,
'body': '${object.event_id.organizer_id.name} reminder',
'lang': '${object.partner_id.lang}'
})
cls.event_0.write({
'event_mail_ids': [
(0, 0, { # right at subscription
'interval_unit': 'now',
'interval_type': 'after_sub',
'notification_type': 'sms',
'sms_template_id': cls.sms_template_sub.id}),
(0, 0, { # 3 days before event
'interval_nbr': 3,
'interval_unit': 'days',
'interval_type': 'before_event',
'notification_type': 'sms',
'sms_template_id': cls.sms_template_rem.id}),
]
})
def test_sms_schedule(self):
self._create_registrations(self.event_0, 3)
# check subscription scheduler
schedulers = self.env['event.mail'].search([('event_id', '=', self.event_0.id), ('interval_type', '=', 'after_sub')])
self.assertEqual(len(schedulers), 1)
self.assertEqual(schedulers.scheduled_date, self.event_0.create_date, 'event: incorrect scheduled date for checking controller')
# verify that subscription scheduler was auto-executed after each registration
self.assertEqual(len(schedulers.mail_registration_ids), 3)
self.assertTrue(all(m.mail_sent is True for m in schedulers.mail_registration_ids))
self.assertEqual(schedulers.mapped('mail_registration_ids.registration_id'), self.event_0.registration_ids)
sanitized_numbers = []
for registration in self.event_0.registration_ids:
reg_sanitized_number = phone_validation.phone_format(registration.phone, 'BE', '32', force_format='E164')
sanitized_numbers.append(reg_sanitized_number)
self.assertSMSNotification([{'number': reg_sanitized_number, 'state': 'ready'}], '%s registration confirmation.' % self.event_0.organizer_id.name)
# clear notification queue to avoid conflicts when checking next notifications
self.env['mail.notification'].search([('sms_number', 'in', sanitized_numbers)]).unlink()
self.env['sms.sms'].search([('number', 'in', sanitized_numbers)]).unlink()
# check before event scheduler
schedulers = self.env['event.mail'].search([('event_id', '=', self.event_0.id), ('interval_type', '=', 'before_event')])
self.assertEqual(len(schedulers), 1, 'event: wrong scheduler creation')
self.assertEqual(schedulers[0].scheduled_date, self.event_0.date_begin + relativedelta(days=-3))
# execute event reminder scheduler explicitly
schedulers.execute()
# verify that subscription scheduler was auto-executed after each registration
for registration in self.event_0.registration_ids:
reg_sanitized_number = phone_validation.phone_format(registration.phone, 'BE', '32', force_format='E164')
self.assertSMSNotification([{'number': reg_sanitized_number, 'state': 'ready'}], '%s reminder' % self.event_0.organizer_id.name)
@@ -0,0 +1,41 @@
<?xml version="1.0"?>
<odoo><data>
<record id="event_mail_view_form" model="ir.ui.view">
<field name="name">event.mail.view.form.inherit.sms</field>
<field name="model">event.mail</field>
<field name="inherit_id" ref="event.view_event_mail_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notification_type']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="attributes">
<attribute name="attrs">{'readonly': [('notification_type', '!=', 'mail')], 'required': [('notification_type', '=', 'mail')]}</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="after">
<field name="sms_template_id"
attrs="{'readonly': [('notification_type', '!=', 'sms')], 'required': [('notification_type', '=', 'sms')]}"
context="{'default_model': 'event.registration'}"/>
</xpath>
</field>
</record>
<record id="event_mail_view_tree" model="ir.ui.view">
<field name="name">event.mail.view.tree.inherit.sms</field>
<field name="model">event.mail</field>
<field name="inherit_id" ref="event.view_event_mail_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notification_type']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="attributes">
<attribute name="attrs">{'readonly': [('notification_type', '!=', 'mail')], 'required': [('notification_type', '=', 'mail')]}</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="after">
<field name="sms_template_id"
attrs="{'readonly': [('notification_type', '!=', 'sms')], 'required': [('notification_type', '=', 'sms')]}"
context="{'default_model': 'event.registration'}"/>
</xpath>
</field>
</record>
</data></odoo>
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0"?>
<odoo><data>
<record id="event_type_view_form" model="ir.ui.view">
<field name="name">event.type.view.form.inherit.sms</field>
<field name="model">event.type</field>
<field name="inherit_id" ref="event.view_event_type_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notification_type']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="attributes">
<attribute name="attrs">{'readonly': [('notification_type', '!=', 'mail')], 'required': [('notification_type', '=', 'mail')]}</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="after">
<field name="sms_template_id"
attrs="{'readonly': [('notification_type', '!=', 'sms')], 'required': [('notification_type', '=', 'sms')]}"
context="{'default_model': 'event.registration'}"/>
</xpath>
</field>
</record>
<record id="event_event_view_form_inherit_sms" model="ir.ui.view">
<field name="name">event.event.view.form</field>
<field name="model">event.event</field>
<field name="inherit_id" ref="event.view_event_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notification_type']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="attributes">
<attribute name="attrs">{'readonly': [('notification_type', '!=', 'mail')], 'required': [('notification_type', '=', 'mail')]}</attribute>
</xpath>
<xpath expr="//field[@name='template_id']" position="after">
<field name="sms_template_id"
attrs="{'readonly': [('notification_type', '!=', 'sms')], 'required': [('notification_type', '=', 'sms')]}"
context="{'default_model': 'event.registration'}"/>
</xpath>
</field>
</record>
</data></odoo>