[FIX] phone_validation: correctly support phone/mobile search parameters

Currently the search method defined on 'phone_mobile_search' supports either
a boolean (is set / is not set) search, either considers all searches to be
"like TERM". This is coming from the main usage that is the search view
that sends domains like "[('phone_mobile_search', 'ilike', term)]".

In this commit we improve the method to correctly support

  * direct check (=)
  * negative operators (not (i)like)
  * like / ilike (previously like were considered as ilike)

Idea is: a positive operator check that any of the phone fields respects the
domain (phone or mobile is TERM). A negative operator checks that all phone
fields respects the negative domain (both phone and mobile do not contain TERM).

Tests are added.

Task-3012789

X-original-commit: 8e905343f38a4dae828ccaddc62ce5c6a2f754db
Part-of: odoo/odoo#104111
This commit is contained in:
Thibault Delavallée
2022-10-26 00:55:05 +02:00
parent 897eedf22e
commit 9a5046ba0a
3 changed files with 51 additions and 38 deletions
+6 -2
View File
@@ -735,10 +735,14 @@ class TestCRMLead(TestCrmCommon):
lead_3,
'Should behave like a text field'
)
self.assertEqual(
self.assertFalse(
self.env['crm.lead'].search([('phone_mobile_search', 'like', 'Hello')]),
'Should behave like a text field (case sensitive)'
)
self.assertEqual(
self.env['crm.lead'].search([('phone_mobile_search', 'ilike', 'Hello')]),
lead_3,
'Should behave like a text field'
'Should behave like a text field (case insensitive)'
)
self.assertEqual(
self.env['crm.lead'].search([('phone_mobile_search', 'like', 'hello123')]),
@@ -68,37 +68,52 @@ class PhoneMixin(models.AbstractModel):
raise UserError(_('Please enter at least 3 characters when searching a Phone/Mobile number.'))
pattern = r'[\s\\./\(\)\-]'
sql_operator = {'=like': 'LIKE', '=ilike': 'ILIKE'}.get(operator, operator)
if value.startswith('+') or value.startswith('00'):
# searching on +32485112233 should also finds 0032485112233 (and vice versa)
# we therefore remove it from input value and search for both of them in db
where_str = ' OR '.join(
f"""model.{phone_field} IS NOT NULL AND (
REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s OR
REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s
)"""
for phone_field in phone_fields
)
query = f"""
SELECT model.id
FROM {self._table} model
WHERE {where_str};
"""
term = re.sub(pattern, '', value[1 if value.startswith('+') else 2:]) + '%'
if operator in expression.NEGATIVE_TERM_OPERATORS:
# searching on +32485112233 should also finds 0032485112233 (and vice versa)
# we therefore remove it from input value and search for both of them in db
where_str = ' AND '.join(
f"""model.{phone_field} IS NULL OR (
REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s OR
REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s
)"""
for phone_field in phone_fields
)
else:
# searching on +32485112233 should also finds 0032485112233 (and vice versa)
# we therefore remove it from input value and search for both of them in db
where_str = ' OR '.join(
f"""model.{phone_field} IS NOT NULL AND (
REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s OR
REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s
)"""
for phone_field in phone_fields
)
query = f"SELECT model.id FROM {self._table} model WHERE {where_str};"
term = re.sub(pattern, '', value[1 if value.startswith('+') else 2:])
if operator not in ('=', '!='): # for like operators
term = f'{term}%'
self._cr.execute(
query,
(pattern, '00' + term, pattern, '+' + term) * len(phone_fields)
query, (pattern, '00' + term, pattern, '+' + term) * len(phone_fields)
)
else:
where_str = ' OR '.join(
f"REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') ILIKE %s"
for phone_field in phone_fields
)
query = f"""
SELECT model.id
FROM {self._table} model
WHERE {where_str};
"""
term = '%' + re.sub(pattern, '', value) + '%'
if operator in expression.NEGATIVE_TERM_OPERATORS:
where_str = ' AND '.join(
f"(model.{phone_field} IS NULL OR REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s)"
for phone_field in phone_fields
)
else:
where_str = ' OR '.join(
f"(model.{phone_field} IS NOT NULL AND REGEXP_REPLACE(model.{phone_field}, %s, '', 'g') {sql_operator} %s)"
for phone_field in phone_fields
)
query = f"SELECT model.id FROM {self._table} model WHERE {where_str};"
term = re.sub(pattern, '', value)
if operator not in ('=', '!='): # for like operators
term = f'%{term}%'
self._cr.execute(query, (pattern, term) * len(phone_fields))
res = self._cr.fetchall()
if not res:
@@ -75,9 +75,8 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients):
test_phone_records = self.test_phone_records.with_env(self.env)
# test "=" search
# Currently not suported, returning results for "ilike"
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0475')])
self.assertEqual(results, test_phone_records[:5])
self.assertFalse(results, 'Search on phone_mobile_search: = should return only matching results')
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0475000000')])
self.assertEqual(results, test_phone_records[0])
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '0032475110606')])
@@ -105,12 +104,7 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients):
'Search on phone_mobile_search: +32/0032 likeliness')
# test inverse ilike search
# Currently not supported, returning results for "ilike"
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'not ilike', '0475')])
# self.assertEqual(results, test_phone_records - test_phone_records[:5],
self.assertEqual(results, test_phone_records[:5],
'Seach on phone_mobile_search: not ilike not supported')
self.assertEqual(results, test_phone_records - test_phone_records[:5])
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'not ilike', '101')])
# self.assertEqual(results, test_phone_records - test_phone_records[1],
self.assertEqual(results, test_phone_records[1],
'Seach on phone_mobile_search: not ilike not supported')
self.assertEqual(results, test_phone_records - test_phone_records[1])