From 38e181ee2c858decbf8feb935f59019591506621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 18 Aug 2016 13:55:00 +0200 Subject: [PATCH] [IMP] mail: send email notifications after transaction commit Using after commit event notifications emails are now send once the current transaction is finished. This allows having issues with emails being sent while the transaction failed sometimes after email sending, but still in the same transaction. A small hook is done to allow invitation emails to be sent directly as some data related to mail.message is unlinked directly in the wizard. --- addons/mail/models/mail_message.py | 4 ++-- addons/mail/models/res_partner.py | 34 ++++++++++++++++++++++++------ addons/mail/wizard/invite.py | 2 +- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index a2f1c516560..53794d4038a 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -749,7 +749,7 @@ class Message(models.Model): #------------------------------------------------------ @api.multi - def _notify(self, force_send=False, user_signature=True): + def _notify(self, force_send=False, send_after_commit=True, user_signature=True): """ Add the related record followers to the destination partner_ids if is not a private message. Call mail_notification.notify to manage the email sending """ @@ -786,7 +786,7 @@ class Message(models.Model): self.write({'channel_ids': [(6, 0, channels.ids)], 'needaction_partner_ids': [(6, 0, partners.ids)]}) # notify partners and channels - partners._notify(self, force_send=force_send, user_signature=user_signature) + partners._notify(self, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature) channels._notify(self) # Discard cache, because child / parent allow reading and therefore diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index 7c3a96ba36b..125acce8010 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -4,7 +4,7 @@ import logging import threading -from odoo import _, api, fields, models +from odoo import _, api, fields, models, registry, SUPERUSER_ID from odoo.osv import expression _logger = logging.getLogger(__name__) @@ -145,7 +145,7 @@ class Partner(models.Model): }) @api.multi - def _notify(self, message, force_send=False, user_signature=True): + def _notify(self, message, force_send=False, send_after_commit=True, user_signature=True): # TDE TODO: model-dependant ? (like customer -> always email ?) message_sudo = message.sudo() email_channels = message.channel_ids.filtered(lambda channel: channel.email_send) @@ -154,14 +154,19 @@ class Partner(models.Model): ('id', 'in', self.ids), ('channel_ids', 'in', email_channels.ids), ('email', '!=', message_sudo.author_id and message_sudo.author_id.email or message.email_from), - ('notify_email', '!=', 'none')])._notify_by_email(message, force_send=force_send, user_signature=user_signature) + ('notify_email', '!=', 'none')])._notify_by_email(message, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature) self._notify_by_chat(message) return True @api.multi - def _notify_by_email(self, message, force_send=False, user_signature=True): + def _notify_by_email(self, message, force_send=False, send_after_commit=True, user_signature=True): """ Method to send email linked to notified messages. The recipients are - the recordset on which this method is called. """ + the recordset on which this method is called. + + :param boolean force_send: send notification emails now instead of letting the scheduler handle the email queue + :param boolean send_after_commit: send notification emails after the transaction end instead of durign the + transaction; this option is used only if force_send is True + :param user_signature: add current user signature to notification emails """ if not self.ids: return True @@ -220,9 +225,24 @@ class Partner(models.Model): # 2. do not send emails immediately if the registry is not loaded, # to prevent sending email during a simple update of the database # using the command-line. + test_mode = getattr(threading.currentThread(), 'testing', False) if force_send and recipients_nbr < recipients_max and \ - (not self.pool._init or getattr(threading.currentThread(), 'testing', False)): - emails.send() + (not self.pool._init or test_mode): + email_ids = emails.ids + dbname = self.env.cr.dbname + + def send_notifications(): + db_registry = registry(dbname) + with db_registry.cursor() as cr: + env = api.Environment(cr, SUPERUSER_ID, {}) + env['mail.mail'].browse(email_ids).send() + + # unless asked specifically, send emails after the transaction to + # avoid side effects due to emails being sent while the transaction fails + if not test_mode and send_after_commit: + self._cr.after('commit', send_notifications) + else: + emails.send() return True diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 51b765bc95a..1d345af517a 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -60,6 +60,6 @@ class Invite(models.TransientModel): 'res_id': wizard.res_id, 'no_auto_thread': True, }) - new_partners.with_context(auto_delete=True)._notify(message, force_send=True, user_signature=True) + new_partners.with_context(auto_delete=True)._notify(message, force_send=True, send_after_commit=False, user_signature=True) message.unlink() return {'type': 'ir.actions.act_window_close'}