[IMP] sms: allow to resend and cancel SMS notifications
Purpose of this commit is to allow SMS notification management. It is based
on what has been done in mail for notification: resend and cancel.
In this commit we add wizards allowing users to resend and cancel failed
SMS notifications. Resend allow to change the contact number, as well as
resending only a subset of notifications.
Future commits will improve the UX itself.
Related to task 1922163
Linked to PR #33510
Co-Authored-By: Thibault Delavallee <tde@odoo.com>
Co-Authored-By: Pierre Rousseau <pro@odoo.com>
This commit is contained in:
co-authored by
Pierre Rousseau
parent
14ccc770bb
commit
45555bdd1f
@@ -13,7 +13,9 @@ The service is provided by the In App Purchase Odoo platform.
|
||||
'depends': ['base', 'iap', 'mail', 'phone_validation'],
|
||||
'data': [
|
||||
'data/ir_cron_data.xml',
|
||||
'wizard/sms_cancel_views.xml',
|
||||
'wizard/sms_composer_views.xml',
|
||||
'wizard/sms_resend_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'views/assets.xml',
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
|
||||
from odoo import api, exceptions, fields, models
|
||||
from odoo.tools import groupby
|
||||
|
||||
|
||||
class MailMessage(models.Model):
|
||||
@@ -11,3 +15,76 @@ class MailMessage(models.Model):
|
||||
_inherit = 'mail.message'
|
||||
|
||||
message_type = fields.Selection(selection_add=[('sms', 'SMS')])
|
||||
|
||||
@api.multi
|
||||
def _format_mail_failures(self):
|
||||
""" A shorter message to notify a SMS delivery failure update
|
||||
|
||||
TDE FIXME: should be cleaned
|
||||
"""
|
||||
res = super(MailMessage, self)._format_mail_failures()
|
||||
|
||||
# prepare notifications computation in batch
|
||||
all_notifications = self.env['mail.notification'].sudo().search([
|
||||
('mail_message_id', 'in', self.ids)
|
||||
])
|
||||
msgid_to_notif = defaultdict(lambda: self.env['mail.notification'].sudo())
|
||||
for notif in all_notifications:
|
||||
msgid_to_notif[notif.mail_message_id.id] += notif
|
||||
|
||||
for message in self:
|
||||
notifications = msgid_to_notif[message.id]
|
||||
if not any(notification.notification_type == 'sms' for notification in notifications):
|
||||
continue
|
||||
info = dict(message._get_mail_failure_dict(),
|
||||
failure_type='sms',
|
||||
notifications=dict((notif.res_partner_id.id, (notif.notification_status, notif.res_partner_id.name)) for notif in notifications if notif.notification_type == 'sms'),
|
||||
module_icon='/sms/static/img/sms_failure.png'
|
||||
)
|
||||
res.append(info)
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _notify_sms_update(self):
|
||||
messages = self.env['mail.message']
|
||||
for message in self:
|
||||
# Check if user has access to the record before displaying a notification about it.
|
||||
# In case the user switches from one company to another, it might happen that he doesn't
|
||||
# have access to the record related to the notification. In this case, we skip it.
|
||||
if message.model and message.res_id:
|
||||
record = self.env[message.model].browse(message.res_id)
|
||||
try:
|
||||
record.check_access_rights('read')
|
||||
record.check_access_rule('read')
|
||||
except exceptions.AccessError:
|
||||
continue
|
||||
else:
|
||||
messages |= message
|
||||
|
||||
""" Notify channels after update of SMS status """
|
||||
updates = [[
|
||||
(self._cr.dbname, 'res.partner', author.id),
|
||||
{'type': 'sms_update', 'elements': self.env['mail.message'].concat(*author_messages)._format_mail_failures()}
|
||||
] for author, author_messages in groupby(messages, itemgetter('author_id'))]
|
||||
self.env['bus.bus'].sendmany(updates)
|
||||
|
||||
@api.multi
|
||||
def message_format(self):
|
||||
""" Override in order to retrieves data about SMS (recipient name and
|
||||
SMS status)
|
||||
|
||||
TDE FIXME: clean the overall message_format thingy
|
||||
"""
|
||||
message_values = super(MailMessage, self).message_format()
|
||||
all_sms_notifications = self.env['mail.notification'].sudo().search([
|
||||
('mail_message_id', 'in', [r['id'] for r in message_values]),
|
||||
('notification_type', '=', 'sms')
|
||||
])
|
||||
msgid_to_notif = defaultdict(lambda: self.env['mail.notification'].sudo())
|
||||
for notif in all_sms_notifications:
|
||||
msgid_to_notif[notif.mail_message_id.id] += notif
|
||||
|
||||
for message in message_values:
|
||||
customer_sms_data = [(notif.id, notif.res_partner_id.display_name, notif.notification_status) for notif in msgid_to_notif.get(message['id'], [])]
|
||||
message['sms_ids'] = customer_sms_data
|
||||
return message_values
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
@@ -1,3 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import sms_cancel
|
||||
from . import sms_composer
|
||||
from . import sms_resend
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class SMSCancel(models.TransientModel):
|
||||
_name = 'sms.cancel'
|
||||
_description = 'Dismiss notification for resend by model'
|
||||
|
||||
model = fields.Char(string='Model', required=True)
|
||||
help_message = fields.Char(string='Help message', compute='_compute_help_message')
|
||||
|
||||
@api.multi
|
||||
@api.depends('model')
|
||||
def _compute_help_message(self):
|
||||
for wizard in self:
|
||||
wizard.help_message = _("Are you sure you want to discard %s SMS delivery failures. You won't be able to re-send these SMS later!") % (wizard._context.get('unread_counter'))
|
||||
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
# TDE CHECK: delete pending SMS
|
||||
author_id = self.env.user.partner_id.id
|
||||
for wizard in self:
|
||||
self._cr.execute("""
|
||||
SELECT notif.id, msg.id
|
||||
FROM mail_message_res_partner_needaction_rel notif
|
||||
JOIN mail_message msg
|
||||
ON notif.mail_message_id = msg.id
|
||||
WHERE notif.notification_type = 'sms' IS TRUE AND notif.notification_status IN ('bounce', 'exception')
|
||||
AND msg.model = %s
|
||||
AND msg.author_id = %s """, (wizard.model, author_id))
|
||||
res = self._cr.fetchall()
|
||||
notif_ids = [row[0] for row in res]
|
||||
message_ids = list(set([row[1] for row in res]))
|
||||
if notif_ids:
|
||||
self.env['mail.notification'].browse(notif_ids).sudo().write({'notification_status': 'canceled'})
|
||||
if message_ids:
|
||||
self.env['mail.message'].browse(message_ids)._notify_sms_update()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
<record id="sms_cancel" model="ir.ui.view">
|
||||
<field name="name">sms.cancel.form</field>
|
||||
<field name="model">sms.cancel</field>
|
||||
<field name="groups_id" eval="[(4,ref('base.group_user'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Cancel notification in failure">
|
||||
<field name="model" invisible='1'/>
|
||||
<field name="help_message"/>
|
||||
<p>If you want to re-send them, click Cancel now, then click on the notification and review them one by one by clicking on the red icon next to each message.</p>
|
||||
<footer>
|
||||
<button string="Discard delivery failures" name="cancel_resend_action" type="object" class="btn-primary" />
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sms_cancel_action" model="ir.actions.act_window">
|
||||
<field name="name">Discard SMS delivery failures</field>
|
||||
<field name="res_model">sms.cancel</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</data></odoo>
|
||||
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, exceptions, fields, models
|
||||
|
||||
|
||||
class SMSRecipient(models.TransientModel):
|
||||
_name = 'sms.resend.recipient'
|
||||
_description = 'Resend Notification'
|
||||
_rec_name = 'sms_resend_id'
|
||||
|
||||
sms_resend_id = fields.Many2one('sms.resend', required=True)
|
||||
notification_id = fields.Many2one('mail.notification', required=True, ondelete='cascade')
|
||||
resend = fields.Boolean(string="Resend", default=True)
|
||||
failure_type = fields.Selection([
|
||||
('sms_number_missing', 'Missing Number'),
|
||||
('sms_number_format', 'Wrong Number Format'),
|
||||
('sms_credit', 'Insufficient Credit'),
|
||||
('sms_server', 'Server Error')], related='notification_id.failure_type', related_sudo=True, readonly=True)
|
||||
partner_id = fields.Many2one('res.partner', 'Partner', related='notification_id.res_partner_id', readonly=True)
|
||||
partner_name = fields.Char('Recipient', readonly='True')
|
||||
sms_number = fields.Char('Number')
|
||||
|
||||
|
||||
class SMSResend(models.TransientModel):
|
||||
_name = 'sms.resend'
|
||||
_description = 'SMS Resend'
|
||||
_rec_name = 'mail_message_id'
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
result = super(SMSResend, self).default_get(fields)
|
||||
if result.get('mail_message_id'):
|
||||
mail_message_id = self.env['mail.message'].browse(result['mail_message_id'])
|
||||
result['recipient_ids'] = [(0, 0, {
|
||||
'notification_id': notif.id,
|
||||
'resend': True,
|
||||
'failure_type': notif.failure_type,
|
||||
'partner_name': notif.res_partner_id.display_name or mail_message_id.record_name,
|
||||
'sms_number': notif.sms_number,
|
||||
}) for notif in mail_message_id.notification_ids if notif.notification_type == 'sms' and notif.notification_status in ('exception', 'bounce')]
|
||||
return result
|
||||
|
||||
mail_message_id = fields.Many2one('mail.message', 'Message', readonly=True, required=True)
|
||||
recipient_ids = fields.One2many('sms.resend.recipient', 'sms_resend_id', string='Recipients')
|
||||
has_cancel = fields.Boolean(compute='_compute_has_cancel')
|
||||
has_insufficient_credit = fields.Boolean(compute='_compute_has_insufficient_credit')
|
||||
|
||||
@api.depends("recipient_ids.failure_type")
|
||||
def _compute_has_insufficient_credit(self):
|
||||
self.has_insufficient_credit = self.recipient_ids.filtered(lambda p: p.failure_type == 'sms_credit')
|
||||
|
||||
@api.depends("recipient_ids.resend")
|
||||
def _compute_has_cancel(self):
|
||||
self.has_cancel = self.recipient_ids.filtered(lambda p: not p.resend)
|
||||
|
||||
def _check_access(self):
|
||||
if not self.mail_message_id or not self.mail_message_id.model or not self.mail_message_id.res_id:
|
||||
raise exceptions.UserError(_('You do not have access to the message and/or related document.'))
|
||||
record = self.env[self.mail_message_id.model].browse(self.mail_message_id.res_id)
|
||||
record.check_access_rights('read')
|
||||
record.check_access_rule('read')
|
||||
|
||||
@api.multi
|
||||
def action_resend(self):
|
||||
self._check_access()
|
||||
|
||||
all_notifications = self.env['mail.notification'].sudo().search([
|
||||
('mail_message_id', '=', self.mail_message_id.id),
|
||||
('notification_type', '=', 'sms'),
|
||||
('notification_status', 'in', ('exception', 'bounce'))
|
||||
])
|
||||
sudo_self = self.sudo()
|
||||
to_cancel_ids = [r.notification_id.id for r in sudo_self.recipient_ids if not r.resend]
|
||||
to_resend_ids = [r.notification_id.id for r in sudo_self.recipient_ids if r.resend]
|
||||
|
||||
if to_cancel_ids:
|
||||
all_notifications.filtered(lambda n: n.id in to_cancel_ids).write({'notification_status': 'canceled'})
|
||||
|
||||
if to_resend_ids:
|
||||
record = self.env[self.mail_message_id.model].browse(self.mail_message_id.res_id)
|
||||
|
||||
sms_pid_to_number = dict((r.partner_id.id, r.sms_number) for r in self.recipient_ids if r.resend and r.partner_id)
|
||||
pids = list(sms_pid_to_number.keys())
|
||||
numbers = [r.sms_number for r in self.recipient_ids if r.resend and not r.partner_id]
|
||||
|
||||
rdata = []
|
||||
for pid, cid, active, pshare, ctype, notif, groups in self.env['mail.followers']._get_recipient_data(record, 'sms', False, pids=pids):
|
||||
if pid and notif == 'sms':
|
||||
rdata.append({'id': pid, 'share': pshare, 'active': active, 'notif': notif, 'groups': groups or [], 'type': 'customer' if pshare else 'user'})
|
||||
if rdata or numbers:
|
||||
record._notify_record_by_sms(
|
||||
self.mail_message_id, {'partners': rdata}, check_existing=True,
|
||||
sms_numbers=numbers, sms_pid_to_number=sms_pid_to_number,
|
||||
put_in_queue=False
|
||||
)
|
||||
|
||||
self.mail_message_id._notify_sms_update()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
self._check_access()
|
||||
|
||||
sudo_self = self.sudo()
|
||||
sudo_self.mapped('recipient_ids.notification_id').write({'notification_status': 'canceled'})
|
||||
self.mail_message_id._notify_sms_update()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
@api.multi
|
||||
def action_buy_credits(self):
|
||||
url = self.env['iap.account'].get_credits_url(service_name='sms')
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': url,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
<record id="mail_resend_message_view_form" model="ir.ui.view">
|
||||
<field name="name">sms.resend.form</field>
|
||||
<field name="model">sms.resend</field>
|
||||
<field name="groups_id" eval="[(4,ref('base.group_user'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Edit Partners">
|
||||
<field name="mail_message_id" invisible="1"/>
|
||||
<field name="has_cancel" invisible="1"/>
|
||||
<field name="has_insufficient_credit" invisible="1"/>
|
||||
<field name="recipient_ids">
|
||||
<tree string="Recipient" editable="top" create="0" delete="0">
|
||||
<field name="partner_name"/>
|
||||
<field name="sms_number"/>
|
||||
<field name="failure_type" string="Reason"/>
|
||||
<field name="resend" widget="boolean_toggle"/>
|
||||
</tree>
|
||||
</field>
|
||||
<div class="alert alert-warning" role="alert" attrs="{'invisible': [('has_cancel', '=', False)]}">
|
||||
<span class="fa fa-info-circle"/> Caution: It won't be possible to send this SMS again to the recipients you did not select.
|
||||
</div>
|
||||
<footer>
|
||||
<button string="Buy credits" name="action_buy_credits" type="object" class="btn-primary o_mail_send"
|
||||
attrs="{'invisible': [('has_insufficient_credit', '=', False)]}"/>
|
||||
<button string="Resend" name="action_resend" type="object" class="btn-primary o_mail_send"/>
|
||||
<button string="Ignore all" name="action_cancel" type="object" class="btn-secondary" />
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sms_resend_action" model="ir.actions.act_window">
|
||||
<field name="name">Sending Failures</field>
|
||||
<field name="res_model">sms.resend</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</data></odoo>
|
||||
@@ -209,7 +209,7 @@ class TestAdvMailPerformance(BaseMailPerformance):
|
||||
@warmup
|
||||
def test_message_assignation_inbox(self):
|
||||
record = self.env['mail.test.track'].create({'name': 'Test'})
|
||||
with self.assertQueryCount(__system__=31, emp=36): # test_mail only: 31 - 36
|
||||
with self.assertQueryCount(__system__=32, emp=37): # test_mail only: 32 - 37
|
||||
record.write({
|
||||
'user_id': self.user_test.id,
|
||||
})
|
||||
@@ -265,7 +265,7 @@ class TestAdvMailPerformance(BaseMailPerformance):
|
||||
def test_message_post_one_inbox_notification(self):
|
||||
record = self.env['mail.test.simple'].create({'name': 'Test'})
|
||||
|
||||
with self.assertQueryCount(__system__=28, emp=33): # com runbot 28 - 33 // test_mail only: 28 - 33
|
||||
with self.assertQueryCount(__system__=29, emp=34): # com runbot 29 - 34 // test_mail only: 29 - 34
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances with an inbox ping</p>',
|
||||
partner_ids=self.user_test.partner_id.ids,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from . import common
|
||||
from . import test_sms_composer
|
||||
from . import test_sms_management
|
||||
from . import test_sms_performance
|
||||
from . import test_sms_post
|
||||
from . import test_sms_sms
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
# -*- 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.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):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestSMSWizards, cls).setUpClass()
|
||||
cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
|
||||
'name': 'Test',
|
||||
'customer_id': cls.partner_1.id,
|
||||
})
|
||||
cls.test_record = cls._reset_mail_context(cls.test_record)
|
||||
cls.msg = cls.test_record.message_post(body='TEST BODY', author_id=cls.partner_employee.id)
|
||||
cls.notif_p1 = cls.env['mail.notification'].create({
|
||||
'mail_message_id': cls.msg.id,
|
||||
'res_partner_id': cls.partner_1.id,
|
||||
'sms_number': cls.partner_1.mobile,
|
||||
'notification_type': 'sms',
|
||||
'notification_status': 'exception',
|
||||
'failure_type': 'sms_number_format',
|
||||
})
|
||||
cls.notif_p2 = cls.env['mail.notification'].create({
|
||||
'mail_message_id': cls.msg.id,
|
||||
'res_partner_id': cls.partner_2.id,
|
||||
'sms_number': cls.partner_2.mobile,
|
||||
'notification_type': 'sms',
|
||||
'notification_status': 'exception',
|
||||
'failure_type': 'sms_credit',
|
||||
})
|
||||
|
||||
def test_sms_resend(self):
|
||||
self._clear_bus()
|
||||
|
||||
with self.sudo('employee'):
|
||||
wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
|
||||
wizard.write({'recipient_ids': [(1, r.id, {'resend': True}) for r in wizard.recipient_ids]})
|
||||
with self.mockSMSGateway():
|
||||
wizard.action_resend()
|
||||
|
||||
self.assertSMSNotification([
|
||||
{'partner': self.partner_1, 'state': 'sent'},
|
||||
{'partner': self.partner_2, 'state': 'sent'}
|
||||
], 'TEST BODY', self.msg, check_sms=True)
|
||||
self.assertBusNotification(
|
||||
[(self.cr.dbname, 'res.partner', self.partner_employee.id)],
|
||||
[{'type': 'sms_update', 'elements': [{
|
||||
'message_id': self.msg.id,
|
||||
'failure_type': 'sms',
|
||||
'notifications': {'%s' % self.partner_1.id: ['sent', self.partner_1.name], '%s' % self.partner_2.id: ['sent', self.partner_2.name]}
|
||||
}]}]
|
||||
)
|
||||
|
||||
def test_sms_resend_update_number(self):
|
||||
self._clear_bus()
|
||||
|
||||
with self.sudo('employee'):
|
||||
wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
|
||||
wizard.write({'recipient_ids': [(1, r.id, {'resend': True, 'sms_number': self.random_numbers[idx]}) for idx, r in enumerate(wizard.recipient_ids)]})
|
||||
with self.mockSMSGateway():
|
||||
wizard.action_resend()
|
||||
|
||||
self.assertSMSNotification([
|
||||
{'partner': self.partner_1, 'state': 'sent', 'number': self.random_numbers_san[0]},
|
||||
{'partner': self.partner_2, 'state': 'sent', 'number': self.random_numbers_san[1]}
|
||||
], 'TEST BODY', self.msg, check_sms=True)
|
||||
self.assertBusNotification(
|
||||
[(self.cr.dbname, 'res.partner', self.partner_employee.id)],
|
||||
[{'type': 'sms_update', 'elements': [{
|
||||
'message_id': self.msg.id,
|
||||
'failure_type': 'sms',
|
||||
'notifications': {'%s' % self.partner_1.id: ['sent', self.partner_1.name], '%s' % self.partner_2.id: ['sent', self.partner_2.name]}
|
||||
}]}]
|
||||
)
|
||||
|
||||
def test_sms_resend_cancel(self):
|
||||
self._clear_bus()
|
||||
|
||||
with self.sudo('employee'):
|
||||
wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
|
||||
with self.mockSMSGateway():
|
||||
wizard.action_cancel()
|
||||
|
||||
self.assertSMSNotification([
|
||||
{'partner': self.partner_1, 'state': 'canceled', 'number': self.notif_p1.sms_number, 'failure_type': 'sms_number_format'},
|
||||
{'partner': self.partner_2, 'state': 'canceled', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}
|
||||
], 'TEST BODY', self.msg, check_sms=False)
|
||||
self.assertBusNotification(
|
||||
[(self.cr.dbname, 'res.partner', self.partner_employee.id)],
|
||||
[{'type': 'sms_update', 'elements': [{
|
||||
'message_id': self.msg.id,
|
||||
'failure_type': 'sms',
|
||||
'notifications': {'%s' % self.partner_1.id: ['canceled', self.partner_1.name], '%s' % self.partner_2.id: ['canceled', self.partner_2.name]}
|
||||
}]}]
|
||||
)
|
||||
|
||||
def test_sms_resend_internals(self):
|
||||
self._clear_bus()
|
||||
self.assertSMSNotification([
|
||||
{'partner': self.partner_1, 'state': 'exception', 'number': self.notif_p1.sms_number, 'failure_type': 'sms_number_format'},
|
||||
{'partner': self.partner_2, 'state': 'exception', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}
|
||||
], 'TEST BODY', self.msg, check_sms=False)
|
||||
|
||||
with self.sudo('employee'):
|
||||
wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
|
||||
self.assertTrue(wizard.has_insufficient_credit)
|
||||
self.assertEqual(set(wizard.mapped('recipient_ids.partner_name')), set((self.partner_1 | self.partner_2).mapped('display_name')))
|
||||
wizard.write({'recipient_ids': [(1, r.id, {'resend': True}) for r in wizard.recipient_ids]})
|
||||
with self.mockSMSGateway():
|
||||
wizard.action_resend()
|
||||
|
||||
def test_sms_resend_w_cancel(self):
|
||||
self._clear_bus()
|
||||
|
||||
with self.sudo('employee'):
|
||||
wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
|
||||
wizard.write({'recipient_ids': [(1, r.id, {'resend': True if r.partner_id == self.partner_1 else False}) for r in wizard.recipient_ids]})
|
||||
with self.mockSMSGateway():
|
||||
wizard.action_resend()
|
||||
|
||||
self.assertSMSNotification([{'partner': self.partner_1, 'state': 'sent'}], 'TEST BODY', self.msg, check_sms=True)
|
||||
self.assertSMSNotification([{'partner': self.partner_2, 'state': 'canceled', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}], 'TEST BODY', self.msg, check_sms=False)
|
||||
self.assertBusNotification(
|
||||
[(self.cr.dbname, 'res.partner', self.partner_employee.id)],
|
||||
[{'type': 'sms_update', 'elements': [{
|
||||
'message_id': self.msg.id,
|
||||
'failure_type': 'sms',
|
||||
'notifications': {'%s' % self.partner_1.id: ['sent', self.partner_1.name], '%s' % self.partner_2.id: ['canceled', self.partner_2.name]}
|
||||
}]}]
|
||||
)
|
||||
|
||||
def test_sms_cancel(self):
|
||||
self._clear_bus()
|
||||
|
||||
with self.mockSMSGateway(), self.sudo('employee'):
|
||||
wizard = self.env['sms.cancel'].with_context(default_model=self.msg.model).create({})
|
||||
wizard.action_cancel()
|
||||
|
||||
self.assertEqual((self.notif_p1 | self.notif_p2).mapped('notification_status'), ['canceled', 'canceled'])
|
||||
|
||||
self.assertBusNotification(
|
||||
[(self.cr.dbname, 'res.partner', self.partner_employee.id)],
|
||||
[{'type': 'sms_update', 'elements': [{
|
||||
'message_id': self.msg.id,
|
||||
'failure_type': 'sms',
|
||||
'notifications': {'%s' % self.partner_1.id: ['canceled', self.partner_1.name], '%s' % self.partner_2.id: ['canceled', self.partner_2.name]}
|
||||
}]}]
|
||||
)
|
||||
Reference in New Issue
Block a user