diff --git a/addons/phone_validation/__manifest__.py b/addons/phone_validation/__manifest__.py
index 2ebc99f3ead..9afac809ea8 100644
--- a/addons/phone_validation/__manifest__.py
+++ b/addons/phone_validation/__manifest__.py
@@ -3,6 +3,7 @@
{
'name': 'Phone Numbers Validation',
+ 'version': '2.0',
'summary': 'Validate and format phone numbers',
'sequence': '9999',
'category': 'Hidden',
@@ -11,12 +12,24 @@ Phone Numbers Validation
========================
This module adds the feature of validation and formatting phone numbers
-according to a destination country. It also handles national and international
-formatting.
+according to a destination country.
-This module applies this feature to Leads and Contacts.""",
+It also adds phone blacklist management through a specific model storing
+blacklisted phone numbers.
+
+It adds two mixins :
+
+ * phone.validation.mixin: parsing / formatting helpers on records, to be
+ used for example in number fields onchange;
+ * mail.thread.phone: handle sanitation and blacklist of records numbers;
+""",
'data': [
- 'views/res_company_view.xml'
+ 'security/ir.model.access.csv',
+ 'views/res_company_view.xml',
+ 'views/phone_blacklist_views.xml',
+ ],
+ 'depends': [
+ 'base',
+ 'mail',
],
- 'depends': ['base'],
}
diff --git a/addons/phone_validation/models/__init__.py b/addons/phone_validation/models/__init__.py
index 85ea0597483..dfe42e557e3 100644
--- a/addons/phone_validation/models/__init__.py
+++ b/addons/phone_validation/models/__init__.py
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import res_company
+from . import phone_blacklist
from . import phone_validation_mixin
+from . import mail_thread_phone
+from . import res_company
diff --git a/addons/phone_validation/models/mail_thread_phone.py b/addons/phone_validation/models/mail_thread_phone.py
new file mode 100644
index 00000000000..3e166985f8e
--- /dev/null
+++ b/addons/phone_validation/models/mail_thread_phone.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class PhoneMixin(models.AbstractModel):
+ """ Purpose of this mixin is to offer two services
+
+ * compute a sanitized phone number based on ´´_sms_get_number_fields´´.
+ It takes first sanitized value, trying each field returned by the
+ method (see ``MailThread._sms_get_number_fields()´´ for more details
+ about the usage of this method);
+ * compute blacklist state of records. It is based on phone.blacklist
+ model and give an easy-to-use field and API to manipulate blacklisted
+ records;
+
+ Main API methods
+
+ * ``_phone_set_blacklisted``: set recordset as blacklisted;
+ * ``_phone_reset_blacklisted``: reactivate recordset (even if not blacklisted
+ this method can be called safely);
+ """
+ _name = 'mail.thread.phone'
+ _description = 'Phone Blacklist Mixin'
+ _inherit = ['mail.thread', 'phone.validation.mixin']
+
+ phone_sanitized = fields.Char(
+ string='Sanitized Number', compute="_compute_phone_sanitized", compute_sudo=True, store=True,
+ help="Field used to store sanitized phone number. Helps speeding up searches and comparisons.")
+ phone_blacklisted = fields.Boolean(
+ string='Phone Blacklisted', compute="_compute_phone_blacklisted", compute_sudo=True, store=False,
+ search="_search_phone_blacklisted", groups="base.group_user",
+ help="If the email address is on the blacklist, the contact won't receive mass mailing anymore, from any list")
+
+ @api.depends(lambda self: self._phone_get_number_fields())
+ def _compute_phone_sanitized(self):
+ self._assert_phone_field()
+ 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)
+ if sanitized:
+ break
+ record.phone_sanitized = sanitized
+
+ @api.depends('phone_sanitized')
+ def _compute_phone_blacklisted(self):
+ # TODO : Should remove the sudo as compute_sudo defined on methods.
+ # But if user doesn't have access to mail.blacklist, doen't work without sudo().
+ blacklist = set(self.env['phone.blacklist'].sudo().search([
+ ('number', 'in', self.mapped('phone_sanitized'))]).mapped('number'))
+ for record in self:
+ record.phone_blacklisted = record.phone_sanitized in blacklist
+
+ @api.model
+ def _search_phone_blacklisted(self, operator, value):
+ # Assumes operator is '=' or '!=' and value is True or False
+ self._assert_phone_field()
+ if operator != '=':
+ if operator == '!=' and isinstance(value, bool):
+ value = not value
+ else:
+ raise NotImplementedError()
+
+ if value:
+ query = """
+ SELECT m.id
+ FROM phone_blacklist bl
+ JOIN %s m
+ ON m.phone_sanitized = bl.number AND bl.active
+ """
+ else:
+ query = """
+ SELECT m.id
+ FROM %s m
+ LEFT JOIN phone_blacklist bl
+ ON m.phone_sanitized = bl.number AND bl.active
+ WHERE bl.id IS NULL
+ """
+ self._cr.execute(query % self._table)
+ res = self._cr.fetchall()
+ if not res:
+ return [(0, '=', 1)]
+ return [('id', 'in', [r[0] for r in res])]
+
+ def _assert_phone_field(self):
+ if not hasattr(self, "_phone_get_number_fields"):
+ raise UserError(_('Invalid primary phone field on model %s') % self._name)
+ if not any(fname in self and self._fields[fname].type == 'char' for fname in self._phone_get_number_fields()):
+ raise UserError(_('Invalid primary phone field on model %s') % self._name)
+
+ def _phone_get_number_fields(self):
+ """ This method returns the fields to use to find the number to use to
+ send an SMS on a record. """
+ return []
+
+ def _phone_set_blacklisted(self):
+ return self.env['phone.blacklist'].sudo()._add([r.phone_sanitized for r in self])
+
+ def _phone_reset_blacklisted(self):
+ return self.env['phone.blacklist'].sudo()._remove([r.phone_sanitized for r in self])
diff --git a/addons/phone_validation/models/phone_blacklist.py b/addons/phone_validation/models/phone_blacklist.py
new file mode 100644
index 00000000000..2a6e90b6cb5
--- /dev/null
+++ b/addons/phone_validation/models/phone_blacklist.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+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__)
+
+
+class PhoneBlackList(models.Model):
+ """ Blacklist of phone numbers. Used to avoid sending unwanted messages to people. """
+ _name = 'phone.blacklist'
+ _inherit = ['mail.thread']
+ _description = 'Phone Blacklist'
+ _rec_name = 'number'
+
+ number = fields.Char(string='Phone Number', required=True, index=True, tracking=True, help='Number should be E164 formatted')
+ active = fields.Boolean(default=True, tracking=True)
+
+ _sql_constraints = [
+ ('unique_number', 'unique (number)', 'Number already exists')
+ ]
+
+ @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)
+ to_create = []
+ done = set()
+ for value in values:
+ number = value['number']
+ sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
+ if not sanitized:
+ raise UserError(_('Invalid number %s') % number)
+ if sanitized in done:
+ continue
+ done.add(sanitized)
+ to_create.append(dict(value, number=sanitized))
+
+ """ To avoid crash during import due to unique email, return the existing records if any """
+ sql = '''SELECT number, id FROM phone_blacklist WHERE number = ANY(%s)'''
+ numbers = [v['number'] for v in to_create]
+ self._cr.execute(sql, (numbers,))
+ bl_entries = dict(self._cr.fetchall())
+ to_create = [v for v in to_create if v['number'] not in bl_entries]
+
+ results = super(PhoneBlackList, self).create(to_create)
+ return self.env['phone.blacklist'].browse(bl_entries.values()) | results
+
+ def write(self, values):
+ if 'number' in values:
+ number = values['number']
+ sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
+ if not sanitized:
+ raise UserError(_('Invalid number %s') % number)
+ values['number'] = sanitized
+ return super(PhoneBlackList, self).write(values)
+
+ def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
+ """ Override _search in order to grep search on sanitized number field """
+ if args:
+ new_args = []
+ for arg in args:
+ 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']
+ if sanitized:
+ new_args.append([arg[0], arg[1], sanitized])
+ else:
+ new_args.append(arg)
+ else:
+ new_args.append(arg)
+ else:
+ new_args = args
+ return super(PhoneBlackList, self)._search(new_args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid)
+
+ def add(self, number):
+ sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
+ return self._add([sanitized])
+
+ def _add(self, numbers):
+ """ Add or re activate a phone blacklist entry.
+
+ :param numbers: list of sanitized numbers """
+ records = self.env["phone.blacklist"].with_context(active_test=False).search([('number', 'in', numbers)])
+ todo = [n for n in numbers if n not in records.mapped('number')]
+ if records:
+ records.write({'active': True})
+ if todo:
+ records += self.create([{'number': n} for n in todo])
+ return records
+
+ def remove(self, number):
+ sanitized = phone_validation.phone_sanitize_numbers_w_record([number], self.env.user)[number]['sanitized']
+ return self._remove([sanitized])
+
+ def _remove(self, numbers):
+ """ Add de-activated or de-activate a phone blacklist entry.
+
+ :param numbers: list of sanitized numbers """
+ records = self.env["phone.blacklist"].with_context(active_test=False).search([('number', 'in', numbers)])
+ todo = [n for n in numbers if n not in records.mapped('number')]
+ if records:
+ records.write({'active': False})
+ if todo:
+ records += self.create([{'number': n, 'active': False} for n in todo])
+ return records
diff --git a/addons/phone_validation/models/phone_validation_mixin.py b/addons/phone_validation/models/phone_validation_mixin.py
index f062586c7c3..0e7ab1c2aaa 100644
--- a/addons/phone_validation/models/phone_validation_mixin.py
+++ b/addons/phone_validation/models/phone_validation_mixin.py
@@ -9,6 +9,11 @@ class PhoneValidationMixin(models.AbstractModel):
_name = 'phone.validation.mixin'
_description = 'Phone Validation Mixin'
+ def _phone_get_country_field(self):
+ if 'country_id' in self:
+ return 'country_id'
+ return False
+
def _phone_get_country(self):
if 'country_id' in self and self.country_id:
return self.country_id
@@ -31,3 +36,17 @@ class PhoneValidationMixin(models.AbstractModel):
force_format='INTERNATIONAL' if always_international else 'NATIONAL',
raise_exception=False
)
+
+ 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']
diff --git a/addons/phone_validation/security/ir.model.access.csv b/addons/phone_validation/security/ir.model.access.csv
new file mode 100644
index 00000000000..53a27a4e6b2
--- /dev/null
+++ b/addons/phone_validation/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_phone_blacklist_all,access.phone.blacklist.all,model_phone_blacklist,,0,0,0,0
+access_phone_blacklist_system,access.phone.blacklist.system,model_phone_blacklist,base.group_system,1,1,1,1
diff --git a/addons/phone_validation/tools/phone_validation.py b/addons/phone_validation/tools/phone_validation.py
index e1df861ac8c..91ee9ba6b37 100644
--- a/addons/phone_validation/tools/phone_validation.py
+++ b/addons/phone_validation/tools/phone_validation.py
@@ -17,7 +17,7 @@ try:
try:
phone_nbr = phonenumbers.parse(number, region=country_code, keep_raw_input=True)
except phonenumbers.phonenumberutil.NumberParseException as e:
- raise UserError(_('Unable to parse %s:\n%s') % (number, e))
+ raise UserError(_('Unable to parse %s: %s') % (number, str(e)))
if not phonenumbers.is_possible_number(phone_nbr):
raise UserError(_('Impossible number %s: probably invalid number of digits') % number)
@@ -77,45 +77,41 @@ except ImportError:
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(
- number, country_code, country_phone_code,
+ stripped, country_code, country_phone_code,
force_format=force_format, raise_exception=True)
except Exception as e:
- result[number] = {'sanitized': False, 'code': 'invalid', 'msg': 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 hasattr(record, record_country_fname) and record[record_country_fname]:
+ 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)
-
-
-def phone_sanitize_numbers_string_w_record(numbers_str, record, country=False, record_country_fname='country_id', force_format='E164'):
- found_numbers = [number.strip() for number in numbers_str.split(',')]
- return phone_sanitize_numbers_w_record(found_numbers, record, country=country, record_country_fname=record_country_fname, force_format=force_format)
-
-
-def phone_get_sanitized_records_number(records, number_fname='mobile', country_fname='country_id', force_format='E164'):
- res = dict.fromkeys(records.ids, False)
- for record in records:
- number = record[number_fname]
- res[record.id] = phone_sanitize_numbers_w_record([number], records, record_country_fname=country_fname,force_format=force_format)[number]['sanitized']
- return res
-
-
-def phone_get_sanitized_record_number(record, number_fname='mobile', country_fname='country_id', force_format='E164'):
- return phone_get_sanitized_records_number(record, number_fname, country_fname, force_format=force_format)[record.id]
diff --git a/addons/phone_validation/views/phone_blacklist_views.xml b/addons/phone_validation/views/phone_blacklist_views.xml
new file mode 100644
index 00000000000..4d76576cefa
--- /dev/null
+++ b/addons/phone_validation/views/phone_blacklist_views.xml
@@ -0,0 +1,72 @@
+
+
+
+ phone.blacklist.view.tree
+ phone.blacklist
+
+
+
+
+
+
+
+
+
+ phone.blacklist.view.form
+ phone.blacklist
+
+
+
+
+
+
+ phone.blacklist.view.search
+ phone.blacklist
+
+
+
+
+
+
+
+
+
+ Phone Blacklist
+ phone.blacklist
+
+
+
+
+ Add a phone number in the blacklist
+
+ Blacklisted phone numbers means that the recipient won't receive mass SMS anymore.
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/sms/__manifest__.py b/addons/sms/__manifest__.py
index 9be1a884b13..a522fc65bd4 100644
--- a/addons/sms/__manifest__.py
+++ b/addons/sms/__manifest__.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'SMS gateway',
+ 'version': '2.0',
'category': 'Tools',
'summary': 'SMS Text Messaging',
'description': """
@@ -32,5 +33,5 @@ The service is provided by the In App Purchase Odoo platform.
'static/src/xml/thread.xml',
],
'installable': True,
- 'auto_install': True,
+ 'auto_install': False,
}
diff --git a/addons/sms/models/__init__.py b/addons/sms/models/__init__.py
index aab4ae27445..8ae76d2a518 100644
--- a/addons/sms/models/__init__.py
+++ b/addons/sms/models/__init__.py
@@ -6,6 +6,7 @@ from . import mail_followers
from . import mail_message
from . import mail_notification
from . import mail_thread
+from . import mail_thread_phone
from . import res_partner
from . import sms_api
from . import sms_sms
diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py
index 9deee25877f..5df73183266 100644
--- a/addons/sms/models/mail_thread.py
+++ b/addons/sms/models/mail_thread.py
@@ -78,7 +78,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_get_sanitized_record_number(record, number_fname=fname)
+ valid_number = phone_validation.phone_sanitize_numbers_w_record([record[fname]], record)[record[fname]]['sanitized']
if valid_number:
break
@@ -92,7 +92,7 @@ class MailThread(models.AbstractModel):
for partner in all_partners:
partner_number = partner.mobile or partner.phone
if partner_number:
- partner_number = phone_validation.phone_sanitize_numbers_string_w_record(partner_number, record)[partner_number]['sanitized']
+ partner_number = phone_validation.phone_sanitize_numbers_w_record([partner_number], record)[partner_number]['sanitized']
if partner_number:
break
@@ -233,7 +233,7 @@ 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_string_w_record(number, partner)[number]
+ 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,
diff --git a/addons/sms/models/mail_thread_phone.py b/addons/sms/models/mail_thread_phone.py
new file mode 100644
index 00000000000..b886a0f15c7
--- /dev/null
+++ b/addons/sms/models/mail_thread_phone.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+
+
+class PhoneMixin(models.AbstractModel):
+ _inherit = 'mail.thread.phone'
+
+ def _phone_get_number_fields(self):
+ """ Add fields coming from sms implementation. """
+ sms_fields = self._sms_get_number_fields()
+ res = super(PhoneMixin, self)._phone_get_number_fields()
+ for fname in (f for f in res if f not in sms_fields):
+ sms_fields.append(fname)
+ return sms_fields
diff --git a/addons/sms/models/res_partner.py b/addons/sms/models/res_partner.py
index 03431d0a68a..987791da643 100644
--- a/addons/sms/models/res_partner.py
+++ b/addons/sms/models/res_partner.py
@@ -5,7 +5,8 @@ from odoo import models
class ResPartner(models.Model):
- _inherit = 'res.partner'
+ _name = 'res.partner'
+ _inherit = ['res.partner', 'mail.thread.phone']
def _sms_get_default_partners(self):
""" Override of mail.thread method.
diff --git a/addons/sms/tests/common.py b/addons/sms/tests/common.py
index 7535eaa25c4..a0ef0a20436 100644
--- a/addons/sms/tests/common.py
+++ b/addons/sms/tests/common.py
@@ -74,7 +74,7 @@ class MockSMS(common.BaseCase):
""" Check canceled SMS. Search is done for a pair partner / number where
partner can be an empty recordset. """
if number is None and partner:
- number = phone_validation.phone_get_sanitized_record_number(partner)
+ number = partner.phone_get_sanitized_number()
sms = self.env['sms.sms'].sudo().search([
('partner_id', '=', partner.id), ('number', '=', number),
('state', '=', 'canceled')
@@ -88,7 +88,7 @@ class MockSMS(common.BaseCase):
""" Check failed SMS. Search is done for a pair partner / number where
partner can be an empty recordset. """
if number is None and partner:
- number = phone_validation.phone_get_sanitized_record_number(partner)
+ number = partner.phone_get_sanitized_number()
sms = self.env['sms.sms'].sudo().search([
('partner_id', '=', partner.id), ('number', '=', number),
('state', '=', 'error')
@@ -102,7 +102,7 @@ class MockSMS(common.BaseCase):
""" Check outgoing SMS. Search is done for a pair partner / number where
partner can be an empty recordset. """
if number is None and partner:
- number = phone_validation.phone_get_sanitized_record_number(partner)
+ number = partner.phone_get_sanitized_number()
sms = self.env['sms.sms'].sudo().search([
('partner_id', '=', partner.id), ('number', '=', number),
('state', '=', 'outgoing')
@@ -139,7 +139,7 @@ class MockSMS(common.BaseCase):
number = recipient_info.get('number')
state = recipient_info.get('state', 'sent')
if number is None and partner:
- number = phone_validation.phone_get_sanitized_record_number(partner)
+ number = partner.phone_get_sanitized_number()
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))
diff --git a/addons/sms/views/res_partner_views.xml b/addons/sms/views/res_partner_views.xml
index f7e1b5da19a..bf779c2cad5 100644
--- a/addons/sms/views/res_partner_views.xml
+++ b/addons/sms/views/res_partner_views.xml
@@ -8,6 +8,9 @@
10
+
+
+
diff --git a/addons/sms/views/sms_sms_views.xml b/addons/sms/views/sms_sms_views.xml
index a6da804286a..efea13966cc 100644
--- a/addons/sms/views/sms_sms_views.xml
+++ b/addons/sms/views/sms_sms_views.xml
@@ -59,8 +59,10 @@
tree,form
-
-
+
diff --git a/addons/sms/views/sms_template_views.xml b/addons/sms/views/sms_template_views.xml
index 0e81c7411dc..93de812f634 100644
--- a/addons/sms/views/sms_template_views.xml
+++ b/addons/sms/views/sms_template_views.xml
@@ -67,7 +67,11 @@
tree,form
-
+
diff --git a/addons/sms/wizard/sms_composer.py b/addons/sms/wizard/sms_composer.py
index e3a05d30f2e..9da5d94bd9a 100644
--- a/addons/sms/wizard/sms_composer.py
+++ b/addons/sms/wizard/sms_composer.py
@@ -114,7 +114,8 @@ class SendSMS(models.TransientModel):
def _compute_sanitized_numbers(self):
if self.numbers:
record = self._get_records() if self.res_model and self.res_id else self.env.user
- sanitize_res = phone_validation.phone_sanitize_numbers_string_w_record(self.numbers, record)
+ numbers = [number.strip() for number in self.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']]
if invalid_numbers:
diff --git a/addons/test_mail_full/models/test_mail_models.py b/addons/test_mail_full/models/test_mail_models.py
index d0d58e0467a..f00c72e71a2 100644
--- a/addons/test_mail_full/models/test_mail_models.py
+++ b/addons/test_mail_full/models/test_mail_models.py
@@ -26,6 +26,28 @@ class MailTestSMS(models.Model):
return ['phone_nbr', 'mobile_nbr']
+class MailTestSMSBL(models.Model):
+ """ A model inheriting from mail.thread with some fields used for SMS
+ gateway, like a partner, a specific mobile phone, ... """
+ _description = 'Chatter Model for SMS Gateway'
+ _name = 'mail.test.sms.bl'
+ _inherit = ['mail.thread.phone']
+ _order = 'name asc, id asc'
+
+ name = fields.Char()
+ subject = fields.Char()
+ email_from = fields.Char()
+ phone_nbr = fields.Char()
+ mobile_nbr = fields.Char()
+ customer_id = fields.Many2one('res.partner', 'Customer')
+
+ def _sms_get_default_partners(self):
+ return self.mapped('customer_id')
+
+ def _sms_get_number_fields(self):
+ return ['phone_nbr', 'mobile_nbr']
+
+
class MailTestSMSSoLike(models.Model):
""" A model like sale order having only a customer, not specific phone
or mobile fields. """
diff --git a/addons/test_mail_full/security/ir.model.access.csv b/addons/test_mail_full/security/ir.model.access.csv
index 186986eef83..408a47fdb96 100644
--- a/addons/test_mail_full/security/ir.model.access.csv
+++ b/addons/test_mail_full/security/ir.model.access.csv
@@ -1,5 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_test_sms_all,mail.test.sms.all,model_mail_test_sms,,0,0,0,0
access_mail_test_sms_user,mail.test.sms.user,model_mail_test_sms,base.group_user,1,1,1,1
+access_mail_test_sms_bl_all,mail.test.sms.bl.all,model_mail_test_sms_bl,,0,0,0,0
+access_mail_test_sms_bl_user,mail.test.sms.bl.user,model_mail_test_sms_bl,base.group_user,1,1,1,1
access_mail_test_sms_partner_all,mail.test.sms.partner.all,model_mail_test_sms_partner,,0,0,0,0
access_mail_test_sms_partner_user,mail.test.sms.partner.user,model_mail_test_sms_partner,base.group_user,1,1,1,1
diff --git a/addons/test_mail_full/tests/__init__.py b/addons/test_mail_full/tests/__init__.py
index b64df4304a9..36523fca998 100644
--- a/addons/test_mail_full/tests/__init__.py
+++ b/addons/test_mail_full/tests/__init__.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from . import common
+from . import test_phone_blacklist
from . import test_sms_composer
from . import test_sms_management
from . import test_sms_performance
diff --git a/addons/test_mail_full/tests/common.py b/addons/test_mail_full/tests/common.py
index 0c7b1b7c9f7..0bd34a96277 100644
--- a/addons/test_mail_full/tests/common.py
+++ b/addons/test_mail_full/tests/common.py
@@ -24,3 +24,41 @@ class BaseFunctionalTest(test_mail_common.BaseFunctionalTest):
# some numbers for mass testing
cls.mass_numbers = ['04561%s2%s3%s' % (x, x, x) for x in range(0, 10)]
cls.mass_numbers_san = [phone_validation.phone_format(number, 'BE', '32', force_format='E164') for number in cls.mass_numbers]
+
+ @classmethod
+ def _create_sms_template(cls, model, body=False):
+ return cls.env['sms.template'].create({
+ 'name': 'Test Template',
+ 'model_id': cls.env['ir.model']._get(model).id,
+ 'body': body if body else 'Dear ${object.display_name} this is an SMS.'
+ })
+
+ @classmethod
+ def _create_records_for_batch(cls, model, count):
+ records = cls.env['mail.test.sms']
+ partners = cls.env['res.partner']
+ country_id = cls.env.ref('base.be').id,
+ for x in range(count):
+ partners += cls.env['res.partner'].with_context(**cls._test_context).create({
+ 'name': 'Partner_%s' % (x),
+ 'email': '_test_partner_%s@example.com' % (x),
+ 'country_id': country_id,
+ 'mobile': '047500%02d%02d' % (x, x)
+ })
+ records += cls.env[model].with_context(**cls._test_context).create({
+ 'name': 'Test_%s' % (x),
+ 'customer_id': partners[x].id,
+ })
+ cls.records = cls._reset_mail_context(records)
+ cls.partners = partners
+
+
+class TestRecipients(test_mail_common.TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestRecipients, cls).setUpClass()
+ cls.partner_numbers = [
+ phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
+ for partner in (cls.partner_1 | cls.partner_2)
+ ]
diff --git a/addons/test_mail_full/tests/test_phone_blacklist.py b/addons/test_mail_full/tests/test_phone_blacklist.py
new file mode 100644
index 00000000000..9b5cb88d742
--- /dev/null
+++ b/addons/test_mail_full/tests/test_phone_blacklist.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.sms.tests import common as sms_common
+from odoo.addons.test_mail_full.tests import common as test_mail_full_common
+
+
+class TestPhoneBlacklist(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
+ """ TODO """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestPhoneBlacklist, cls).setUpClass()
+ cls._test_body = 'VOID CONTENT'
+
+ cls.test_record = cls.env['mail.test.sms.bl'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ 'mobile_nbr': cls.test_numbers[0],
+ 'phone_nbr': cls.test_numbers[1],
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+
+ def test_phone_blacklist_internals(self):
+ with self.sudo('employee'):
+ test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id)
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1])
+ self.assertFalse(test_record.phone_blacklisted)
+
+ bl_record = self.env['phone.blacklist'].sudo().create([{'number': self.test_numbers_san[1]}])
+ test_record.invalidate_cache()
+ self.assertTrue(test_record.phone_blacklisted)
+
+ self.env['phone.blacklist'].sudo().remove(self.test_numbers_san[1])
+ self.assertFalse(bl_record.active)
+ test_record.invalidate_cache()
+ self.assertFalse(test_record.phone_blacklisted)
+
+ self.env['phone.blacklist'].sudo().add(self.test_numbers_san[1])
+ self.assertTrue(bl_record.active)
+ test_record.invalidate_cache()
+ self.assertTrue(test_record.phone_blacklisted)
+
+ bl_record_2 = self.env['phone.blacklist'].sudo().create([{'number': self.test_numbers_san[1]}])
+ self.assertEqual(bl_record, bl_record_2)
+
+ rec = self.env['mail.test.sms.bl'].search([('phone_blacklisted', '=', True)])
+ self.assertEqual(rec, test_record)
+
+ bl_record.unlink()
+ rec = self.env['mail.test.sms.bl'].search([('phone_blacklisted', '=', True)])
+ self.assertEqual(rec, self.env['mail.test.sms.bl'])
+
+ def test_phone_sanitize_api(self):
+ with self.sudo('employee'):
+ test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id)
+ self.assertFalse(test_record.phone_blacklisted)
+
+ test_record._phone_set_blacklisted()
+ test_record.invalidate_cache()
+ self.assertTrue(test_record.phone_blacklisted)
+
+ test_record._phone_reset_blacklisted()
+ test_record.invalidate_cache()
+ self.assertFalse(test_record.phone_blacklisted)
+
+ def test_phone_sanitize_internals(self):
+ with self.sudo('employee'):
+ test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id)
+ self.assertEqual(test_record.phone_nbr, self.test_numbers[1])
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1])
+
+ test_record.write({'phone_nbr': 'incorrect'})
+ self.assertEqual(test_record.phone_nbr, 'incorrect')
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[0])
+
+ test_record.write({'mobile_nbr': 'incorrect'})
+ self.assertEqual(test_record.mobile_nbr, 'incorrect')
+ self.assertEqual(test_record.phone_sanitized, False)
+
+ test_record.write({'phone_nbr': self.test_numbers[1]})
+ self.assertEqual(test_record.phone_nbr, self.test_numbers[1])
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1])
diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py
index a50cd210e08..19a64e415ad 100644
--- a/addons/test_mail_full/tests/test_sms_composer.py
+++ b/addons/test_mail_full/tests/test_sms_composer.py
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.addons.phone_validation.tools import phone_validation
from odoo.addons.sms.tests import common as sms_common
-from odoo.addons.test_mail.tests import common as test_mail_common
from odoo.addons.test_mail_full.tests import common as test_mail_full_common
-class TestSMSComposerComment(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSMSComposerComment(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
""" TODO LIST
* add test for default_res_model / default_res_id and stuff like that;
@@ -21,11 +19,6 @@ class TestSMSComposerComment(test_mail_full_common.BaseFunctionalTest, sms_commo
super(TestSMSComposerComment, cls).setUpClass()
cls._test_body = 'VOID CONTENT'
- cls.partner_numbers = [
- phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
- for partner in (cls.partner_1 | cls.partner_2)
- ]
-
cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
'name': 'Test',
'customer_id': cls.partner_1.id,
@@ -159,7 +152,7 @@ class TestSMSComposerComment(test_mail_full_common.BaseFunctionalTest, sms_commo
# self.assertSMSSent((self.partner_1 | self.partner_2).mapped('mobile'), test_body)
-class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS):
""" TODO LIST
* add test for mass with log note
@@ -170,28 +163,8 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M
super(TestSMSComposerMass, cls).setUpClass()
cls._test_body = 'Zizisse an SMS.'
- records = cls.env['mail.test.sms']
- partners = cls.env['res.partner']
- country_id = cls.env.ref('base.be').id,
- for x in range(3):
- partners += cls.env['res.partner'].with_context(**cls._test_context).create({
- 'name': 'Partner_%s' % (x),
- 'email': '_test_partner_%s@example.com' % (x),
- 'country_id': country_id,
- 'mobile': '047500%s%s99' % (x, x)
- })
- records += cls.env['mail.test.sms'].with_context(**cls._test_context).create({
- 'name': 'Test_%s' % (x),
- 'customer_id': partners[x].id,
- })
- cls.records = cls._reset_mail_context(records)
- cls.partners = partners
-
- cls.sms_template = cls.env['sms.template'].create({
- 'name': 'Test Template',
- 'model_id': cls.env['ir.model']._get('mail.test.sms').id,
- 'body': 'Dear ${object.display_name} this is an SMS.',
- })
+ cls._create_records_for_batch('mail.test.sms', 3)
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
def test_composer_mass_active_domain(self):
with self.sudo('employee'):
@@ -291,35 +264,3 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M
self.assertSMSOutgoing(record.customer_id, None, 'Cher·e· %s ceci est un SMS.' % record.display_name)
else:
self.assertSMSOutgoing(record.customer_id, None, 'Dear %s this is an SMS.' % record.display_name)
-
- def test_message_schedule_sms(self):
- with self.sudo('employee'):
- with self.mockSMSGateway():
- self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body)
-
- for record in self.records:
- self.assertSMSOutgoing(record.customer_id, None, self._test_body)
-
- def test_message_schedule_sms_w_log(self):
- with self.sudo('employee'):
- with self.mockSMSGateway():
- self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body, mass_keep_log=True)
-
- for record in self.records:
- self.assertSMSNotification([{'partner': record.customer_id, 'state': 'ready'}], self._test_body)
-
- def test_message_schedule_sms_w_template(self):
- with self.sudo('employee'):
- with self.mockSMSGateway():
- self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template)
-
- for record in self.records:
- self.assertSMSOutgoing(record.customer_id, None, 'Dear %s this is an SMS.' % record.display_name)
-
- def test_message_schedule_sms_w_template_and_log(self):
- with self.sudo('employee'):
- with self.mockSMSGateway():
- self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template, mass_keep_log=True)
-
- for record in self.records:
- self.assertSMSNotification([{'partner': record.customer_id, 'state': 'ready'}], 'Dear %s this is an SMS.' % record.display_name)
diff --git a/addons/test_mail_full/tests/test_sms_management.py b/addons/test_mail_full/tests/test_sms_management.py
index 4463ce8b647..7d8d3a1904b 100644
--- a/addons/test_mail_full/tests/test_sms_management.py
+++ b/addons/test_mail_full/tests/test_sms_management.py
@@ -2,11 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.sms.tests import common as sms_common
-from odoo.addons.test_mail.tests import common as test_mail_common
from odoo.addons.test_mail_full.tests import common as test_mail_full_common
-class TestSMSWizards(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSMSWizards(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
@classmethod
def setUpClass(cls):
diff --git a/addons/test_mail_full/tests/test_sms_post.py b/addons/test_mail_full/tests/test_sms_post.py
index 23687402f96..2b9be0964ec 100644
--- a/addons/test_mail_full/tests/test_sms_post.py
+++ b/addons/test_mail_full/tests/test_sms_post.py
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.addons.phone_validation.tools import phone_validation
from odoo.addons.sms.tests import common as sms_common
-from odoo.addons.test_mail.tests import common as test_mail_common
from odoo.addons.test_mail_full.tests import common as test_mail_full_common
-class TestSMSPost(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSMSPost(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
""" TODO
* add tests for new mail.message and mail.thread fields;
@@ -18,11 +16,6 @@ class TestSMSPost(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS,
super(TestSMSPost, cls).setUpClass()
cls._test_body = 'VOID CONTENT'
- cls.partner_numbers = [
- phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
- for partner in (cls.partner_1 | cls.partner_2)
- ]
-
cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
'name': 'Test',
'customer_id': cls.partner_1.id,
@@ -227,7 +220,7 @@ class TestSMSPost(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS,
self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages)
-class TestSMSPostException(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSMSPostException(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
@classmethod
def setUpClass(cls):
@@ -304,7 +297,7 @@ class TestSMSPostException(test_mail_full_common.BaseFunctionalTest, sms_common.
], self._test_body, messages)
def test_message_sms_crash_credit_single(self):
- with self.sudo('employee'), self.mockSMSGateway(nbr_t_error={phone_validation.phone_get_sanitized_record_number(self.partner_2): 'credit'}):
+ with self.sudo('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): '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)
@@ -336,7 +329,7 @@ class TestSMSPostException(test_mail_full_common.BaseFunctionalTest, sms_common.
], self._test_body, messages)
def test_message_sms_crash_wrong_number_single(self):
- with self.sudo('employee'), self.mockSMSGateway(nbr_t_error={phone_validation.phone_get_sanitized_record_number(self.partner_2): 'wrong_number_format'}):
+ with self.sudo('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): '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)
@@ -345,3 +338,46 @@ class TestSMSPostException(test_mail_full_common.BaseFunctionalTest, sms_common.
{'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_number_format'},
{'partner': self.partner_3, 'state': 'sent'},
], self._test_body, messages)
+
+
+class TestSMSApi(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSApi, cls).setUpClass()
+ cls._test_body = 'Zizisse an SMS.'
+
+ cls._create_records_for_batch('mail.test.sms', 3)
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
+
+ def test_message_schedule_sms(self):
+ with self.sudo('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body)
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, self._test_body)
+
+ def test_message_schedule_sms_w_log(self):
+ with self.sudo('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body, mass_keep_log=True)
+
+ for record in self.records:
+ self.assertSMSNotification([{'partner': record.customer_id, 'state': 'ready'}], self._test_body)
+
+ def test_message_schedule_sms_w_template(self):
+ with self.sudo('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template)
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, 'Dear %s this is an SMS.' % record.display_name)
+
+ def test_message_schedule_sms_w_template_and_log(self):
+ with self.sudo('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template, mass_keep_log=True)
+
+ for record in self.records:
+ self.assertSMSNotification([{'partner': record.customer_id, 'state': 'ready'}], 'Dear %s this is an SMS.' % record.display_name)
diff --git a/addons/test_mail_full/tests/test_sms_server_actions.py b/addons/test_mail_full/tests/test_sms_server_actions.py
index 506513c524a..d8be1757375 100644
--- a/addons/test_mail_full/tests/test_sms_server_actions.py
+++ b/addons/test_mail_full/tests/test_sms_server_actions.py
@@ -2,15 +2,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.sms.tests import common as sms_common
-from odoo.addons.test_mail.tests import common as test_mail_common
from odoo.addons.test_mail_full.tests import common as test_mail_full_common
-class TestSmsTemplate(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.TestRecipients):
+class TestServerAction(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
@classmethod
def setUpClass(cls):
- super(TestSmsTemplate, cls).setUpClass()
+ super(TestServerAction, cls).setUpClass()
cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
'name': 'Test',
'customer_id': cls.partner_1.id,
@@ -21,16 +20,10 @@ class TestSmsTemplate(test_mail_full_common.BaseFunctionalTest, sms_common.MockS
'phone_nbr': cls.test_numbers[0],
})
- model_id = cls.env['ir.model']._get('mail.test.sms').id
- cls.sms_template = cls.env['sms.template'].create({
- 'name': 'Test Template',
- 'model_id': model_id,
- 'body': 'Dear ${object.display_name} this is an SMS.'
- })
-
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
cls.action = cls.env['ir.actions.server'].create({
'name': 'Test SMS Action',
- 'model_id': model_id,
+ 'model_id': cls.env['ir.model']._get('mail.test.sms').id,
'state': 'sms',
'sms_template_id': cls.sms_template.id,
})
diff --git a/addons/test_mail_full/tests/test_sms_sms.py b/addons/test_mail_full/tests/test_sms_sms.py
index 46149f559a8..426e3872901 100644
--- a/addons/test_mail_full/tests/test_sms_sms.py
+++ b/addons/test_mail_full/tests/test_sms_sms.py
@@ -7,11 +7,10 @@ from unittest.mock import DEFAULT
from odoo import exceptions
from odoo.addons.sms.models.sms_sms import SmsSms as SmsSms
from odoo.addons.sms.tests import common as sms_common
-from odoo.addons.test_mail.tests import common as test_mail_common
from odoo.addons.test_mail_full.tests import common as test_mail_full_common
-class TestSMSPost(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSMSPost(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS):
@classmethod
def setUpClass(cls):
diff --git a/addons/test_mail_full/tests/test_sms_template.py b/addons/test_mail_full/tests/test_sms_template.py
index dab46cd04c7..a1c754511ab 100644
--- a/addons/test_mail_full/tests/test_sms_template.py
+++ b/addons/test_mail_full/tests/test_sms_template.py
@@ -2,11 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.sms.tests import common as sms_common
-from odoo.addons.test_mail.tests import common as test_mail_common
from odoo.addons.test_mail_full.tests import common as test_mail_full_common
-class TestSmsTemplate(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_common.MockEmails, test_mail_common.TestRecipients):
+class TestSmsTemplate(test_mail_full_common.BaseFunctionalTest, sms_common.MockSMS, test_mail_full_common.TestRecipients):
@classmethod
def setUpClass(cls):
@@ -19,11 +18,7 @@ class TestSmsTemplate(test_mail_full_common.BaseFunctionalTest, sms_common.MockS
cls.body_en = 'Dear ${object.display_name} this is an SMS.'
cls.body_fr = u"Hello ${object.display_name} ceci est en français."
- cls.sms_template = cls.env['sms.template'].create({
- 'name': 'Test Template',
- 'model_id': cls.env['ir.model']._get('mail.test.sms').id,
- 'body': cls.body_en,
- })
+ cls.sms_template = cls._create_sms_template('mail.test.sms', body=cls.body_en)
def test_sms_template_render(self):
rendered_body = self.sms_template._render_template(self.sms_template.body, self.sms_template.model, self.test_record.ids)