[IMP] phone_validation: improve mobile/phone search

PURPOSE

This commit aims to simplify the phone filter of the lead search view
to better manage phone numbers containing letters.

SPECS

The current phone filter will remove all non-digit characters from the
input string. When the user enters an american phone number containing
letters, the filter can sometimes return phone numbers that do not match
exactly with the provided input string.

eg: If the user types "hello123" in the search bar, the filter will
return all phone numbers matching with "%123%". This pattern will indeed
match with "hello123" but it will also match with many other phone numbers
containing only "123".

To fix the issue, the phone filter will no longer trim all non-digit
characters from the input string. The filter will be more standard and
will better manage phone numbers containing letters.

LINKS

Task ID-2424185
COM PR odoo/odoo#64078

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Julien Banken
2021-04-23 13:15:49 +00:00
committed by Thibault Delavallée
parent 4b8c55697c
commit 0bc56aa7a2
2 changed files with 61 additions and 39 deletions
+24 -24
View File
@@ -452,13 +452,13 @@ class TestCRMLead(TestCrmCommon):
with self.assertRaises(UserError):
self.env['crm.lead'].search([('phone_mobile_search', 'like', '42')])
# + / 00 prefixes do not block search -> currently not working
# self.assertEqual(lead_1 + lead_2, self.env['crm.lead'].search([
# ('phone_mobile_search', 'like', '+32499332211')
# ]))
# self.assertEqual(lead_1 + lead_2, self.env['crm.lead'].search([
# ('phone_mobile_search', 'like', '0032499332211')
# ]))
# + / 00 prefixes do not block search
self.assertEqual(lead_1 + lead_2, self.env['crm.lead'].search([
('phone_mobile_search', 'like', '+32499332211')
]))
self.assertEqual(lead_1 + lead_2, self.env['crm.lead'].search([
('phone_mobile_search', 'like', '0032499332211')
]))
self.assertEqual(lead_1 + lead_2, self.env['crm.lead'].search([
('phone_mobile_search', 'like', '499332211')
]))
@@ -466,22 +466,22 @@ class TestCRMLead(TestCrmCommon):
('phone_mobile_search', 'like', '3322')
]))
# textual input still possible -> currently not working
# self.assertEqual(
# self.env['crm.lead'].search([('phone_mobile_search', 'like', 'hello')]),
# lead_3,
# 'Should behave like a text field'
# )
# self.assertEqual(
# self.env['crm.lead'].search([('phone_mobile_search', 'like', 'Hello')]),
# lead_3,
# 'Should behave like a text field'
# )
# self.assertEqual(
# self.env['crm.lead'].search([('phone_mobile_search', 'like', 'hello123')]),
# self.env['crm.lead'],
# 'Should behave like a text field'
# )
# textual input still possible
self.assertEqual(
self.env['crm.lead'].search([('phone_mobile_search', 'like', 'hello')]),
lead_3,
'Should behave like a text field'
)
self.assertEqual(
self.env['crm.lead'].search([('phone_mobile_search', 'like', 'Hello')]),
lead_3,
'Should behave like a text field'
)
self.assertEqual(
self.env['crm.lead'].search([('phone_mobile_search', 'like', 'hello123')]),
self.env['crm.lead'],
'Should behave like a text field'
)
@users('user_sales_manager')
def test_phone_mobile_search_format(self):
@@ -490,7 +490,7 @@ class TestCRMLead(TestCrmCommon):
'0499223311',
# separators
'0499/223311', '0499/22.33.11', '0499/22 33 11', '0499/223 311',
# international format
# international format -> currently not working
# '+32499223311', '0032499223311',
]
leads = self.env['crm.lead'].create([
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo import api, fields, models, _
from odoo.addons.phone_validation.tools import phone_validation
from odoo.exceptions import AccessError, UserError
@@ -45,26 +47,46 @@ class PhoneMixin(models.AbstractModel):
phone_mobile_search = fields.Char("Phone/Mobile", store=False, search='_search_phone_mobile_search')
def _search_phone_mobile_search(self, operator, value):
value = value.strip()
if len(value) < 3:
raise UserError(_('Please enter at least 3 characters when searching a Phone/Mobile number.'))
if len(value) <= 2:
raise UserError(_('Please enter at least 3 digits when searching on phone / mobile.'))
query = f"""
pattern = r'[\s\\./\(\)\-]'
if value.startswith('+') or value.startswith('00'):
# searching on +32485112233 should also finds 0032485112233 (and vice versa)
query = f"""
SELECT model.id
FROM {self._table} model
WHERE REGEXP_REPLACE(model.phone, '[^\d+]+', '', 'g') SIMILAR TO CONCAT(%s, REGEXP_REPLACE(%s, '\D+', '', 'g'), '%%')
OR REGEXP_REPLACE(model.mobile, '[^\d+]+', '', 'g') SIMILAR TO CONCAT(%s, REGEXP_REPLACE(%s, '\D+', '', 'g'), '%%')
WHERE
model.phone IS NOT NULL AND (
REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s OR
REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s
) OR
model.mobile IS NOT NULL AND (
REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s OR
REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s
);
"""
# searching on +32485112233 should also finds 00485112233 (00 / + prefix are both valid)
# we therefore remove it from input value and search for both of them in db
if value.startswith('+') or value.startswith('00'):
value = value.replace('+', '').replace('00', '', 1)
starts_with = '00|\+'
term = re.sub(pattern, '', value[1 if value.startswith('+') else 2:]) + '%'
self._cr.execute(query, (
pattern, '00' + term,
pattern, '+' + term,
pattern, '00' + term,
pattern, '+' + term
))
else:
starts_with = '%'
self._cr.execute(query, (starts_with, value, starts_with, value))
query = f"""
SELECT model.id
FROM {self._table} model
WHERE
REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s OR
REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s;
"""
term = '%' + re.sub(pattern, '', value) + '%'
self._cr.execute(query, (
pattern, term,
pattern, term
))
res = self._cr.fetchall()
if not res:
return [(0, '=', 1)]