[REF] mail: add 'force_send' field on composer to control email queue usage

Currently when using the composer to create a mass mailing emails are sent
directly, unless scheduled date is in the future.

With this commit it is now controllable through a field like what has been
done on SMS composer. Its default behavior is the same as before

  * posting on a monorecord: force send notification emails;
  * posting on multirecords: use the queue (force_send=False parameter given
    to message_post and propagated to _notify_thread);
  * mass mailing: use force send to send emails directly;

Usage of mail_notify_force_send context key is also removed when possible
as it is a standard parameter of posting API.

Task-3132710 (Mail: Configurable composer)

Part-of: odoo/odoo#99482
This commit is contained in:
Thibault Delavallée
2023-01-17 20:58:41 +01:00
parent 35762204a6
commit 360d9ce201
5 changed files with 33 additions and 8 deletions
@@ -183,7 +183,6 @@ class AlarmManager(models.AbstractModel):
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,
+12 -2
View File
@@ -104,6 +104,12 @@ class MailComposer(models.TransientModel):
# comment mode by default removes emails
if 'auto_delete' in fields_list and 'auto_delete' not in result and result.get('composition_mode') == 'comment':
result['auto_delete'] = True
# batch post mode by default use queues for notifications
if 'force_send' in fields_list and 'force_send' not in result:
result['force_send'] = (
result.get('composition_mode') != 'comment' or
len(self._parse_res_ids(result.get('res_ids') or [])) <= 1
)
return {
fname: result[fname]
@@ -181,6 +187,8 @@ class MailComposer(models.TransientModel):
auto_delete_keep_log = fields.Boolean(
'Keep Message Copy', default=True,
help='Keep a copy of the email content if emails are removed (mass mailing only)')
force_send = fields.Boolean(
'Send mailing or notifications directly')
mail_server_id = fields.Many2one('ir.mail_server', 'Outgoing mail server')
scheduled_date = fields.Char(
'Scheduled Date',
@@ -463,10 +471,8 @@ class MailComposer(models.TransientModel):
post_values_all = self._prepare_mail_values(res_ids)
ActiveModel = self.env[self.model] if self.model and hasattr(self.env[self.model], 'message_post') else self.env['mail.thread']
if self.composition_batch:
# do not send emails directly but use the queue instead
# add context key to avoid subscribing the author
ActiveModel = ActiveModel.with_context(
mail_notify_force_send=False,
mail_create_nosubscribe=True,
)
@@ -510,6 +516,8 @@ class MailComposer(models.TransientModel):
# as 'send' does not filter out scheduled mails (only 'process_email_queue'
# does) we need to do it manually
if not self.force_send:
continue
iter_mails_sudo_tosend = iter_mails_sudo.filtered(
lambda mail: (
not mail.scheduled_date or
@@ -563,6 +571,7 @@ class MailComposer(models.TransientModel):
MAIL
STA - 'auto_delete',
DYN - 'body_html',
STA - 'force_send', (notify parameter)
STA - 'model',
DYN - 'recipient_ids', (from partner_ids)
DYN - 'res_id',
@@ -667,6 +676,7 @@ class MailComposer(models.TransientModel):
values.update(
email_add_signature=not bool(self.template_id) and self.email_add_signature,
email_layout_xmlid=self.email_layout_xmlid,
force_send=self.force_send,
mail_auto_delete=self.auto_delete,
model_description=model_description,
)
@@ -15,6 +15,7 @@
<field name="composition_batch" invisible="1"/>
<field name="composition_mode" invisible="1"/>
<field name="email_layout_xmlid" invisible="1"/>
<field name="force_send" invisible="1"/>
<field name="lang" invisible="1"/>
<field name="mail_server_id" invisible="1"/>
<field name="model" invisible="1"/>
+1
View File
@@ -184,6 +184,7 @@ class RatingMixin(models.AbstractModel):
self.with_context(mail_notify_force_send=force_send).message_post_with_source(
template,
email_layout_xmlid='mail.mail_notification_light',
force_send=force_send,
subtype_xmlid='mail.mt_note',
)
+19 -5
View File
@@ -134,6 +134,7 @@ class TestComposerForm(TestMailComposer):
self.assertFalse(composer_form.composition_batch)
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.email_from, self.env.user.email_formatted)
self.assertTrue(composer_form.force_send, 'MailComposer: single record post send notifications right away')
self.assertFalse(composer_form.mail_server_id)
self.assertEqual(composer_form.model, self.test_record._name)
self.assertFalse(composer_form.partner_ids)
@@ -207,10 +208,11 @@ class TestComposerForm(TestMailComposer):
self.assertTrue(composer_form.auto_delete, 'Should take template value')
self.assertTrue(composer_form.auto_delete_keep_log)
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
self.assertEqual(composer_form.body, '<p>TemplateBody %s</p>' % self.test_record.name)
self.assertEqual(composer_form.body, f'<p>TemplateBody {self.test_record.name}</p>')
self.assertFalse(composer_form.composition_batch)
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.email_from, self.user_employee_2.email_formatted)
self.assertTrue(composer_form.force_send, 'MailComposer: single record post send notifications right away')
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_record._name)
self.assertEqual(composer_form.partner_ids[:], self.partner_1)
@@ -219,7 +221,7 @@ class TestComposerForm(TestMailComposer):
self.assertFalse(composer_form.reply_to_force_new)
self.assertEqual(literal_eval(composer_form.res_ids), self.test_record.ids)
self.assertEqual(composer_form.scheduled_date, FieldDatetime.to_string(self.reference_now + timedelta(days=2)))
self.assertEqual(composer_form.subject, 'TemplateSubject %s' % self.test_record.name)
self.assertEqual(composer_form.subject, f'TemplateSubject {self.test_record.name}')
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
self.assertFalse(composer_form.subtype_is_log)
@@ -242,6 +244,7 @@ class TestComposerForm(TestMailComposer):
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.email_from, self.template.email_from,
'MailComposer: comment in batch mode should have template raw email_from if template')
self.assertFalse(composer_form.force_send, 'MailComposer: batch record post use email queue for notifications')
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_record._name)
self.assertFalse(composer_form.record_name, 'MailComposer: comment in batch mode should have void record name')
@@ -270,6 +273,7 @@ class TestComposerForm(TestMailComposer):
self.assertFalse(composer_form.composition_batch)
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.email_from, self.env.user.partner_id.email_formatted)
self.assertTrue(composer_form.force_send)
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_record._name)
self.assertFalse(composer_form.partner_ids[:])
@@ -296,6 +300,7 @@ class TestComposerForm(TestMailComposer):
self.assertTrue(composer_form.composition_batch)
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.email_from, self.env.user.email_formatted)
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
self.assertFalse(composer_form.mail_server_id)
self.assertEqual(composer_form.model, self.test_records._name)
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
@@ -321,6 +326,7 @@ class TestComposerForm(TestMailComposer):
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.email_from, self.template.email_from,
'MailComposer: mass mode should have template raw email_from if template')
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_records._name)
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
@@ -351,6 +357,7 @@ class TestComposerForm(TestMailComposer):
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.email_from, self.template.email_from,
'MailComposer: mass mode should have template raw email_from if template')
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_records._name)
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
@@ -1347,9 +1354,16 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase):
# and 5 emails, and 1 inbox notification (admin)
self.assertEqual(len(self._new_mails), 2 * len(test_records),
'Should have created 1 mail.mail per group per record')
self.assertEqual(self._new_mails.mapped('state'), ['sent'] * len(self._new_mails))
self.assertEqual(len(self._mails), 5 * len(test_records),
'Should have sent 5 emails, one per recipient per record')
if not batch:
self.assertEqual(self._new_mails.mapped('state'), ['sent'] * len(self._new_mails))
self.assertEqual(len(self._mails), 5,
'Should have sent 5 emails, one per recipient per record')
else:
self.assertEqual(self._new_mails.mapped('state'), ['outgoing'] * len(self._new_mails))
self.assertEqual(len(self._mails), 0,
'Should have kept force_send False parameter, and not sent any email')
# simulate cron sending emails
self.env['mail.mail'].sudo().process_email_queue()
# template is sent only to partners (email_to are transformed)
for test_record in test_records: