[FIX] phone_validation: correctly use phone fields when searching on phone/mobile

Currently the search methods defined on ``phone_mobile_search`` field forces
the usage of ``phone`` and ``mobile`` fields on model. This currently works
on models using by default this search field (lead and partner). However this
breaks on any model not having those two fields and inheriting from the
'mail.thread.phone' mixin.

In this commit we use the result of ``_phone_get_number_fields`` to know which
fields to use in the SQL query.

Tests are added to ensure it works as intended.

Task-3012789

X-original-commit: 6b235868da6fed1b2a0eaa7a25dc2202c6fefaa0
Part-of: odoo/odoo#104111
This commit is contained in:
Thibault Delavallée
2022-10-26 00:55:03 +02:00
parent aeeb10ad37
commit 47f08bf9d3
3 changed files with 78 additions and 22 deletions
@@ -248,6 +248,12 @@ msgstr ""
msgid "Messages"
msgstr ""
#. module: phone_validation
#: code:addons/phone_validation/models/mail_thread_phone.py:0
#, python-format
msgid "Missing definition of phone fields."
msgstr ""
#. module: phone_validation
#: model:ir.model.constraint,message:phone_validation.constraint_phone_blacklist_unique_number
msgid "Number already exists"
@@ -6,6 +6,7 @@ import re
from odoo import api, fields, models, _
from odoo.addons.phone_validation.tools import phone_validation
from odoo.exceptions import AccessError, UserError
from odoo.osv import expression
class PhoneMixin(models.AbstractModel):
@@ -47,6 +48,13 @@ 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):
phone_fields = [
fname for fname in self._phone_get_number_fields()
if fname in self._fields and self._fields[fname].store
]
if not phone_fields:
raise UserError(_('Missing definition of phone fields.'))
value = value.strip()
if len(value) < 3:
raise UserError(_('Please enter at least 3 characters when searching a Phone/Mobile number.'))
@@ -55,39 +63,35 @@ class PhoneMixin(models.AbstractModel):
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
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
);
WHERE {where_str};
"""
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
))
self._cr.execute(
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
REGEXP_REPLACE(model.phone, %s, '', 'g') ILIKE %s OR
REGEXP_REPLACE(model.mobile, %s, '', 'g') ILIKE %s;
WHERE {where_str};
"""
term = '%' + re.sub(pattern, '', value) + '%'
self._cr.execute(query, (
pattern, term,
pattern, term
))
self._cr.execute(query, (pattern, term) * len(phone_fields))
res = self._cr.fetchall()
if not res:
return [(0, '=', 1)]
@@ -45,3 +45,49 @@ class TestSMSActionsCommon(TestSMSCommon, TestSMSRecipients):
self.test_phone_records.mapped('phone_nbr'),
[False] * 5 + ['+32475110505', '0032475110606', '0032475110707', False]
)
@users('employee')
def test_search_phone_mobile_search_equal(self):
""" Test searching by phone/mobile with direct search """
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])
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')])
self.assertEqual(results, test_phone_records[6])
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', '=', '+32475110606')])
self.assertEqual(results, test_phone_records[6])
@users('employee')
def test_search_phone_mobile_search_ilike(self):
""" Test searching by phone/mobile on various ilike combinations """
test_phone_records = self.test_phone_records.with_env(self.env)
# test ilike search
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '0475')])
self.assertEqual(results, test_phone_records[:5])
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '101')])
self.assertEqual(results, test_phone_records[1])
# test search using +32/0032
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '+32475')])
self.assertEqual(results, test_phone_records[5:8],
'Search on phone_mobile_search: +32/0032 likeliness')
results = self.env['mail.test.sms.bl'].search([('phone_mobile_search', 'ilike', '0032475')])
self.assertEqual(results, test_phone_records[5:8],
'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')
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')