diff --git a/addons/calendar/models/calendar_alarm_manager.py b/addons/calendar/models/calendar_alarm_manager.py index f67a32d5d5e..916ddd7fdc9 100644 --- a/addons/calendar/models/calendar_alarm_manager.py +++ b/addons/calendar/models/calendar_alarm_manager.py @@ -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, diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index ac2fde727a2..b2b1ee98b3d 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -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, ) diff --git a/addons/mail/wizard/mail_compose_message_views.xml b/addons/mail/wizard/mail_compose_message_views.xml index 93078b739ce..bb4f0e31233 100644 --- a/addons/mail/wizard/mail_compose_message_views.xml +++ b/addons/mail/wizard/mail_compose_message_views.xml @@ -15,6 +15,7 @@ + diff --git a/addons/rating/models/rating_mixin.py b/addons/rating/models/rating_mixin.py index 599fe908804..b06797afd2e 100644 --- a/addons/rating/models/rating_mixin.py +++ b/addons/rating/models/rating_mixin.py @@ -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', ) diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index 251457ba98f..10f1234b9b3 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -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, '

TemplateBody %s

' % self.test_record.name) + self.assertEqual(composer_form.body, f'

TemplateBody {self.test_record.name}

') 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: