summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/mass_mailing_sms/models/mailing_mailing.py32
-rw-r--r--addons/sale/models/sale.py8
-rw-r--r--addons/sms/models/mail_thread.py6
-rw-r--r--addons/test_mail_full/models/mailing_mailing.py3
-rw-r--r--addons/test_mail_full/models/test_mail_models.py18
-rw-r--r--addons/test_mail_full/security/ir.model.access.csv2
-rw-r--r--addons/test_mail_full/tests/test_mass_sms.py83
-rw-r--r--addons/test_mail_full/tests/test_sms_post.py18
8 files changed, 160 insertions, 10 deletions
diff --git a/addons/mass_mailing_sms/models/mailing_mailing.py b/addons/mass_mailing_sms/models/mailing_mailing.py
index a0b938dbb04..ea2e36d7cbe 100644
--- a/addons/mass_mailing_sms/models/mailing_mailing.py
+++ b/addons/mass_mailing_sms/models/mailing_mailing.py
@@ -180,31 +180,51 @@ class Mailing(models.Model):
self.ensure_one()
target = self.env[self.mailing_model_real]
+ partner_fields = []
if issubclass(type(target), self.pool['mail.thread.phone']):
phone_fields = ['phone_sanitized']
elif issubclass(type(target), self.pool['mail.thread']):
phone_fields = target._sms_get_number_fields()
+ partner_fields = target._sms_get_partner_fields()
else:
phone_fields = []
if 'mobile' in target._fields:
phone_fields.append('mobile')
if 'phone' in target._fields:
phone_fields.append('phone')
- if not phone_fields:
+ partner_field = next(
+ (fname for fname in partner_fields if target._fields[fname].type == 'many2one'),
+ False
+ )
+ if not phone_fields and not partner_field:
raise UserError(_("Unsupported %s for mass SMS", self.mailing_model_id.name))
query = """
SELECT %(select_query)s
FROM mailing_trace trace
JOIN %(target_table)s target ON (trace.res_id = target.id)
+ %(join_add_query)s
WHERE (%(where_query)s)
- AND trace.mass_mailing_id = %%(mailing_id)s
- AND trace.model = %%(target_model)s
+ AND trace.mass_mailing_id = %%(mailing_id)s
+ AND trace.model = %%(target_model)s
"""
+ if phone_fields:
+ # phone fields are checked on target mailed model
+ select_query = 'target.id, ' + ', '.join('target.%s' % fname for fname in phone_fields)
+ where_query = ' OR '.join('target.%s IS NOT NULL' % fname for fname in phone_fields)
+ join_add_query = ''
+ else:
+ # phone fields are checked on res.partner model
+ partner_phone_fields = ['mobile', 'phone']
+ select_query = 'target.id, ' + ', '.join('partner.%s' % fname for fname in partner_phone_fields)
+ where_query = ' OR '.join('partner.%s IS NOT NULL' % fname for fname in partner_phone_fields)
+ join_add_query = 'JOIN res_partner partner ON (target.%s = partner.id)' % partner_field
+
query = query % {
- 'select_query': 'target.id, ' + ', '.join('target.%s' % fname for fname in phone_fields),
- 'where_query': ' OR '.join('target.%s IS NOT NULL' % fname for fname in phone_fields),
- 'target_table': target._table
+ 'select_query': select_query,
+ 'where_query': where_query,
+ 'target_table': target._table,
+ 'join_add_query': join_add_query,
}
params = {'mailing_id': self.id, 'target_model': self.mailing_model_real}
self._cr.execute(query, params)
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 37f30458176..7c81f62bfe6 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -858,6 +858,14 @@ Reason(s) of this behavior could be:
self.filtered(lambda o: o.state == 'draft').with_context(tracking_disable=True).write({'state': 'sent'})
return super(SaleOrder, self.with_context(mail_post_autofollow=True)).message_post(**kwargs)
+ def _sms_get_number_fields(self):
+ """ No phone or mobile field is available on sale model. Instead SMS will
+ fallback on partner-based computation using ``_sms_get_partner_fields``. """
+ return []
+
+ def _sms_get_partner_fields(self):
+ return ['partner_id']
+
def _send_order_confirmation_mail(self):
if self.env.su:
# sending mail in sudo was meant for it being sent from superuser
diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py
index c3ff8168faa..45bd8643906 100644
--- a/addons/sms/models/mail_thread.py
+++ b/addons/sms/models/mail_thread.py
@@ -53,13 +53,15 @@ class MailThread(models.AbstractModel):
"""
partners = self.env['res.partner']
for fname in self._sms_get_partner_fields():
- partners |= self.mapped(fname)
+ partners = partners.union(*self.mapped(fname)) # ensure ordering
return partners
def _sms_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 ['mobile']
+ if 'mobile' in self:
+ return ['mobile']
+ return []
def _sms_get_recipients_info(self, force_field=False, partner_fallback=True):
"""" Get SMS recipient information on current record set. This method
diff --git a/addons/test_mail_full/models/mailing_mailing.py b/addons/test_mail_full/models/mailing_mailing.py
index a97c41e9047..8d8a49a1ec9 100644
--- a/addons/test_mail_full/models/mailing_mailing.py
+++ b/addons/test_mail_full/models/mailing_mailing.py
@@ -15,7 +15,8 @@ class Mailing(models.Model):
"""Returns a set of emails opted-out in target model"""
self.ensure_one()
if self.mailing_model_real in ('mail.test.sms.bl.optout',
- 'mail.test.sms.partner'):
+ 'mail.test.sms.partner',
+ 'mail.test.sms.partner.2many'):
res_ids = self._get_recipients()
opt_out_contacts = set(self.env[self.mailing_model_real].search([
('id', 'in', res_ids),
diff --git a/addons/test_mail_full/models/test_mail_models.py b/addons/test_mail_full/models/test_mail_models.py
index 9ee84a0b3a2..a3b5eb8dc4d 100644
--- a/addons/test_mail_full/models/test_mail_models.py
+++ b/addons/test_mail_full/models/test_mail_models.py
@@ -89,3 +89,21 @@ class MailTestSMSPartner(models.Model):
def _sms_get_number_fields(self):
return []
+
+
+class MailTestSMSPartner2Many(models.Model):
+ """ A model like sale order having only a customer, not specific phone
+ or mobile fields. """
+ _description = 'Chatter Model for SMS Gateway (M2M Partners only)'
+ _name = 'mail.test.sms.partner.2many'
+ _inherit = ['mail.thread']
+
+ name = fields.Char()
+ customer_ids = fields.Many2many('res.partner', string='Customers')
+ opt_out = fields.Boolean()
+
+ def _sms_get_partner_fields(self):
+ return ['customer_ids']
+
+ def _sms_get_number_fields(self):
+ return []
diff --git a/addons/test_mail_full/security/ir.model.access.csv b/addons/test_mail_full/security/ir.model.access.csv
index b65c78635ce..5a099a7d8b4 100644
--- a/addons/test_mail_full/security/ir.model.access.csv
+++ b/addons/test_mail_full/security/ir.model.access.csv
@@ -7,3 +7,5 @@ access_mail_test_sms_bl_optout_all,mail.test.sms.bl.optout.all,model_mail_test_s
access_mail_test_sms_bl_optout_user,mail.test.sms.bl.optout.user,model_mail_test_sms_bl_optout,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
+access_mail_test_sms_partner_2many_all,mail.test.sms.partner.2many.all,model_mail_test_sms_partner_2many,,0,0,0,0
+access_mail_test_sms_partner_2many_user,mail.test.sms.partner.2many.user,model_mail_test_sms_partner_2many,base.group_user,1,1,1,1
diff --git a/addons/test_mail_full/tests/test_mass_sms.py b/addons/test_mail_full/tests/test_mass_sms.py
index 9dc48232ab3..dbc91b95c77 100644
--- a/addons/test_mail_full/tests/test_mass_sms.py
+++ b/addons/test_mail_full/tests/test_mass_sms.py
@@ -5,6 +5,7 @@ from ast import literal_eval
from odoo.addons.phone_validation.tools import phone_validation
from odoo.addons.test_mail_full.tests.common import TestMailFullCommon
+from odoo import exceptions
from odoo.tests import tagged
from odoo.tests.common import users
from odoo.tools import mute_logger
@@ -262,7 +263,7 @@ class TestMassSMSInternals(TestMassSMSCommon):
mailing_test.action_send_sms()
-@tagged('mass_mailing')
+@tagged('mass_mailing', 'mass_mailing_sms')
class TestMassSMS(TestMassSMSCommon):
@users('user_marketing')
@@ -295,6 +296,86 @@ class TestMassSMS(TestMassSMSCommon):
@users('user_marketing')
@mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mass_sms_partner_only(self):
+ """ Check sending SMS marketing on models having only a partner_id fields
+ set is working. """
+ mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
+ mailing.write({
+ 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.partner').id,
+ })
+
+ records = self.env['mail.test.sms.partner'].create([
+ {'name': 'SMSTest on %s' % partner.name,
+ 'customer_id': partner.id,
+ } for partner in self.partners
+ ])
+
+ with self.mockSMSGateway():
+ mailing.action_send_sms()
+
+ self.assertEqual(len(mailing.mailing_trace_ids), 10)
+ self.assertSMSTraces(
+ [{'partner': record.customer_id,
+ 'number': record.customer_id.phone_sanitized,
+ 'state': 'sent',
+ 'content': 'Dear %s this is a mass SMS with two links' % record.display_name
+ } for record in records],
+ mailing, records,
+ sms_links_info=[[
+ ('http://www.odoo.com/smstest', True, {}),
+ ('http://www.odoo.com/smstest/%s' % record.id, True, {}),
+ # unsubscribe is not shortened and parsed at sending
+ ('unsubscribe', False, {}),
+ ] for record in records],
+ )
+
+ # add a new record, send -> sent list should not resend traces
+ new_record = self.env['mail.test.sms.partner'].create([
+ {'name': 'Duplicate SMS on %s' % self.partners[0].name,
+ 'customer_id': self.partners[0].id,
+ }
+ ])
+ with self.mockSMSGateway():
+ mailing.action_send_sms()
+
+ self.assertEqual(len(mailing.mailing_trace_ids), 11)
+ self.assertSMSTraces(
+ [{'partner': new_record.customer_id,
+ 'number': new_record.customer_id.phone_sanitized,
+ 'state': 'sent',
+ 'content': 'Dear %s this is a mass SMS with two links' % new_record.display_name
+ }],
+ mailing, new_record,
+ sms_links_info=[[
+ ('http://www.odoo.com/smstest', True, {}),
+ ('http://www.odoo.com/smstest/%s' % new_record.id, True, {}),
+ # unsubscribe is not shortened and parsed at sending
+ ('unsubscribe', False, {}),
+ ]],
+ )
+
+ @users('user_marketing')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mass_sms_partner_only_m2m(self):
+ """ Check sending SMS marketing on models having only a m2m to partners
+ is currently not suppored. """
+ mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
+ mailing.write({
+ 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.partner.2many').id,
+ })
+
+ records = self.env['mail.test.sms.partner.2many'].create([
+ {'name': 'SMSTest on %s' % partner.name,
+ 'customer_ids': [(4, partner.id)],
+ } for partner in self.partners
+ ])
+
+ with self.assertRaises(exceptions.UserError), self.mockSMSGateway():
+ mailing.action_send_sms()
+
+
+ @users('user_marketing')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
def test_mass_sms_w_opt_out(self):
mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
recipients = self._create_mailing_sms_test_records(model='mail.test.sms.bl.optout', count=5)
diff --git a/addons/test_mail_full/tests/test_sms_post.py b/addons/test_mail_full/tests/test_sms_post.py
index 6851974fd88..ad39f0c2a8a 100644
--- a/addons/test_mail_full/tests/test_sms_post.py
+++ b/addons/test_mail_full/tests/test_sms_post.py
@@ -108,6 +108,24 @@ class TestSMSPost(TestMailFullCommon, TestRecipients):
# should not crash but have a failed notification
self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': False, 'state': 'exception', 'failure_type': 'sms_number_missing'}], self._test_body, messages)
+ def test_message_sms_model_w_partner_m2m_only(self):
+ with self.with_user('employee'):
+ record = self.env['mail.test.sms.partner.2many'].create({'customer_ids': [(4, self.partner_1.id)]})
+
+ with self.mockSMSGateway():
+ messages = record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages)
+
+ # TDE: should take first found one according to partner ordering
+ with self.with_user('employee'):
+ record = self.env['mail.test.sms.partner.2many'].create({'customer_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]})
+
+ with self.mockSMSGateway():
+ messages = record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.partner_2}], self._test_body, messages)
+
def test_message_sms_on_field_w_partner(self):
with self.with_user('employee'), self.mockSMSGateway():
test_record = self.env['mail.test.sms'].browse(self.test_record.id)