[IMP] phone_validation: add a phone formatting tool on model

RATIONALE

Simplify field management for mail / phone / sms flows. Make it working out
of the box, easier to use and tweak.

SPECIFICATIONS

Add a '_phone_format' tool method on BaseModel. It allows to format a number
either directly, either from a field available on the model. It allows to
ease number formatting. It uses available helpers to find numbers using
'_phone_get_number_fields' and '_phone_get_country_field'. With default
generic behavior this allows to simplify most calls to phone number formatting.

Having it available at BaseModel level allows to remove some custom code,
calls to phone_validation API, ...

Task-3422449 (Mail, Phone: Move and improve field helpers)

Part-of: odoo/odoo#130468
This commit is contained in:
Thibault Delavallée
2023-08-02 18:50:17 +02:00
parent 626557147c
commit adfdf5ab33
20 changed files with 144 additions and 194 deletions
+4 -4
View File
@@ -633,12 +633,12 @@ class Lead(models.Model):
@api.onchange('phone', 'country_id', 'company_id')
def _onchange_phone_validation(self):
if self.phone:
self.phone = self.phone_get_sanitized_number(number_fname='phone', force_format='INTERNATIONAL') or self.phone
self.phone = self._phone_format(fname='phone', force_format='INTERNATIONAL') or self.phone
@api.onchange('mobile', 'country_id', 'company_id')
def _onchange_mobile_validation(self):
if self.mobile:
self.mobile = self.phone_get_sanitized_number(number_fname='mobile', force_format='INTERNATIONAL') or self.mobile
self.mobile = self._phone_format(fname='mobile', force_format='INTERNATIONAL') or self.mobile
def _prepare_values_from_partner(self, partner):
""" Get a dictionary with values coming from partner information to
@@ -706,8 +706,8 @@ class Lead(models.Model):
"""
self.ensure_one()
if self.partner_id and self.phone != self.partner_id.phone:
lead_phone_formatted = self.phone_get_sanitized_number(number_fname='phone') or self.phone or False
partner_phone_formatted = self.partner_id.phone_get_sanitized_number(number_fname='phone') or self.partner_id.phone or False
lead_phone_formatted = self._phone_format(fname='phone') or self.phone or False
partner_phone_formatted = self.partner_id._phone_format(fname='phone') or self.partner_id.phone or False
return lead_phone_formatted != partner_phone_formatted
return False
+1 -1
View File
@@ -17,7 +17,7 @@ Key Features
* Manage your Events and Registrations
* Use emails to automatically confirm and send acknowledgments for any event registration
""",
'depends': ['base_setup', 'mail', 'portal', 'utm'],
'depends': ['base_setup', 'mail', 'phone_validation', 'portal', 'utm'],
'data': [
'security/event_security.xml',
'security/ir.model.access.csv',
+3 -26
View File
@@ -7,15 +7,6 @@ from odoo import _, api, fields, models, SUPERUSER_ID
from odoo.tools import format_date
from odoo.exceptions import AccessError, ValidationError
# phone_validation is not officially in the depends of event, but we would like
# to have the formatting available in event, not in event_sms -> do a conditional
# import just to be sure
try:
from odoo.addons.phone_validation.tools.phone_validation import phone_format
except ImportError:
def phone_format(number, country_code, country_phone_code, force_format='INTERNATIONAL', raise_exception=True):
return number
class EventRegistration(models.Model):
_name = 'event.registration'
@@ -134,13 +125,13 @@ class EventRegistration(models.Model):
def _onchange_phone_validation(self):
if self.phone:
country = self.partner_id.country_id or self.event_id.country_id or self.env.company.country_id
self.phone = self._phone_format(self.phone, country)
self.phone = self._phone_format(fname='phone', country=country) or self.phone
@api.onchange('mobile', 'event_id', 'partner_id')
def _onchange_mobile_validation(self):
if self.mobile:
country = self.partner_id.country_id or self.event_id.country_id or self.env.company.country_id
self.mobile = self._phone_format(self.mobile, country)
self.mobile = self._phone_format(fname='mobile', country=country)
# ------------------------------------------------------------
# CRUD
@@ -165,7 +156,7 @@ class EventRegistration(models.Model):
for fname in {'mobile', 'phone'}:
if values.get(fname):
values[fname] = self._phone_format(values[fname], related_country)
values[fname] = self._phone_format(number=values[fname], country=related_country) or values[fname]
registrations = super(EventRegistration, self).create(vals_list)
@@ -221,20 +212,6 @@ class EventRegistration(models.Model):
""" Checks that all registrations are for `auto-confirm` events. """
return all(event.auto_confirm for event in self.event_id)
def _phone_format(self, number, country):
""" Call phone_validation formatting tool function. Returns original
number in case formatting cannot be done (no country, wrong info, ...) """
if not number or not country:
return number
new_number = phone_format(
number,
country.code,
country.phone_code,
force_format='E164',
raise_exception=False,
)
return new_number if new_number else number
# ------------------------------------------------------------
# ACTIONS / BUSINESS
# ------------------------------------------------------------
@@ -208,14 +208,8 @@ class EventRegistration(models.Model):
# compare phone, taking into account formatting
if valid_partner and self.phone and valid_partner.phone:
phone_formatted = phone_validation.phone_format(
self.phone,
valid_partner.country_id.code or None,
valid_partner.country_id.phone_code or None,
force_format='E164',
raise_exception=False
)
partner_phone_formatted = valid_partner._phone_format(valid_partner.phone)
phone_formatted = self._phone_format(fname='phone', country=valid_partner.country_id)
partner_phone_formatted = valid_partner._phone_format(fname='phone')
if phone_formatted and partner_phone_formatted and phone_formatted != partner_phone_formatted:
valid_partner = self.env['res.partner']
if (not phone_formatted or not partner_phone_formatted) and self.phone != valid_partner.phone:
+3 -3
View File
@@ -168,16 +168,16 @@ class TestEventSale(TestEventSaleCommon):
)
self.assertEqual(
set(ticket1_new_reg.mapped('phone')),
set(['+32456111111', self.event_customer._phone_format(self.event_customer.phone)])
set(['+32456111111', self.event_customer._phone_format(fname='phone')])
)
self.assertEqual(
set(ticket1_new_reg.mapped('mobile')),
set(['+32456222222', self.event_customer._phone_format(self.event_customer.mobile)])
set(['+32456222222', self.event_customer._phone_format(fname='mobile')])
)
for field in ['name', 'email']:
self.assertEqual(ticket2_new_reg[field], self.event_customer[field])
for field in ['phone', 'mobile']:
self.assertEqual(ticket2_new_reg[field], self.event_customer._phone_format(self.event_customer[field]))
self.assertEqual(ticket2_new_reg[field], self.event_customer._phone_format(fname=field))
# ADDING MANUAL LINES ON SO
# ------------------------------------------------------------
+10 -6
View File
@@ -43,17 +43,21 @@ class MailingSMSController(http.Controller):
return request.redirect('/web')
# parse and validate number
sms_number = post.get('sms_number', '').strip(' ')
sanitize_res = phone_validation.phone_sanitize_numbers([sms_number], request.geoip.country_code, None)[sms_number]
tocheck_number = sanitize_res['sanitized'] or sms_number
trace = check_res['trace'].filtered(lambda r: r.sms_number == tocheck_number)[:1]
mailing_list_ids = trace.mass_mailing_id.contact_list_ids
sanitized = phone_validation.phone_format(
sms_number,
request.geoip.country_code,
force_format='E164',
raise_exception=False,
)
tocheck_number = sanitized or sms_number
trace = check_res['trace'].filtered(lambda r: r.sms_number == tocheck_number)[:1] if tocheck_number else False
# compute opt-out / blacklist information
lists_optout = request.env['mailing.list'].sudo()
lists_optin = request.env['mailing.list'].sudo()
unsubscribe_error = False
if tocheck_number and trace:
mailing_list_ids = trace.mass_mailing_id.contact_list_ids
if mailing_list_ids:
subscriptions = request.env['mailing.contact.subscription'].sudo().search([
('list_id', 'in', mailing_list_ids.ids),
@@ -74,7 +78,7 @@ class MailingSMSController(http.Controller):
elif tocheck_number:
unsubscribe_error = _('Number %s not found', tocheck_number)
else:
unsubscribe_error = sanitize_res['msg']
unsubscribe_error = _('Invalid number %s', post.get('sms_number', ''))
return request.render('mass_mailing_sms.blacklist_number', {
'mailing_id': mailing_id,
@@ -21,9 +21,8 @@ class MassSMSTest(models.TransientModel):
self.ensure_one()
numbers = [number.strip() for number in self.numbers.splitlines()]
sanitize_res = phone_validation.phone_sanitize_numbers_w_record(numbers, self.env.user)
sanitized_numbers = [info['sanitized'] for info in sanitize_res.values() if info['sanitized']]
invalid_numbers = [number for number, info in sanitize_res.items() if info['code']]
sanitized_numbers = [self.env.user._phone_format(number=number) for number in numbers]
invalid_numbers = [number for sanitized, number in zip(sanitized_numbers, numbers) if not sanitized]
record = self.env[self.mailing_id.mailing_model_real].search([], limit=1)
body = self.mailing_id.body_plaintext
@@ -11,7 +11,6 @@ from odoo.exceptions import UserError, ValidationError
from odoo.addons.payment import utils as payment_utils
from odoo.addons.payment_razorpay.const import PAYMENT_STATUS_MAPPING
from odoo.addons.payment_razorpay.controllers.main import RazorpayController
from odoo.addons.phone_validation.tools.phone_validation import phone_sanitize_numbers
_logger = logging.getLogger(__name__)
@@ -52,16 +51,14 @@ class PaymentTransaction(models.Model):
return_url_params = {'reference': self.reference}
phone = self.partner_phone
error_message = _("The phone number is missing.")
if phone:
# sanitize partner phone
country_code = self.partner_country_id.code
country_phone_code = self.partner_country_id.phone_code
phone_info = phone_sanitize_numbers([phone], country_code, country_phone_code)
phone = phone_info[self.partner_phone]['sanitized']
error_message = phone_info[self.partner_phone]['msg']
if not phone:
raise ValidationError("Razorpay: " + error_message)
try:
phone = self._phone_format(number=phone, country=self.partner_country_id, raise_exception=True)
except Exception as err:
raise ValidationError("Razorpay: " + str(err)) from err
else:
raise ValidationError("Razorpay: " + _("The phone number is missing."))
rendering_values = {
'key_id': self.provider_id.razorpay_key_id,
@@ -4,7 +4,6 @@
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
@@ -127,7 +126,7 @@ class PhoneMixin(models.AbstractModel):
number_fields = self._phone_get_number_fields()
for record in self:
for fname in number_fields:
sanitized = record.phone_get_sanitized_number(number_fname=fname)
sanitized = record._phone_format(fname=fname)
if sanitized:
break
record.phone_sanitized = sanitized
@@ -148,9 +147,9 @@ class PhoneMixin(models.AbstractModel):
# may not be calculated as blacklisted even though it is if both field values exist in a model.
for number_field in number_fields:
if 'mobile' in number_field:
mobile_blacklisted = record.phone_sanitized_blacklisted and record.phone_get_sanitized_number(number_fname=number_field) == record.phone_sanitized
mobile_blacklisted = record.phone_sanitized_blacklisted and record._phone_format(fname=number_field) == record.phone_sanitized
else:
phone_blacklisted = record.phone_sanitized_blacklisted and record.phone_get_sanitized_number(number_fname=number_field) == record.phone_sanitized
phone_blacklisted = record.phone_sanitized_blacklisted and record._phone_format(fname=number_field) == record.phone_sanitized
record.mobile_blacklisted = mobile_blacklisted
record.phone_blacklisted = phone_blacklisted
@@ -196,20 +195,6 @@ class PhoneMixin(models.AbstractModel):
res = [self._phone_get_country_field()] if self._phone_get_country_field() else []
return res + self._phone_get_number_fields()
def phone_get_sanitized_numbers(self, number_fname='mobile', force_format='E164'):
res = dict.fromkeys(self.ids, False)
country_fname = self._phone_get_country_field()
for record in self:
number = record[number_fname]
res[record.id] = phone_validation.phone_sanitize_numbers_w_record([number], record, record_country_fname=country_fname, force_format=force_format)[number]['sanitized']
return res
def phone_get_sanitized_number(self, number_fname='mobile', force_format='E164'):
self.ensure_one()
country_fname = self._phone_get_country_field()
number = self[number_fname]
return phone_validation.phone_sanitize_numbers_w_record([number], self, record_country_fname=country_fname, force_format=force_format)[number]['sanitized']
def _phone_set_blacklisted(self):
return self.env['phone.blacklist'].sudo()._add([r.phone_sanitized for r in self])
+65 -1
View File
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
from odoo import api, exceptions, models
from odoo.addons.phone_validation.tools import phone_validation
class BaseModel(models.AbstractModel):
@@ -24,3 +25,66 @@ class BaseModel(models.AbstractModel):
if 'country_id' in self:
return 'country_id'
return False
def _phone_format(self, fname=False, number=False, country=False, force_format='E164', raise_exception=False):
""" Format and return number. This number can be found using a field
(in which case self should be a singleton recordet), or directly given
if the formatting itself is what matter. Field name can be found
automatically using '_phone_get_number_fields'
:param str fname: if number is not given, fname indicates the field to
use to find the number; otherwise use '_phone_get_number_fields';
:param str number: number to format (in which case fields-based computation
is skipped);
:param <res.country> country: country used for formatting number; otherwise
it is fetched based on record, using '_phone_get_country_field';
:param str force_format: stringified version of format globals; should be
one of 'E164', 'INTERNATIONAL', 'NATIONAL' or 'RFC3966';
:param bool raise_exception: raise if formatting is not possible (notably
wrong formatting, invalid country information, ...). Otherwise False
is returned;
:return str: formatted number. If formatting is not possible False is
returned.
"""
if not number:
# if no number is given, having a singletong recordset is mandatory to
# always have a number as input
self.ensure_one()
fnames = self._phone_get_number_fields() if not fname else [fname]
number = next((self[fname] for fname in fnames if fname in self and self[fname]), False)
if not number:
return False
# fetch country info only if self is a singleton recordset allowing to
# effectively try to find a country
if not country and self:
self.ensure_one()
country_fname = self._phone_get_country_field()
country = self[country_fname] if country_fname and country_fname in self else self.env['res.country']
if not country:
country = self.env.company.country_id
if not country:
return number
return self._phone_format_number(number, country=country, force_format=force_format, raise_exception=raise_exception)
def _phone_format_number(self, number, country, force_format='E164', raise_exception=False):
""" Format and return number according to the asked format. This is
mainly a small helper around 'phone_validation.phone_format'."""
if not number or not country:
return False
try:
number = phone_validation.phone_format(
number,
country.code,
country.phone_code,
force_format=force_format,
raise_exception=True, # do not get original number returned
)
except exceptions.UserError:
if raise_exception:
raise
number = False
return number
@@ -4,7 +4,6 @@
import logging
from odoo import api, fields, models, _
from odoo.addons.phone_validation.tools import phone_validation
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
@@ -26,21 +25,20 @@ class PhoneBlackList(models.Model):
@api.model_create_multi
def create(self, values):
# First of all, extract values to ensure emails are really unique (and don't modify values in place)
# First of all, extract values to ensure numbers are really unique (and don't modify values in place)
to_create = []
done = set()
for value in values:
number = value['number']
sanitized_values = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]
sanitized = sanitized_values['sanitized']
if not sanitized:
raise UserError(sanitized_values['msg'] + _(" Please correct the number and try again."))
if sanitized in done:
try:
sanitized_value = self.env.user._phone_format(number=value['number'], raise_exception=True)
except UserError as err:
raise UserError(str(err) + _(" Please correct the number and try again.")) from err
if sanitized_value in done:
continue
done.add(sanitized)
to_create.append(dict(value, number=sanitized))
done.add(sanitized_value)
to_create.append(dict(value, number=sanitized_value))
""" To avoid crash during import due to unique email, return the existing records if any """
# To avoid crash during import due to unique number, return the existing records if any
bl_entries = {}
if to_create:
sql = '''SELECT number, id FROM phone_blacklist WHERE number = ANY(%s)'''
@@ -54,11 +52,10 @@ class PhoneBlackList(models.Model):
def write(self, values):
if 'number' in values:
number = values['number']
sanitized_values = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]
sanitized = sanitized_values['sanitized']
if not sanitized:
raise UserError(sanitized_values['msg'] + _(" Please correct the number and try again."))
try:
sanitized = self.env.user._phone_format(number=values['number'], raise_exception=True)
except UserError as err:
raise UserError(str(err) + _(" Please correct the number and try again.")) from err
values['number'] = sanitized
return super(PhoneBlackList, self).write(values)
@@ -67,7 +64,7 @@ class PhoneBlackList(models.Model):
def sanitize_number(arg):
if isinstance(arg, (list, tuple)) and arg[0] == 'number' and isinstance(arg[2], str):
number = arg[2]
sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
sanitized = self.env.user._phone_format(number=number)
if sanitized:
return (arg[0], arg[1], sanitized)
return arg
@@ -76,7 +73,7 @@ class PhoneBlackList(models.Model):
return super()._search(domain, offset, limit, order, access_rights_uid)
def add(self, number, message=None):
sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
sanitized = self.env.user._phone_format(number=number)
return self._add([sanitized], message=message)
def _add(self, numbers, message=None):
@@ -101,7 +98,7 @@ class PhoneBlackList(models.Model):
return records
def remove(self, number, message=None):
sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
sanitized = self.env.user._phone_format(number=number)
return self._remove([sanitized], message=message)
def _remove(self, numbers, message=None):
+2 -22
View File
@@ -11,29 +11,9 @@ class Partner(models.Model):
@api.onchange('phone', 'country_id', 'company_id')
def _onchange_phone_validation(self):
if self.phone:
self.phone = self._phone_format(self.phone, force_format='INTERNATIONAL')
self.phone = self._phone_format(fname='phone', force_format='INTERNATIONAL') or self.phone
@api.onchange('mobile', 'country_id', 'company_id')
def _onchange_mobile_validation(self):
if self.mobile:
self.mobile = self._phone_format(self.mobile, force_format='INTERNATIONAL')
def _phone_format(self, number, country=None, company=None, force_format='E164'):
country = country or self.country_id or self.env.company.country_id
if not country or not number:
return number
return phone_validation.phone_format(
number,
country.code if country else None,
country.phone_code if country else None,
force_format=force_format,
raise_exception=False
)
def phone_get_sanitized_number(self, number_fname='mobile', force_format='E164'):
""" Stand alone version, allowing to use it on partner model without
having any dependency on sms module. To cleanup in master (15.3 +)."""
self.ensure_one()
country_fname = 'country_id'
number = self[number_fname]
return phone_validation.phone_sanitize_numbers_w_record([number], self, record_country_fname=country_fname, force_format=force_format)[number]['sanitized']
self.mobile = self._phone_format(fname='mobile', force_format='INTERNATIONAL') or self.mobile
+4 -8
View File
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, models
from odoo.addons.phone_validation.tools import phone_validation
class Users(models.Model):
@@ -13,13 +12,10 @@ class Users(models.Model):
numbers_to_blacklist = {} # numbers to blacklist and the related user
if post.get('request_blacklist'):
for user in self:
sanitized = phone_validation.phone_sanitize_numbers_w_record([user.phone, user.mobile], user)
user_phone = sanitized[user.phone]['sanitized']
user_mobile = sanitized[user.mobile]['sanitized']
if user_phone:
numbers_to_blacklist[user_phone] = user
if user_mobile:
numbers_to_blacklist[user_mobile] = user
for fname in self._phone_get_number_fields():
number = user._phone_format(fname=fname)
if number:
numbers_to_blacklist[number] = user
super(Users, self)._deactivate_portal_user(**post)
@@ -17,7 +17,9 @@ try:
try:
phone_nbr = phonenumbers.parse(number, region=country_code or None, keep_raw_input=True)
except phonenumbers.phonenumberutil.NumberParseException as e:
raise UserError(_('Unable to parse %(phone)s: %(error)s', phone=number, error=str(e)))
raise UserError(
_('Unable to parse %(phone)s: %(error)s', phone=number, error=str(e))
) from e
if not phonenumbers.is_possible_number(phone_nbr):
raise UserError(_('Impossible number %s: probably invalid number of digits.', number))
@@ -44,11 +46,10 @@ try:
"""
try:
phone_nbr = phone_parse(number, country_code)
except (phonenumbers.phonenumberutil.NumberParseException, UserError) as e:
except UserError:
if raise_exception:
raise
else:
return number
return number
if force_format == 'E164':
phone_fmt = phonenumbers.PhoneNumberFormat.E164
elif force_format == 'RFC3966':
@@ -73,44 +74,3 @@ except ImportError:
)
_phonenumbers_lib_warning = True
return number
def phone_sanitize_numbers(numbers, country_code, country_phone_code, force_format='E164'):
""" Given a list of numbers, return parsezd and sanitized information
:return dict: {number: {
'sanitized': sanitized and formated number or False (if cannot format)
'code': 'empty' (number was a void string), 'invalid' (error) or False (sanitize ok)
'msg': error message when 'invalid'
}}
"""
if not isinstance(numbers, (list)):
raise NotImplementedError()
result = dict.fromkeys(numbers, False)
for number in numbers:
if not number:
result[number] = {'sanitized': False, 'code': 'empty', 'msg': False}
continue
try:
stripped = number.strip()
sanitized = phone_format(
stripped, country_code, country_phone_code,
force_format=force_format, raise_exception=True)
except Exception as e:
result[number] = {'sanitized': False, 'code': 'invalid', 'msg': str(e)}
else:
result[number] = {'sanitized': sanitized, 'code': False, 'msg': False}
return result
def phone_sanitize_numbers_w_record(numbers, record, country=False, record_country_fname='country_id', force_format='E164'):
if not isinstance(numbers, (list)):
raise NotImplementedError()
if not country:
if record and record_country_fname and hasattr(record, record_country_fname) and record[record_country_fname]:
country = record[record_country_fname]
elif record:
country = record.env.company.country_id
country_code = country.code if country else None
country_phone_code = country.phone_code if country else None
return phone_sanitize_numbers(numbers, country_code, country_phone_code, force_format=force_format)
+5 -8
View File
@@ -88,7 +88,7 @@ class MailThread(models.AbstractModel):
valid_number = False
for fname in [f for f in tocheck_fields if f in record]:
valid_number = phone_validation.phone_sanitize_numbers_w_record([record[fname]], record)[record[fname]]['sanitized']
valid_number = record._phone_format(fname=fname)
if valid_number:
break
@@ -104,7 +104,7 @@ class MailThread(models.AbstractModel):
partner = self.env['res.partner']
for partner in all_partners:
for fname in self.env['res.partner']._phone_get_number_fields():
valid_number = phone_validation.phone_sanitize_numbers_w_record([partner[fname]], record)[partner[fname]]['sanitized']
valid_number = partner._phone_format(fname=fname)
if valid_number:
break
@@ -271,20 +271,17 @@ class MailThread(models.AbstractModel):
if partner_ids:
for partner in self.env['res.partner'].sudo().browse(partner_ids):
number = sms_pid_to_number.get(partner.id) or partner.mobile or partner.phone
sanitize_res = phone_validation.phone_sanitize_numbers_w_record([number], partner)[number]
number = sanitize_res['sanitized'] or number
sms_create_vals.append(dict(
sms_base_vals,
partner_id=partner.id,
number=number
number=partner._phone_format(number=number) or number,
))
# notify from additional numbers
if sms_numbers:
sanitized = phone_validation.phone_sanitize_numbers_w_record(sms_numbers, self)
tocreate_numbers = [
value['sanitized'] or original
for original, value in sanitized.items()
self._phone_format(number=sms_number) or sms_number
for sms_number in sms_numbers
]
sms_create_vals += [dict(
sms_base_vals,
+3 -3
View File
@@ -93,7 +93,7 @@ class SMSCase(MockSMS):
def _find_sms_sent(self, partner, number):
if number is None and partner:
number = partner.phone_get_sanitized_number()
number = partner._phone_format()
sent_sms = next((sms for sms in self._sms if sms['number'] == number), None)
if not sent_sms:
raise AssertionError('sent sms not found for %s (number: %s)' % (partner, number))
@@ -101,7 +101,7 @@ class SMSCase(MockSMS):
def _find_sms_sms(self, partner, number, status):
if number is None and partner:
number = partner.phone_get_sanitized_number()
number = partner._phone_format()
domain = [('id', 'in', self._new_sms.ids),
('partner_id', '=', partner.id),
('number', '=', number)]
@@ -214,7 +214,7 @@ class SMSCase(MockSMS):
number = recipient_info.get('number')
state = recipient_info.get('state', 'sent')
if number is None and partner:
number = partner.phone_get_sanitized_number()
number = partner._phone_format()
notif = notifications.filtered(lambda n: n.res_partner_id == partner and n.sms_number == number and n.notification_status == state)
self.assertTrue(notif, 'SMS: not found notification for %s (number: %s, state: %s)' % (partner, number, state))
+3 -5
View File
@@ -136,8 +136,7 @@ class SendSMS(models.TransientModel):
value = composer.recipient_single_number_itf or composer.recipient_single_number
if value:
records = composer._get_records()
sanitized = phone_validation.phone_sanitize_numbers_w_record([value], records)[value]['sanitized']
composer.recipient_single_valid = bool(sanitized)
composer.recipient_single_valid = bool(records._phone_format(number=value)) if len(records) == 1 else False
else:
composer.recipient_single_valid = False
@@ -147,9 +146,8 @@ class SendSMS(models.TransientModel):
if composer.numbers:
record = composer._get_records() if composer.res_model and composer.res_id else self.env.user
numbers = [number.strip() for number in composer.numbers.split(',')]
sanitize_res = phone_validation.phone_sanitize_numbers_w_record(numbers, record)
sanitized_numbers = [info['sanitized'] for info in sanitize_res.values() if info['sanitized']]
invalid_numbers = [number for number, info in sanitize_res.items() if info['code']]
sanitized_numbers = [record._phone_format(number=number) for number in numbers]
invalid_numbers = [number for sanitized, number in zip(sanitized_numbers, numbers) if not sanitized]
if invalid_numbers:
raise UserError(_('Following numbers are not correctly encoded: %s', repr(invalid_numbers)))
composer.sanitized_numbers = ','.join(sanitized_numbers)
@@ -573,7 +573,7 @@ class TestSMSComposerMass(SMSCommon):
with self.mockSMSGateway():
messages = composer._action_send_sms()
number = self.partners[2].phone_get_sanitized_number()
number = self.partners[2]._phone_format()
self.assertSMSNotification(
[{'partner': test_record_2.customer_id, 'number': number}],
"Hello %s ceci est en français." % test_record_2.display_name, messages
+3 -3
View File
@@ -329,7 +329,7 @@ class TestSMSPostException(SMSCommon, TestSMSRecipients):
], self._test_body, messages)
def test_message_sms_crash_credit_single(self):
with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'credit'}):
with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2._phone_format(): 'credit'}):
test_record = self.env['mail.test.sms'].browse(self.test_record.id)
messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
@@ -361,7 +361,7 @@ class TestSMSPostException(SMSCommon, TestSMSRecipients):
], self._test_body, messages)
def test_message_sms_crash_unregistered_single(self):
with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'unregistered'}):
with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2._phone_format(): 'unregistered'}):
test_record = self.env['mail.test.sms'].browse(self.test_record.id)
messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
@@ -382,7 +382,7 @@ class TestSMSPostException(SMSCommon, TestSMSRecipients):
], self._test_body, messages)
def test_message_sms_crash_wrong_number_single(self):
with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'wrong_number_format'}):
with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2._phone_format(): 'wrong_number_format'}):
test_record = self.env['mail.test.sms'].browse(self.test_record.id)
messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
@@ -69,7 +69,9 @@ class WebsiteForm(form.WebsiteForm):
# or if both numbers (after formating) are the same. This way we get additional phone
# if possible, without modifying an existing one. (see inverse function on model crm.lead)
if values_phone and visitor_partner.phone:
if visitor_partner._phone_format(visitor_partner.phone) == values_phone:
if values_phone == visitor_partner.phone:
values['partner_id'] = visitor_partner.id
elif (visitor_partner._phone_format('phone') or visitor_partner.phone) == values_phone:
values['partner_id'] = visitor_partner.id
else:
values['partner_id'] = visitor_partner.id