[MERGE] Sync with trunk
[odoo/odoo.git] / addons / mail / mail_thread.py
index ebb7a40..9943114 100644 (file)
@@ -778,6 +778,7 @@ class mail_thread(osv.AbstractModel):
         """
         assert isinstance(message, Message), 'message must be an email.message.Message at this point'
         fallback_model = model
+        bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context)
 
         # Get email.message.Message variables for future processing
         message_id = message.get('Message-Id')
@@ -786,6 +787,24 @@ class mail_thread(osv.AbstractModel):
         references = decode_header(message, 'References')
         in_reply_to = decode_header(message, 'In-Reply-To')
 
+        # 0. Verify whether this is a bounced email (wrong destination,...) -> use it to collect data, such as dead leads
+        if bounce_alias in email_to:
+            bounce_match = tools.bounce_re.search(email_to)
+            if bounce_match:
+                bounced_mail_id = bounce_match.group(1)
+                if self.pool['mail.mail'].exists(cr, uid, bounced_mail_id):
+                    mail = self.pool['mail.mail'].browse(cr, uid, bounced_mail_id, context=context)
+                    bounced_model = mail.model
+                    bounced_thread_id = mail.res_id
+                else:
+                    bounced_model = bounce_match.group(2)
+                    bounced_thread_id = int(bounce_match.group(3)) if bounce_match.group(3) else 0
+                _logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
+                             email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
+                if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce'):
+                    self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
+                return []
+
         # 1. Verify if this is a reply to an existing thread
         thread_references = references or in_reply_to
         ref_match = thread_references and tools.reference_re.search(thread_references)
@@ -1024,6 +1043,15 @@ class mail_thread(osv.AbstractModel):
             self.write(cr, uid, ids, update_vals, context=context)
         return True
 
+    def message_receive_bounce(self, cr, uid, ids, mail_id=None, context=None):
+        """Called by ``message_process`` when a bounce email (such as Undelivered
+        Mail Returned to Sender) is received for an existing thread. The default
+        behavior is to check is an integer  ``message_bounce`` column exists.
+        If it is the case, its content is incremented. """
+        if self._all_columns.get('message_bounce'):
+            for obj in self.browse(cr, uid, ids, context=context):
+                self.write(cr, uid, [obj.id], {'message_bounce': obj.message_bounce + 1}, context=context)
+
     def _message_extract_payload(self, message, save_original=False):
         """Extract body as HTML and attachments from the mail message"""
         attachments = []
@@ -1395,6 +1423,12 @@ class mail_thread(osv.AbstractModel):
             parent_id = message_ids and message_ids[0] or False
         # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
         elif parent_id:
+            # update original mail_mail if exists
+            if type == 'email':
+                mail_mail_ids = self.pool['mail.mail'].search(cr, SUPERUSER_ID, [('mail_message_id', '=', parent_id)], context=context)
+                for mail in self.pool['mail.mail'].browse(cr, SUPERUSER_ID, mail_mail_ids, context=context):
+                    self.pool['mail.mail'].write(cr, SUPERUSER_ID, [mail.id], {'replied': mail.replied + 1}, context=context)
+
             message_ids = mail_message.search(cr, SUPERUSER_ID, [('id', '=', parent_id), ('parent_id', '!=', False)], context=context)
             # avoid loops when finding ancestors
             processed_list = []