From 99828563c165fe0ef2be955e3ca3ebc1063a02b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= <tde@odoo.com> Date: Tue, 19 Apr 2022 14:52:56 +0200 Subject: [PATCH] [IMP] various: support multi-emails in '_message_post_after_hook' PURPOSE Be defensive when dealing with email fields, notably when having multi-emails or email field containing an already-formatted email. SPECIFICATIONS Post-message hook is used in various apps to link records to a newly created created partner. This is the case notably when used with a template as it creates partner on the fly based on emails to always handle partners. We now check either the complete 'email', either the normalized version of it to avoid comparison issues with multi emails and formatted emails. As 'email_normalized' now supports multi-emails by storing the first found one it helps finding the partner. Task-2612945 (Mail: Defensive email formatting) X-original-commit: odoo/odoo@62f28f1b898ff9f37bb74345a814e83b37386181 Part-of: odoo/odoo#133958 --- addons/crm/models/crm_lead.py | 13 ++++++---- addons/event/models/event_registration.py | 17 ++++++++----- .../hr_recruitment/models/hr_recruitment.py | 18 +++++++++----- addons/project/models/project.py | 14 +++++++---- .../website_event_track/models/event_track.py | 24 +++++++++++-------- 5 files changed, 56 insertions(+), 30 deletions(-) diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index 19b565c9f6f2..bf8dd84981ac 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -1838,12 +1838,17 @@ class Lead(models.Model): # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using # suggested recipients. This heuristic allows to avoid ugly hacks in JS. - new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email_from) + new_partner = message.partner_ids.filtered( + lambda partner: partner.email == self.email_from or (self.email_normalized and partner.email_normalized == self.email_normalized) + ) if new_partner: + if new_partner[0].email_normalized: + email_domain = ('email_normalized', '=', new_partner[0].email_normalized) + else: + email_domain = ('email_from', '=', new_partner[0].email) self.search([ - ('partner_id', '=', False), - ('email_from', '=', new_partner.email), - ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id}) + ('partner_id', '=', False), email_domain, ('stage_id.fold', '=', False) + ]).write({'partner_id': new_partner[0].id}) return super(Lead, self)._message_post_after_hook(message, msg_vals) def _message_partner_info_from_emails(self, emails, link_mail=False): diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py index 3ecb8ea39d51..31c81a92e2e5 100644 --- a/addons/event/models/event_registration.py +++ b/addons/event/models/event_registration.py @@ -4,7 +4,7 @@ from dateutil.relativedelta import relativedelta from odoo import _, api, fields, models, SUPERUSER_ID -from odoo.tools import format_datetime +from odoo.tools import format_datetime, email_normalize from odoo.exceptions import AccessError, ValidationError @@ -305,13 +305,18 @@ class EventRegistration(models.Model): # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using # suggested recipients. This heuristic allows to avoid ugly hacks in JS. - new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email) + email_normalized = email_normalize(self.email) + new_partner = message.partner_ids.filtered( + lambda partner: partner.email == self.email or (email_normalized and partner.email_normalized == email_normalized) + ) if new_partner: + if new_partner[0].email_normalized: + email_domain = ('email', 'in', [new_partner[0].email, new_partner[0].email_normalized]) + else: + email_domain = ('email', '=', new_partner[0].email) self.search([ - ('partner_id', '=', False), - ('email', '=', new_partner.email), - ('state', 'not in', ['cancel']), - ]).write({'partner_id': new_partner.id}) + ('partner_id', '=', False), email_domain, ('state', 'not in', ['cancel']), + ]).write({'partner_id': new_partner[0].id}) return super(EventRegistration, self)._message_post_after_hook(message, msg_vals) # ------------------------------------------------------------ diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index 4a7965451464..73544832831c 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -470,18 +470,24 @@ class Applicant(models.Model): # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using # suggested recipients. This heuristic allows to avoid ugly hacks in JS. - new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email_from) + email_normalized = tools.email_normalize(self.email_from) + new_partner = message.partner_ids.filtered( + lambda partner: partner.email == self.email_from or (email_normalized and partner.email_normalized == email_normalized) + ) if new_partner: - if new_partner.create_date.date() == fields.Date.today(): - new_partner.write({ + if new_partner[0].create_date.date() == fields.Date.today(): + new_partner[0].write({ 'type': 'private', 'phone': self.partner_phone, 'mobile': self.partner_mobile, }) + if new_partner[0].email_normalized: + email_domain = ('email_from', 'in', [new_partner[0].email, new_partner[0].email_normalized]) + else: + email_domain = ('email_from', '=', new_partner[0].email) self.search([ - ('partner_id', '=', False), - ('email_from', '=', new_partner.email), - ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id}) + ('partner_id', '=', False), email_domain, ('stage_id.fold', '=', False) + ]).write({'partner_id': new_partner[0].id}) return super(Applicant, self)._message_post_after_hook(message, msg_vals) def create_employee_from_applicant(self): diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 5cc17438806a..50a4fb3409aa 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -2122,12 +2122,18 @@ class Task(models.Model): # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using # suggested recipients. This heuristic allows to avoid ugly hacks in JS. - new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email_from) + email_normalized = tools.email_normalize(self.email_from) + new_partner = message.partner_ids.filtered( + lambda partner: partner.email == self.email_from or (email_normalized and partner.email_normalized == email_normalized) + ) if new_partner: + if new_partner[0].email_normalized: + email_domain = ('email_from', 'in', [new_partner[0].email, new_partner[0].email_normalized]) + else: + email_domain = ('email_from', '=', new_partner[0].email) self.search([ - ('partner_id', '=', False), - ('email_from', '=', new_partner.email), - ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id}) + ('partner_id', '=', False), email_domain, ('stage_id.fold', '=', False) + ]).write({'partner_id': new_partner[0].id}) return super(Task, self)._message_post_after_hook(message, msg_vals) def action_assign_to_me(self): diff --git a/addons/website_event_track/models/event_track.py b/addons/website_event_track/models/event_track.py index 22b5ffec3b41..2ae02b423123 100644 --- a/addons/website_event_track/models/event_track.py +++ b/addons/website_event_track/models/event_track.py @@ -5,7 +5,7 @@ from datetime import timedelta from pytz import utc from random import randint -from odoo import api, fields, models +from odoo import api, fields, models, tools from odoo.addons.http_routing.models.ir_http import slug from odoo.osv import expression from odoo.tools.mail import is_html_empty @@ -482,15 +482,19 @@ class Track(models.Model): # Contact(s) created from chatter set on track : we verify if at least one is the expected contact # linked to the track. (created from contact_email if any, then partner_email if any) main_email = self.contact_email or self.partner_email - if main_email: - new_partner = message.partner_ids.filtered(lambda partner: partner.email == main_email) - if new_partner: - main_email_string = 'contact_email' if self.contact_email else 'partner_email' - self.search([ - ('partner_id', '=', False), - (main_email_string, '=', new_partner.email), - ('stage_id.is_cancel', '=', False), - ]).write({'partner_id': new_partner.id}) + main_email_normalized = tools.email_normalize(main_email) + new_partner = message.partner_ids.filtered( + lambda partner: partner.email == main_email or (main_email_normalized and partner.email_normalized == main_email_normalized) + ) + if new_partner: + mail_email_fname = 'contact_email' if self.contact_email else 'partner_email' + if new_partner[0].email_normalized: + email_domain = (mail_email_fname, 'in', [new_partner[0].email, new_partner[0].email_normalized]) + else: + email_domain = (mail_email_fname, '=', new_partner[0].email) + self.search([ + ('partner_id', '=', False), email_domain, ('stage_id.is_cancel', '=', False), + ]).write({'partner_id': new_partner[0].id}) return super(Track, self)._message_post_after_hook(message, msg_vals) def _track_template(self, changes): -- GitLab