diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py
index c7537e476da..df073e59c02 100644
--- a/addons/event/models/event_registration.py
+++ b/addons/event/models/event_registration.py
@@ -308,10 +308,15 @@ class EventRegistration(models.Model):
return
onsubscribe_schedulers.update({'mail_done': False})
- # we could simply call _create_missing_mail_registrations and let cron do their job
- # but it currently leads to several delays. We therefore call execute until
- # cron triggers are correctly used
- onsubscribe_schedulers.with_user(SUPERUSER_ID).execute()
+ # either trigger the cron, either run schedulers immediately (scaling choice)
+ async_scheduler = self.env['ir.config_parameter'].sudo().get_param('event.event_mail_async')
+ if async_scheduler:
+ self.env.ref('event.event_mail_scheduler')._trigger()
+ else:
+ # we could simply call _create_missing_mail_registrations and let cron do their job
+ # but it currently leads to several delays. We therefore call execute until
+ # cron triggers are correctly used
+ onsubscribe_schedulers.with_user(SUPERUSER_ID).execute()
# ------------------------------------------------------------
# MAILING / GATEWAY
diff --git a/addons/event/tests/common.py b/addons/event/tests/common.py
index 1122af2381f..a7fbac4fef9 100644
--- a/addons/event/tests/common.py
+++ b/addons/event/tests/common.py
@@ -104,3 +104,56 @@ class EventCase(common.TransactionCase):
'phone': f'04560000{idx}{idx}',
} for idx in range(0, reg_count)])
return registrations
+
+ @classmethod
+ def _setup_test_reports(cls):
+ cls.test_report_view = cls.env["ir.ui.view"].create({
+ "arch_db": """
+
+
+
+
+
This is a sample of an external report.
+
+
+
+""",
+ "key": "event_registration_test_report",
+ "name": "event_registration_test_report",
+ "type": "qweb",
+ })
+ cls.env["ir.model.data"].create({
+ "model": "ir.ui.view",
+ "module": "event",
+ "name": "event_registration_test_report",
+ "res_id": cls.test_report_view.id,
+ })
+
+ cls.test_report_action = cls.env['ir.actions.report'].create({
+ 'name': 'Test Report on event.registration',
+ 'model': 'event.registration',
+ 'print_report_name': "f'TestReport for {object.name}'",
+ 'report_type': 'qweb-pdf',
+ 'report_name': 'event.event_registration_test_report',
+ })
+
+ cls.template_subscription = cls.env['mail.template'].create({
+ "body_html": """
Hello your registration to is confirmed.
""",
+ "email_from": "{{ (object.event_id.organizer_id.email_formatted or object.event_id.user_id.email_formatted or '') }}",
+ "email_to": """{{ (object.email and '"%s" <%s>' % (object.name, object.email)) or object.partner_id.email_formatted or '' }}""",
+ "lang": "{{ object.event_id.lang or object.partner_id.lang }}",
+ "model_id": cls.env['ir.model']._get_id("event.registration"),
+ "name": "Event: Registration Confirmation TEST",
+ "subject": "Confirmation for {{ object.event_id.name }}",
+ "report_template_ids": [(4, cls.test_report_action.id)],
+ })
+ cls.template_reminder = cls.env['mail.template'].create({
+ "body_html": """Hello this is a reminder for your registration to .
""",
+ "email_from": "{{ (object.event_id.organizer_id.email_formatted or object.event_id.user_id.email_formatted or '') }}",
+ "email_to": """{{ (object.email and '"%s" <%s>' % (object.name, object.email)) or object.partner_id.email_formatted or '' }}""",
+ "lang": "{{ object.event_id.lang or object.partner_id.lang }}",
+ "model_id": cls.env['ir.model']._get_id("event.registration"),
+ "name": "Event: Registration Reminder TEST",
+ "subject": "Reminder for {{ object.event_id.name }}: {{ object.get_date_range_str() }}",
+ "report_template_ids": [(4, cls.test_report_action.id)],
+ })
diff --git a/addons/event/tests/test_event_mail_schedule.py b/addons/event/tests/test_event_mail_schedule.py
index 511bfcc477b..f448e9b6003 100644
--- a/addons/event/tests/test_event_mail_schedule.py
+++ b/addons/event/tests/test_event_mail_schedule.py
@@ -6,6 +6,7 @@ from dateutil.relativedelta import relativedelta
from freezegun import freeze_time
from odoo import Command
+from odoo.addons.base.tests.test_ir_cron import CronMixinCase
from odoo.addons.event.tests.common import EventCase
from odoo.addons.mail.tests.common import MockEmail
from odoo.tests import tagged, users
@@ -13,7 +14,7 @@ from odoo.tools import formataddr, mute_logger
@tagged('event_mail', 'post_install', '-at_install')
-class TestMailSchedule(EventCase, MockEmail):
+class TestMailSchedule(EventCase, MockEmail, CronMixinCase):
@classmethod
def setUpClass(cls):
@@ -27,6 +28,8 @@ class TestMailSchedule(EventCase, MockEmail):
# deactivate other schedulers to avoid messing with crons
cls.env['event.mail'].search([]).unlink()
+ # consider asynchronous sending as default sending
+ cls.env["ir.config_parameter"].set_param("event.event_mail_async", False)
# freeze some datetimes, and ensure more than 1D+1H before event starts
# to ease time-based scheduler check
@@ -37,9 +40,7 @@ class TestMailSchedule(EventCase, MockEmail):
cls.event_date_begin = datetime(2021, 3, 22, 8, 0, 0)
cls.event_date_end = datetime(2021, 3, 24, 18, 0, 0)
- cls.template_subscription_id = cls.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')
- cls.template_reminder_id = cls.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')
-
+ cls._setup_test_reports()
with cls.mock_datetime_and_now(cls, cls.reference_now):
# create with admin to force create_date
cls.test_event = cls.env['event.event'].create({
@@ -52,28 +53,28 @@ class TestMailSchedule(EventCase, MockEmail):
'interval_unit': 'now',
'interval_type': 'after_sub',
'notification_type': 'mail',
- 'template_ref': f'mail.template,{cls.template_subscription_id}',
+ 'template_ref': f'mail.template,{cls.template_subscription.id}',
}),
(0, 0, { # one hour after subscription
'interval_nbr': 1,
'interval_unit': 'hours',
'interval_type': 'after_sub',
'notification_type': 'mail',
- 'template_ref': f'mail.template,{cls.template_subscription_id}',
+ 'template_ref': f'mail.template,{cls.template_subscription.id}',
}),
(0, 0, { # 1 days before event
'interval_nbr': 1,
'interval_unit': 'days',
'interval_type': 'before_event',
'notification_type': 'mail',
- 'template_ref': f'mail.template,{cls.template_reminder_id}',
+ 'template_ref': f'mail.template,{cls.template_reminder.id}',
}),
(0, 0, { # immediately after event
'interval_nbr': 1,
'interval_unit': 'hours',
'interval_type': 'after_event',
'notification_type': 'mail',
- 'template_ref': f'mail.template,{cls.template_reminder_id}',
+ 'template_ref': f'mail.template,{cls.template_reminder.id}',
}),
]
})
@@ -160,9 +161,10 @@ class TestMailSchedule(EventCase, MockEmail):
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))],
'outgoing',
content=None,
- fields_values={'subject': 'Your registration at %s' % test_event.name,
- 'email_from': self.user_eventmanager.company_id.email_formatted,
- })
+ fields_values={
+ 'email_from': self.user_eventmanager.company_id.email_formatted,
+ 'subject': f'Confirmation for {test_event.name}',
+ })
# same for second scheduler: scheduled but not sent
self.assertEqual(len(after_sub_scheduler_2.mail_registration_ids), 2, 'event: should have 2 scheduled communication (1 / registration)')
@@ -198,9 +200,10 @@ class TestMailSchedule(EventCase, MockEmail):
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))],
'outgoing',
content=None,
- fields_values={'subject': 'Your registration at %s' % test_event.name,
- 'email_from': self.user_eventmanager.company_id.email_formatted,
- })
+ fields_values={
+ 'email_from': self.user_eventmanager.company_id.email_formatted,
+ 'subject': f'Confirmation for {test_event.name}',
+ })
# PRE SCHEDULERS (MOVE FORWARD IN TIME)
# --------------------------------------------------
@@ -233,9 +236,10 @@ class TestMailSchedule(EventCase, MockEmail):
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))],
'outgoing',
content=None,
- fields_values={'subject': '%s: tomorrow' % test_event.name,
- 'email_from': self.user_eventmanager.company_id.email_formatted,
- })
+ fields_values={
+ 'email_from': self.user_eventmanager.company_id.email_formatted,
+ 'subject': f'Reminder for {test_event.name}: tomorrow',
+ })
# NEW REGISTRATION EFFECT ON SCHEDULERS
# --------------------------------------------------
@@ -283,7 +287,7 @@ class TestMailSchedule(EventCase, MockEmail):
# manual check because 2 identical mails are sent and mail tools do not support it easily
for mail in self._new_mails:
self.assertEqual(mail.email_from, self.user_eventmanager.company_id.email_formatted)
- self.assertEqual(mail.subject, 'Your registration at %s' % test_event.name)
+ self.assertEqual(mail.subject, f'Confirmation for {test_event.name}')
self.assertEqual(mail.state, 'outgoing')
self.assertEqual(mail.email_to, formataddr((reg3.name, reg3.email)))
@@ -308,9 +312,10 @@ class TestMailSchedule(EventCase, MockEmail):
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email)), formataddr((reg3.name, reg3.email))],
'outgoing',
content=None,
- fields_values={'subject': '%s: today' % test_event.name,
- 'email_from': self.user_eventmanager.company_id.email_formatted,
- })
+ fields_values={
+ 'email_from': self.user_eventmanager.company_id.email_formatted,
+ 'subject': f"Reminder for {test_event.name}: today",
+ })
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
@users('user_eventmanager')
@@ -327,8 +332,8 @@ class TestMailSchedule(EventCase, MockEmail):
# consider having hanging registrations, still not processed (e.g. adding
# a new scheduler after)
self.env.invalidate_all()
- # com 58, event 36
- with self.assertQueryCount(61), self.mock_datetime_and_now(reference_now), \
+ # com 59, event 37
+ with self.assertQueryCount(62), self.mock_datetime_and_now(reference_now), \
self.mock_mail_gateway():
_existing = self.env['event.registration'].create([
{
@@ -347,7 +352,7 @@ class TestMailSchedule(EventCase, MockEmail):
'interval_unit': 'now',
'interval_type': 'after_sub',
'notification_type': 'mail',
- 'template_ref': f'mail.template,{self.template_subscription_id}',
+ 'template_ref': f'mail.template,{self.template_subscription.id}',
}),
]})
self.env.invalidate_all()
@@ -369,6 +374,44 @@ class TestMailSchedule(EventCase, MockEmail):
self.assertEqual(self.mail_mail_create_mocked.call_count, 7,
'EventMail: TODO: should create one mail / new registration')
+ @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
+ @users('user_eventmanager')
+ def test_event_mail_schedule_on_subscription_async(self):
+ """ Async mode for schedulers activated, should not send communication
+ in the same transaction. """
+ test_event = self.test_event.with_env(self.env)
+ cron = self.env.ref('event.event_mail_scheduler')
+ reference_now = self.reference_now
+
+ self.env['ir.config_parameter'].sudo().set_param('event.event_mail_async', True)
+ with self.capture_triggers(cron.id) as capt, \
+ self.mock_datetime_and_now(reference_now + relativedelta(minutes=10)), \
+ self.mock_mail_gateway():
+ existing = self.env['event.registration'].create([
+ {
+ 'email': f'new.async.attendee.{idx}@test.example.com',
+ 'event_id': test_event.id,
+ 'name': f'New Async Attendee {idx}',
+ } for idx in range(5)
+ ])
+ self.assertEqual(len(self._new_mails), 0)
+ self.assertEqual(self.mail_mail_create_mocked.call_count, 0)
+ capt.records.ensure_one()
+ self.assertEqual(capt.records.call_at, reference_now.replace(microsecond=0) + relativedelta(minutes=10))
+
+ # run cron: emails should be send for registrations
+ with self.mock_datetime_and_now(reference_now + relativedelta(minutes=10)), \
+ self.mock_mail_gateway():
+ cron.sudo().method_direct_trigger()
+ self.assertMailMailWEmails(
+ [formataddr((reg.name, reg.email)) for reg in existing],
+ "outgoing",
+ content=f"Hello your registration to {test_event.name} is confirmed",
+ fields_values={
+ 'email_from': self.user_eventmanager.company_id.email_formatted,
+ 'subject': f'Confirmation for {test_event.name}',
+ })
+
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
def test_unique_event_mail_ids(self):
# create event with default event_mail_ids lines
diff --git a/addons/event_sms/tests/test_sms_schedule.py b/addons/event_sms/tests/test_sms_schedule.py
index 4b68c01e118..f27a880a63c 100644
--- a/addons/event_sms/tests/test_sms_schedule.py
+++ b/addons/event_sms/tests/test_sms_schedule.py
@@ -17,6 +17,9 @@ class TestSMSSchedule(EventCase, SMSCase):
def setUpClass(cls):
super(TestSMSSchedule, cls).setUpClass()
+ # consider asynchronous sending as default sending
+ cls.env["ir.config_parameter"].set_param("event.event_mail_async", False)
+
cls.sms_template_sub = cls.env['sms.template'].create({
'name': 'Test subscription',
'model_id': cls.env.ref('event.model_event_registration').id,