[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:
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user