diff --git a/addons/event/data/event_demo.xml b/addons/event/data/event_demo.xml index 8c434201649..7ab534cbf32 100644 --- a/addons/event/data/event_demo.xml +++ b/addons/event/data/event_demo.xml @@ -128,7 +128,7 @@ - + @@ -164,7 +164,7 @@ - + diff --git a/addons/event/models/__init__.py b/addons/event/models/__init__.py index 6dcac1874dd..3de00d4b7b9 100644 --- a/addons/event/models/__init__.py +++ b/addons/event/models/__init__.py @@ -7,5 +7,6 @@ from . import event_registration from . import event_stage from . import event_tag from . import event_ticket +from . import mail_template from . import res_config_settings from . import res_partner diff --git a/addons/event/models/event_event.py b/addons/event/models/event_event.py index b8336668947..332c132e6b1 100644 --- a/addons/event/models/event_event.py +++ b/addons/event/models/event_event.py @@ -380,10 +380,8 @@ class EventEvent(models.Model): command = [Command.unlink(mail.id) for mail in mails_to_remove] if event.event_type_id.event_type_mail_ids: command += [ - Command.create({ - attribute_name: line[attribute_name] if not isinstance(line[attribute_name], models.BaseModel) else line[attribute_name].id - for attribute_name in self.env['event.type.mail']._get_event_mail_fields_whitelist() - }) for line in event.event_type_id.event_type_mail_ids + Command.create(line._prepare_event_mail_values()) + for line in event.event_type_id.event_type_mail_ids ] if command: event.event_mail_ids = command diff --git a/addons/event/models/event_mail.py b/addons/event/models/event_mail.py index 70207bd8ac9..9bdfa4cd7a8 100644 --- a/addons/event/models/event_mail.py +++ b/addons/event/models/event_mail.py @@ -29,6 +29,10 @@ class EventTypeMail(models.Model): _name = 'event.type.mail' _description = 'Mail Scheduling on Event Category' + @api.model + def _selection_template_model(self): + return [('mail.template', 'Mail')] + event_type_id = fields.Many2one( 'event.type', string='Event Type', ondelete='cascade', required=True) @@ -44,16 +48,24 @@ class EventTypeMail(models.Model): ('before_event', 'Before the event'), ('after_event', 'After the event')], string='Trigger', default="before_event", required=True) - template_id = fields.Many2one( - 'mail.template', string='Email Template', - domain=[('model', '=', 'event.registration')], ondelete='restrict', - help='This field contains the template of the mail that will be automatically sent') + template_model_id = fields.Many2one('ir.model', string='Template Model', compute='_compute_template_model_id', compute_sudo=True) + template_ref = fields.Reference(string='Template', selection='_selection_template_model', required=True) - @api.model - def _get_event_mail_fields_whitelist(self): - """ Whitelist of fields that are copied from event_type_mail_ids to event_mail_ids when - changing the event_type_id field of event.event """ - return ['notification_type', 'template_id', 'interval_nbr', 'interval_unit', 'interval_type'] + @api.depends('notification_type') + def _compute_template_model_id(self): + mail_model = self.env['ir.model']._get('mail.template') + for mail in self: + mail.template_model_id = mail_model if mail.notification_type == 'mail' else False + + def _prepare_event_mail_values(self): + self.ensure_one() + return { + 'notification_type': self.notification_type, + 'interval_nbr': self.interval_nbr, + 'interval_unit': self.interval_unit, + 'interval_type': self.interval_type, + 'template_ref': '%s,%i' % (self.template_ref._name, self.template_ref.id) + } class EventMailScheduler(models.Model): @@ -64,6 +76,10 @@ class EventMailScheduler(models.Model): _rec_name = 'event_id' _description = 'Event Automated Mailing' + @api.model + def _selection_template_model(self): + return [('mail.template', 'Mail')] + 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) @@ -78,10 +94,6 @@ class EventMailScheduler(models.Model): ('before_event', 'Before the event'), ('after_event', 'After the event')], string='Trigger ', default="before_event", required=True) - template_id = fields.Many2one( - 'mail.template', string='Email Template', - domain=[('model', '=', 'event.registration')], ondelete='restrict', - help='This field contains the template of the mail that will be automatically sent') scheduled_date = fields.Datetime('Schedule Date', compute='_compute_scheduled_date', store=True) # contact and status mail_registration_ids = fields.One2many( @@ -92,6 +104,14 @@ class EventMailScheduler(models.Model): [('running', 'Running'), ('scheduled', 'Scheduled'), ('sent', 'Sent')], string='Global communication Status', compute='_compute_mail_state') mail_count_done = fields.Integer('# Sent', copy=False, readonly=True) + template_model_id = fields.Many2one('ir.model', string='Template Model', compute='_compute_template_model_id', compute_sudo=True) + template_ref = fields.Reference(string='Template', selection='_selection_template_model', required=True) + + @api.depends('notification_type') + def _compute_template_model_id(self): + mail_model = self.env['ir.model']._get('mail.template') + for mail in self: + mail.template_model_id = mail_model if mail.notification_type == 'mail' else False @api.depends('event_id.date_begin', 'event_id.date_end', 'interval_type', 'interval_unit', 'interval_nbr') def _compute_scheduled_date(self): @@ -140,11 +160,11 @@ class EventMailScheduler(models.Model): if scheduler.mail_done or scheduler.notification_type != 'mail': continue # no template -> ill configured, skip and avoid crash - if not scheduler.template_id: + if not scheduler.template_ref: continue # do not send emails if the mailing was scheduled before the event but the event is over if scheduler.scheduled_date <= now and (scheduler.interval_type != 'before_event' or scheduler.event_id.date_end > now): - scheduler.event_id.mail_attendees(scheduler.template_id.id) + scheduler.event_id.mail_attendees(scheduler.template_ref.id) scheduler.update({ 'mail_done': True, 'mail_count_done': scheduler.event_id.seats_reserved + scheduler.event_id.seats_used, @@ -168,7 +188,7 @@ class EventMailScheduler(models.Model): if random.random() < 0.1666 or scheduler.interval_unit in ('now', 'hours'): ex_s = exception_to_unicode(exception) try: - event, template = scheduler.event_id, scheduler.template_id + event, template = scheduler.event_id, scheduler.template_ref emails = list(set([event.organizer_id.email, event.user_id.email, template.write_uid.email])) subject = _("WARNING: Event Scheduler Error for event: %s", event.name) body = _("""Event Scheduler for: @@ -248,7 +268,7 @@ class EventMailRegistration(models.Model): reg_mail.scheduler_id.notification_type == 'mail' ) for reg_mail in todo: - reg_mail.scheduler_id.template_id.send_mail(reg_mail.registration_id.id) + reg_mail.scheduler_id.template_ref.send_mail(reg_mail.registration_id.id) todo.write({'mail_sent': True}) @api.depends('registration_id', 'scheduler_id.interval_unit', 'scheduler_id.interval_type') diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py index 563eca16616..7a9429e3e1e 100644 --- a/addons/event/models/event_registration.py +++ b/addons/event/models/event_registration.py @@ -227,7 +227,7 @@ class EventRegistration(models.Model): default_model='event.registration', default_res_id=self.id, default_use_template=bool(template), - default_template_id=template.id, + default_template_ref='mail.template,%i' % template.id, default_composition_mode='comment', custom_layout="mail.mail_notification_light", ) diff --git a/addons/event/models/mail_template.py b/addons/event/models/mail_template.py new file mode 100644 index 00000000000..f62f737bb4b --- /dev/null +++ b/addons/event/models/mail_template.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models +from odoo.osv import expression + + +class MailTemplate(models.Model): + _inherit = 'mail.template' + + @api.model + def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): + """Context-based hack to filter reference field in a m2o search box to emulate a domain the ORM currently does not support. + + As we can not specify a domain on a reference field, we added a context + key `filter_template_on_event` on the template reference field. If this + key is set, we add our domain in the `args` in the `_name_search` + method to filtrate the mail templates. + """ + if self.env.context.get('filter_template_on_event'): + args = expression.AND([[('model', '=', 'event.registration')], args]) + return super(MailTemplate, self)._name_search(name, args, operator, limit, name_get_uid) diff --git a/addons/event/tests/common.py b/addons/event/tests/common.py index ad3252dc0f2..0a2855d8ba7 100644 --- a/addons/event/tests/common.py +++ b/addons/event/tests/common.py @@ -80,12 +80,12 @@ class TestEventCommon(common.TransactionCase): (0, 0, { # right at subscription 'interval_unit': 'now', 'interval_type': 'after_sub', - 'template_id': cls.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), + 'template_ref': 'mail.template,%i' % cls.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), (0, 0, { # 1 days before event 'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event', - 'template_id': cls.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), + 'template_ref': 'mail.template,%i' % cls.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), ], }) cls.event_0 = cls.env['event.event'].create({ diff --git a/addons/event/tests/test_event_internals.py b/addons/event/tests/test_event_internals.py index 49b02f24141..9ea121503f1 100644 --- a/addons/event/tests/test_event_internals.py +++ b/addons/event/tests/test_event_internals.py @@ -104,7 +104,8 @@ class TestEventData(TestEventCommon): event_type.write({ 'event_type_mail_ids': [(5, 0), (0, 0, { 'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event', - 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})], + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}) + ], 'event_type_ticket_ids': [(5, 0), (0, 0, {'name': 'TestRegistration'})], }) event.write({'event_type_id': event_type.id}) @@ -117,7 +118,7 @@ class TestEventData(TestEventCommon): self.assertEqual(event.event_mail_ids.interval_nbr, 1) self.assertEqual(event.event_mail_ids.interval_unit, 'days') self.assertEqual(event.event_mail_ids.interval_type, 'before_event') - self.assertEqual(event.event_mail_ids.template_id, self.env.ref('event.event_reminder')) + self.assertEqual(event.event_mail_ids.template_ref, self.env.ref('event.event_reminder')) self.assertEqual(len(event.event_ticket_ids), 1) # update template, unlink from event -> should not impact event @@ -161,7 +162,7 @@ class TestEventData(TestEventCommon): 'interval_nbr': 77, 'interval_unit': 'days', 'interval_type': 'after_event', - 'template_id': self.env.ref('event.event_reminder').id, + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder'), }) ] }) @@ -178,7 +179,7 @@ class TestEventData(TestEventCommon): 'notification_type': 'mail', 'interval_unit': 'now', 'interval_type': 'after_sub', - 'template_id': self.env.ref('event.event_subscription').id, + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription'), }) ] }) @@ -306,6 +307,18 @@ class TestEventData(TestEventCommon): set(['Registration Ticket']) ) + def test_event_mail_filter_template_on_event(self): + """Test that the mail template are filtered to show only those which are related to the event registration model. + + This is important to be able to show only relevant mail templates on the related + field "template_ref". + """ + self.env['mail.template'].search([('model', '=', 'event.registration')]).unlink() + self.env['mail.template'].create({'model_id': self.env['ir.model']._get('event.registration').id, 'name': 'test template'}) + self.env['mail.template'].create({'model_id': self.env['ir.model']._get('res.partner').id, 'name': 'test template'}) + templates = self.env['mail.template'].with_context(filter_template_on_event=True).name_search('test template') + self.assertEqual(len(templates), 1, 'Should return only mail templates related to the event registration model') + @users('user_eventmanager') def test_event_registrable(self): """Test if `_compute_event_registrations_open` works properly.""" diff --git a/addons/event/tests/test_event_mail_schedule.py b/addons/event/tests/test_event_mail_schedule.py index 34eacf1d701..1889e167010 100644 --- a/addons/event/tests/test_event_mail_schedule.py +++ b/addons/event/tests/test_event_mail_schedule.py @@ -39,22 +39,22 @@ class TestMailSchedule(TestEventCommon, MockEmail): (0, 0, { # right at subscription 'interval_unit': 'now', 'interval_type': 'after_sub', - 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), (0, 0, { # one day after subscription 'interval_nbr': 1, 'interval_unit': 'hours', 'interval_type': 'after_sub', - 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), (0, 0, { # 1 days before event 'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event', - 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), (0, 0, { # immediately after event 'interval_nbr': 1, 'interval_unit': 'hours', 'interval_type': 'after_event', - 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), + 'template_ref': 'mail.template,%i' % self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), ] }) self.assertEqual(test_event.create_date, now) diff --git a/addons/event/views/event_views.xml b/addons/event/views/event_views.xml index 289d0f49673..fdbae5777a1 100644 --- a/addons/event/views/event_views.xml +++ b/addons/event/views/event_views.xml @@ -13,7 +13,6 @@