From 3c1e464589fbf5b1823b04881c030476c2f7b196 Mon Sep 17 00:00:00 2001 From: Yolann Sabaux Date: Mon, 7 Mar 2022 11:13:08 +0000 Subject: [PATCH] [FIX] mail: mass mailing to same mail adress Steps to repoduce: - Accounting > Customers > Invoices: select several invoices to send - Action > Send & Print > (deselect Print) > Send & Print Issue: - It sends only one invoice per company Cause: - the mail_compose_message sets the status of an email as `cancel` when a mail has already been sent to a specific adress mail in the batch Solution: - If the use of mass mailing is document-based (e.g.: sending multiple invoices) it will allow to send multiple emails to the same adress opw-2775121 closes odoo/odoo#88992 X-original-commit: f08685020f6a00d4e10e30ccbcf70c9eb1a764d5 Signed-off-by: Thibault Delavallee (tde) --- addons/account/wizard/account_invoice_send.py | 5 +++- addons/mail/wizard/mail_compose_message.py | 6 +++-- .../purchase/models/mail_compose_message.py | 6 +++-- addons/sale/wizard/mail_compose_message.py | 6 +++-- addons/test_mail/tests/test_mail_composer.py | 25 +++++++++++++++++++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/addons/account/wizard/account_invoice_send.py b/addons/account/wizard/account_invoice_send.py index e577127ba67..c1c747e5827 100644 --- a/addons/account/wizard/account_invoice_send.py +++ b/addons/account/wizard/account_invoice_send.py @@ -116,7 +116,10 @@ class AccountInvoiceSend(models.TransientModel): def _send_email(self): if self.is_email: # with_context : we don't want to reimport the file we just exported. - self.composer_id.with_context(no_new_invoice=True, mail_notify_author=self.env.user.partner_id in self.composer_id.partner_ids)._action_send_mail() + self.composer_id.with_context(no_new_invoice=True, + mail_notify_author=self.env.user.partner_id in self.composer_id.partner_ids, + mailing_document_based=True, + )._action_send_mail() if self.env.context.get('mark_invoice_as_sent'): #Salesman send posted invoice, without the right to write #but they should have the right to change this flag diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 3fca30f5caf..f264e170819 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -473,6 +473,8 @@ class MailComposer(models.TransientModel): blacklist_ids = self._get_blacklist_record_ids(mail_values_dict) optout_emails = self._get_optout_emails(mail_values_dict) done_emails = self._get_done_emails(mail_values_dict) + # in case of an invoice e.g. + mailing_document_based = self.env.context.get('mailing_document_based') for record_id, mail_values in mail_values_dict.items(): recipients = recipients_info[record_id] @@ -495,7 +497,7 @@ class MailComposer(models.TransientModel): elif optout_emails and mail_to in optout_emails: mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_optout' - elif done_emails and mail_to in done_emails: + elif done_emails and mail_to in done_emails and not mailing_document_based: mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_dup' # void of falsy values -> error @@ -505,7 +507,7 @@ class MailComposer(models.TransientModel): elif not mail_to_normalized or not email_re.findall(mail_to): mail_values['state'] = 'cancel' mail_values['failure_type'] = 'mail_email_invalid' - elif done_emails is not None: + elif done_emails is not None and not mailing_document_based: done_emails.append(mail_to) return mail_values_dict diff --git a/addons/purchase/models/mail_compose_message.py b/addons/purchase/models/mail_compose_message.py index 2a8c76b7ee0..7d98e99ce47 100644 --- a/addons/purchase/models/mail_compose_message.py +++ b/addons/purchase/models/mail_compose_message.py @@ -8,6 +8,8 @@ class MailComposeMessage(models.TransientModel): _inherit = 'mail.compose.message' def _action_send_mail(self, auto_commit=False): - if self.env.context.get('mark_rfq_as_sent') and self.model == 'purchase.order': - self = self.with_context(mail_notify_author=self.env.user.partner_id in self.partner_ids) + if self.model == 'purchase.order': + self = self.with_context(mailing_document_based=True) + if self.env.context.get('mark_rfq_as_sent'): + self = self.with_context(mail_notify_author=self.env.user.partner_id in self.partner_ids) return super(MailComposeMessage, self)._action_send_mail(auto_commit=auto_commit) diff --git a/addons/sale/wizard/mail_compose_message.py b/addons/sale/wizard/mail_compose_message.py index 43217dbc616..6af644c0f8a 100644 --- a/addons/sale/wizard/mail_compose_message.py +++ b/addons/sale/wizard/mail_compose_message.py @@ -8,6 +8,8 @@ class MailComposeMessage(models.TransientModel): _inherit = 'mail.compose.message' def _action_send_mail(self, auto_commit=False): - if self.env.context.get('mark_so_as_sent') and self.model == 'sale.order': - self = self.with_context(mail_notify_author=self.env.user.partner_id in self.partner_ids) + if self.model == 'sale.order': + self = self.with_context(mailing_document_based=True) + if self.env.context.get('mark_so_as_sent'): + self = self.with_context(mail_notify_author=self.env.user.partner_id in self.partner_ids) return super(MailComposeMessage, self)._action_send_mail(auto_commit=auto_commit) diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index c9ad9faf0e4..ba416aef8c6 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -701,6 +701,31 @@ class TestComposerResultsComment(TestMailComposer): """ Test global output of composer used in comment mode. Test notably notification and emails generated during this process. """ + @users('employee') + @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') + def test_mail_composer_document_based(self): + """ Tests a document-based mass mailing with the same address mails + This should be allowed and not considered as duplicate in this context + """ + attachment_data = self._generate_attachments_data(2) + email_to_1 = self.test_record.customer_id.email + self.template.write({ + 'auto_delete': False, # keep sent emails to check content + 'attachment_ids': [(0, 0, a) for a in attachment_data], + 'email_to': '%s, %s' % (email_to_1, email_to_1), + 'report_name': 'TestReport for {{ object.name }}', # test cursor forces html + 'report_template': self.test_report.id, + }) + # launch composer in mass mode + composer_form = Form(self.env['mail.compose.message'].with_context( + self._get_web_context(self.test_record, add_web=True, + default_template_id=self.template.id) + )) + composer = composer_form.save() + with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app(): + composer.with_context(mailing_document_based=True)._action_send_mail() + self.assertEqual(len(self._mails), 2, 'Should have sent 2 emails.') + @users('employee') @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') def test_mail_composer_notifications_delete(self):