# Copyright 2019 Alexandre Díaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from email.utils import getaddresses from lxml import etree from odoo import _, api, fields, models from odoo.tools import email_split_and_format class MailThread(models.AbstractModel): _inherit = "mail.thread" failed_message_ids = fields.One2many( "mail.message", "res_id", string="Failed Messages", domain=lambda self: [("model", "=", self._name)] + self._get_failed_message_domain(), ) def _get_failed_message_domain(self): """Domain used to display failed messages on the 'failed_messages' widget""" failed_states = self.env["mail.message"].get_failed_states() return [ ("mail_tracking_needs_action", "=", True), ("mail_tracking_ids.state", "in", list(failed_states)), ] @api.returns("self", lambda value: value.id) def message_post(self, *args, **kwargs): """Adds CC recipient to the message. Because Odoo implementation avoid store cc recipients we ensure that this information its written into the mail.message record. """ new_message = super().message_post(*args, **kwargs) email_cc = kwargs.get("cc") if email_cc: new_message.sudo().write({"email_cc": email_cc}) return new_message def _message_get_suggested_recipients(self): """Adds email Cc recipients as suggested recipients. If the recipient has a res.partner, use it. """ res = super()._message_get_suggested_recipients() ResPartnerObj = self.env["res.partner"] email_cc_formated_list = [] for record in self: emails_cc = record.message_ids.mapped("email_cc") for email in emails_cc: email_cc_formated_list.extend(email_split_and_format(email)) email_cc_formated_list = set(email_cc_formated_list) for cc in email_cc_formated_list: email_parts = getaddresses([cc])[0] partner_id = record._message_partner_info_from_emails([email_parts[1]])[ 0 ].get("partner_id") if not partner_id: record._message_add_suggested_recipient(res, email=cc, reason=_("Cc")) else: partner = ResPartnerObj.browse(partner_id) record._message_add_suggested_recipient( res, partner=partner, reason=_("Cc") ) return res @api.model def fields_view_get( self, view_id=None, view_type="form", toolbar=False, submenu=False ): """Add filters for failed messages. These filters will show up on any form or search views of any model inheriting from ``mail.thread``. """ res = super().fields_view_get( view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu ) if view_type not in {"search", "form"}: return res doc = etree.XML(res["arch"]) if view_type == "search": # Modify view to add new filter element nodes = doc.xpath("//search") if nodes: # Create filter element new_filter = etree.Element( "filter", { "string": _("Failed sent messages"), "name": "failed_message_ids", "domain": str( [ [ "failed_message_ids.mail_tracking_ids.state", "in", list(self.env["mail.message"].get_failed_states()), ], [ "failed_message_ids.mail_tracking_needs_action", "=", True, ], ] ), }, ) nodes[0].append(etree.Element("separator")) nodes[0].append(new_filter) elif view_type == "form": # Modify view to add new field element nodes = doc.xpath("//field[@name='message_ids' and @widget='mail_thread']") if nodes: # Create field field_failed_messages = etree.Element( "field", {"name": "failed_message_ids", "widget": "mail_failed_message"}, ) nodes[0].addprevious(field_failed_messages) res["arch"] = etree.tostring(doc, encoding="unicode") return res