diff --git a/addons/event_crm/models/event_registration.py b/addons/event_crm/models/event_registration.py
index 2fd8e9e4ac1..1abca8f3602 100644
--- a/addons/event_crm/models/event_registration.py
+++ b/addons/event_crm/models/event_registration.py
@@ -2,8 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
+from markupsafe import Markup
-from odoo import api, fields, models, _
+from odoo import api, fields, models, tools, _
from odoo.addons.phone_validation.tools import phone_validation
@@ -195,11 +196,17 @@ class EventRegistration(models.Model):
) # CHECKME: broader than just public partner
# mono registration mode: keep partner only if email and phone matches;
- # otherwise registration > partner. Note that _phone_format has to be
- # taken into account as it may have side effects on phone formatting
+ # otherwise registration > partner. Note that email format and phone
+ # formatting have to taken into account in comparison
if len(self) == 1 and valid_partner:
- if self.email and valid_partner.email and valid_partner.email != self.email:
- valid_partner = self.env['res.partner']
+ # compare emails: email_normalized or raw
+ if self.email and valid_partner.email:
+ if valid_partner.email_normalized and tools.email_normalize(self.email) != valid_partner.email_normalized:
+ valid_partner = self.env['res.partner']
+ elif not valid_partner.email_normalized and valid_partner.email != self.email:
+ valid_partner = self.env['res.partner']
+
+ # compare phone, taking into account formatting
if valid_partner and self.phone and valid_partner.phone:
phone_formatted = phone_validation.phone_format(
self.phone,
@@ -220,7 +227,7 @@ class EventRegistration(models.Model):
if not valid_partner.email:
contact_vals['email_from'] = self._find_first_notnull('email')
if not valid_partner.phone:
- contact_vals['email_from'] = self._find_first_notnull('phone')
+ contact_vals['phone'] = self._find_first_notnull('phone')
else:
# don't force email_from + partner_id because those fields are now synchronized automatically
contact_vals = {
@@ -249,17 +256,21 @@ class EventRegistration(models.Model):
line_suffix=line_suffix
) for registration in self
]
- return ("%s
" % prefix if prefix else "") + (
- "
" if line_counter else "") + ("".join(reg_lines)) + ("
" if line_counter else "")
+ description = (prefix if prefix else '') + Markup("
")
+ if line_counter:
+ description += Markup("") + Markup('').join(reg_lines) + Markup("
")
+ else:
+ description += Markup("") + Markup('').join(reg_lines) + Markup("
")
+ return description
def _get_lead_description_registration(self, line_suffix=''):
""" Build the description line specific to a given registration. """
self.ensure_one()
- return "%s (%s)%s" % (
+ return Markup("") + "%s (%s)%s" % (
self.name or self.partner_id.name or self.email,
" - ".join(self[field] for field in ('email', 'phone') if self[field]),
- " %s" % line_suffix if line_suffix else "",
- )
+ f" {line_suffix}" if line_suffix else "",
+ ) + Markup("")
def _get_lead_tracked_values(self):
""" Tracked values are based on two subset of fields to track in order
@@ -340,8 +351,8 @@ class EventRegistration(models.Model):
def _convert_value(self, value, field_name):
""" Small tool because convert_to_write is touchy """
- if value and self._fields[field_name].type in ['many2many', 'one2many']:
+ if isinstance(value, models.BaseModel) and self._fields[field_name].type in ['many2many', 'one2many']:
return value.ids
- if value and self._fields[field_name].type == 'many2one':
+ if isinstance(value, models.BaseModel) and self._fields[field_name].type == 'many2one':
return value.id
return value
diff --git a/addons/event_crm/tests/common.py b/addons/event_crm/tests/common.py
index 6d53c0fb41f..8197aca73ea 100644
--- a/addons/event_crm/tests/common.py
+++ b/addons/event_crm/tests/common.py
@@ -3,7 +3,7 @@
from datetime import datetime, timedelta
-from odoo import fields
+from odoo import fields, tools
from odoo.addons.crm.tests.common import TestCrmCommon
from odoo.addons.event.tests.common import EventCase
@@ -99,8 +99,8 @@ class EventCrmCase(TestCrmCommon, EventCase):
self.assertNotIn('False', lead.name) # avoid a "Dear False" like construct ^^ (this assert is serious and intended)
self.assertEqual(lead.contact_name, expected_contact_name)
self.assertEqual(lead.partner_name, expected_partner_name)
- self.assertEqual(lead.email_from, partner.email if partner else registrations._find_first_notnull('email'))
- self.assertEqual(lead.phone, partner.phone if partner else registrations._find_first_notnull('phone'))
+ self.assertEqual(lead.email_from, partner.email if partner and partner.email else registrations._find_first_notnull('email'))
+ self.assertEqual(lead.phone, partner.phone if partner and partner.phone else registrations._find_first_notnull('phone'))
self.assertEqual(lead.mobile, partner.mobile if partner and partner.mobile else registrations._find_first_notnull('mobile'))
# description: to improve
@@ -111,7 +111,10 @@ class EventCrmCase(TestCrmCommon, EventCase):
elif registration.partner_id.name:
self.assertIn(registration.partner_id.name, lead.description)
if registration.email:
- self.assertIn(registration.email, lead.description)
+ if tools.email_normalize(registration.email) == registration.partner_id.email_normalized:
+ self.assertIn(registration.partner_id.email, lead.description)
+ else:
+ self.assertIn(tools.email_normalize(registration.email), lead.description)
if registration.phone:
self.assertIn(registration.phone, lead.description)
diff --git a/addons/event_crm/tests/test_event_crm_flow.py b/addons/event_crm/tests/test_event_crm_flow.py
index 74352c775db..b0fe62e5f76 100644
--- a/addons/event_crm/tests/test_event_crm_flow.py
+++ b/addons/event_crm/tests/test_event_crm_flow.py
@@ -18,11 +18,14 @@ class TestEventCrmFlow(TestEventCrmCommon):
]
cls.registration_values[-1]['email'] = '"John Doe" '
- def assert_initial_data(self):
+ def test_assert_initial_data(self):
+ """ Ensure base test values to ease test understanding and maintenance """
self.assertEqual(len(self.registration_values), 5)
+
+ self.assertEqual(self.event_customer.country_id, self.env.ref('base.be'))
self.assertEqual(self.event_customer.email_normalized, 'constantin@test.example.com')
- self.assertEqual(self.event_customer.phone, '0485112233')
self.assertFalse(self.event_customer.mobile)
+ self.assertEqual(self.event_customer.phone, '0485112233')
@users('user_eventregistrationdesk')
def test_event_crm_flow_batch_create(self):
@@ -97,43 +100,24 @@ class TestEventCrmFlow(TestEventCrmCommon):
self.assertNotIn('invalid@not.example.com', lead.description)
@users('user_eventregistrationdesk')
- def test_event_crm_flow_per_attendee_single(self):
- self.assert_initial_data()
-
- # test: partner-based contact information, everything synchonized
- registration = self.env['event.registration'].create({
- 'partner_id': self.event_customer.id,
- 'event_id': self.event_0.id,
- })
- self.assertEqual(registration.email, self.event_customer.email)
- self.assertEqual(registration.phone, self.event_customer.phone)
-
- # per-attendee rule
- self.assertLeadConvertion(self.test_rule_attendee, registration, partner=registration.partner_id)
-
- # test: no partner and contact information
- registration = self.env['event.registration'].create({
- 'name': 'My Registration',
- 'partner_id': False,
- 'email': 'super.email@test.example.com',
- 'phone': False,
- 'mobile': '0456332211',
- 'event_id': self.event_0.id,
- })
- self.assertEqual(len(self.event_0.registration_ids), 2)
- self.assertLeadConvertion(self.test_rule_attendee, registration, partner=None)
-
- # test: no partner and few information
- registration = self.env['event.registration'].create({
- 'name': False,
- 'partner_id': False,
- 'email': 'giga.email@test.example.com',
- 'phone': False,
- 'mobile': False,
- 'event_id': self.event_0.id,
- })
- self.assertEqual(len(self.event_0.registration_ids), 3)
- self.assertLeadConvertion(self.test_rule_attendee, registration, partner=None)
+ def test_event_crm_flow_per_attendee_single_wo_partner(self):
+ """ Single registration, attendee based, no partner involved, check
+ contact info propagation """
+ for name, email, mobile, phone in [
+ ('My Name', 'super.email@test.example.com', '0456442211', '0456332211'),
+ (False, 'super.email@test.example.com', False, '0456442211'),
+ ('"My Name"', '"My Name" ', False, False),
+ ]:
+ with self.subTest(name=name, email=email, mobile=mobile, phone=phone):
+ registration = self.env['event.registration'].create({
+ 'name': name,
+ 'partner_id': False,
+ 'email': email,
+ 'mobile': mobile,
+ 'phone': phone,
+ 'event_id': self.event_0.id,
+ })
+ self.assertLeadConvertion(self.test_rule_attendee, registration, partner=None)
# test: partner but with other contact information -> registration prior
registration = self.env['event.registration'].create({
@@ -143,9 +127,36 @@ class TestEventCrmFlow(TestEventCrmCommon):
'mobile': '0456112233',
'event_id': self.event_0.id,
})
- self.assertEqual(len(self.event_0.registration_ids), 4)
self.assertLeadConvertion(self.test_rule_attendee, registration, partner=None)
+ @users('user_eventregistrationdesk')
+ def test_event_crm_flow_per_attendee_single_wpartner(self):
+ """ Single registration, attendee based, with partner involved, check
+ contact information, check synchronization and update """
+ self.event_customer2.write({
+ 'email': False,
+ 'phone': False,
+ })
+ self.test_rule_attendee.write({
+ 'event_registration_filter': '[]', # try various email combinations
+ })
+ for email, phone, base_partner, expected_partner in [
+ (False, False, self.event_customer, self.event_customer), # should take partner info
+ ('"Other Name" ', False, self.event_customer, self.event_customer), # same email normalized
+ ('other.email@test.example.com', False, self.event_customer, self.env['res.partner']), # not same email -> no partner on lead
+ (False, '+32485112233', self.event_customer, self.event_customer), # same phone but differently formatted
+ (False, '0485112244', self.event_customer, self.env['res.partner']), # other phone -> no partner on lead
+ ('other.email@test.example.com', '0485112244', self.event_customer2, self.event_customer2), # mail / phone update from registration as void on partner
+ ]:
+ with self.subTest(email=email, phone=phone, base_partner=base_partner):
+ registration = self.env['event.registration'].create({
+ 'partner_id': base_partner.id,
+ 'email': email,
+ 'phone': phone,
+ 'event_id': self.event_0.id,
+ })
+ self.assertLeadConvertion(self.test_rule_attendee, registration, partner=expected_partner)
+
@users('user_eventregistrationdesk')
def test_event_crm_trigger_done(self):
"""Test the case when the "crm.lead.rule" is executed when we write on the