[FIX] (mass_mailing_)sms: allow to directly send SMS in mass mode

PURPOSE

Sending SMS is sometimes required as an immediate marketing tool.
Using delayed crons is not always the best user choice. Implement a
Send Now mechanism in batch SMS.

SPECIFICATIONS

Allow to send directly SMS when doing SMS marketing

SMS Application
  * in sms composer, in mass mode: rename Send SMS to Put in queue and
    add a Send Now button by-passing the queue;
  * set Put in queue as primary, Send Now as secondary;

SMS Marketing Application
  * in mailing view for SMS: rename Send SMS to Put in queue and
    add a Send Now button by-passing the queue;
  * set Put in queue as primary, Send Now as secondary;

Tests of SMS marketing with 400 contacts to SMS indicates it takes about
10 seconds to be completed which is considered as ok.

LINKS

Task 2076366 (send now)
Task 2067873 (template access)
PR #37298
PR odoo/enterprise#5750
This commit is contained in:
Thibault Delavallée
2019-09-25 11:12:59 +00:00
parent 889d30e2c8
commit 48cc126137
5 changed files with 45 additions and 25 deletions
@@ -20,6 +20,8 @@ class Mailing(models.Model):
sms_has_insufficient_credit = fields.Boolean(
'Insufficient IAP credits', compute='_compute_sms_has_insufficient_credit',
help='UX Field to propose to buy IAP credits')
sms_force_send = fields.Boolean(
'Send Directly', help='Use at your own risks.')
# opt_out_link
sms_allow_unsubscribe = fields.Boolean('Include opt-out link', default=True)
@@ -63,7 +65,15 @@ class Mailing(models.Model):
# --------------------------------------------------
def action_put_in_queue_sms(self):
return self.action_put_in_queue()
res = self.action_put_in_queue()
if self.sms_force_send:
self.action_send_mail()
return res
def action_send_now_sms(self):
if not self.sms_force_send:
self.write({'sms_force_send': True})
return self.action_send_mail()
def action_test(self):
if self.mailing_type == 'sms':
@@ -166,6 +176,7 @@ class Mailing(models.Model):
'composition_mode': 'mass',
'mailing_id': self.id,
'mass_keep_log': self.keep_archives,
'mass_force_send': self.sms_force_send,
'mass_sms_allow_unsubscribe': self.sms_allow_unsubscribe,
}
@@ -183,11 +194,6 @@ class Mailing(models.Model):
raise UserError(_('There is no recipients selected.'))
composer = self.env['sms.composer'].with_context(active_id=False).create(mailing._send_sms_get_composer_values(res_ids))
# extra_context = self._get_mass_mailing_context()
# auto-commit except in testing mode
# auto_commit = not getattr(threading.currentThread(), 'testing', False)
# composer.send_mail(auto_commit=auto_commit)
composer._action_send_sms()
mailing.write({'state': 'done', 'sent_date': fields.Datetime.now()})
return True
@@ -23,10 +23,14 @@
<attribute name="attrs">{'invisible': ['|', ('state', 'in', ('in_queue', 'done')), ('mailing_type', '!=', 'mail')]}</attribute>
</xpath>
<xpath expr="//button[@name='action_put_in_queue']" position="after">
<button name="action_put_in_queue" type="object"
class="oe_highlight" string="Send Now"
<button name="action_put_in_queue_sms" type="object"
class="oe_highlight" string="Put in Queue"
attrs="{'invisible': ['|', ('mailing_type', '=', 'mail'), ('state', 'in', ('in_queue', 'done'))]}"
confirm="This will send SMS to all recipients. Do you still want to proceed ?"/>
confirm="This will schedule an SMS marketing to all recipients. Do you still want to proceed ?"/>
<button name="action_send_now_sms" type="object"
string="Send Now"
attrs="{'invisible': ['|', ('mailing_type', '=', 'mail'), ('state', 'in', ('in_queue', 'done'))]}"
confirm="This will send SMS to all recipients now. Do you still want to proceed ?"/>
</xpath>
<!-- Headers / Warnings -->
<xpath expr="//header" position="after">
@@ -92,6 +96,7 @@
<page string="SMS Content" name="sms_body" attrs="{'invisible': [('mailing_type', '!=', 'sms')]}">
<field name="body_plaintext" widget="sms_widget" required="1"/>
<group>
<field name="sms_force_send" invisible="1"/>
<field name="sms_allow_unsubscribe"/>
</group>
</page>
@@ -74,12 +74,9 @@ class SMSComposer(models.TransientModel):
all_bodies[sms_id] = body
return all_bodies
def _prepare_mass_sms_values(self, records=None):
result = super(SMSComposer, self)._prepare_mass_sms_values(records=records)
def _prepare_mass_sms_values(self, records):
result = super(SMSComposer, self)._prepare_mass_sms_values(records)
if self.composition_mode == 'mass' and self.mailing_id:
records = records if records is not None else self._get_records()
# for record_id, sms_values in result.items():
for record in records:
sms_values = result[record.id]
@@ -90,8 +87,8 @@ class SMSComposer(models.TransientModel):
})
return result
def _action_send_sms_mass(self, records=None):
sms_all = super(SMSComposer, self)._action_send_sms_mass()
def _prepare_mass_sms(self, records, sms_record_values):
sms_all = super(SMSComposer, self)._prepare_mass_sms(records, sms_record_values)
if self.mailing_id:
updated_bodies = sms_all._update_body_short_links()
for sms in sms_all:
+15 -8
View File
@@ -147,7 +147,12 @@ class SendSMS(models.TransientModel):
self._action_send_sms()
return False
def _action_send_sms(self, force_send=False):
def action_send_sms_mass_now(self):
if not self.mass_force_send:
self.write({'mass_force_send': True})
return self.action_send_sms()
def _action_send_sms(self):
records = self._get_records()
if self.composition_mode == 'numbers':
return self._action_send_sms_numbers()
@@ -183,16 +188,15 @@ class SendSMS(models.TransientModel):
records = records if records is not None else self._get_records()
sms_record_values = self._prepare_mass_sms_values(records)
sms_create_vals = [sms_record_values[record.id] for record in records]
sms = self.env['sms.sms'].sudo().create(sms_create_vals)
sms = self._prepare_mass_sms(records, sms_record_values)
if sms and self.mass_keep_log and records and issubclass(type(records), self.pool['mail.thread']):
log_values = self._prepare_mass_log_values(sms_record_values)
log_values = self._prepare_mass_log_values(records, sms_record_values)
records._message_log_batch(**log_values)
if sms and self.mass_force_send:
sms.send(auto_commit=False, raise_exception=False)
return self.env['sms.sms'].sudo().search([('id', 'in', sms.ids)])
return sms
# ------------------------------------------------------------
@@ -230,8 +234,7 @@ class SendSMS(models.TransientModel):
all_bodies = self.env['mail.template']._render_template(self.body, records._name, records.ids)
return all_bodies
def _prepare_mass_sms_values(self, records=None):
records = records if records is not None else self._get_records()
def _prepare_mass_sms_values(self, records):
all_bodies = self._prepare_body_values(records)
all_recipients = self._prepare_recipient_values(records)
blacklist_ids = self._get_blacklist_record_ids(records, all_recipients)
@@ -263,13 +266,17 @@ class SendSMS(models.TransientModel):
}
return result
def _prepare_mass_sms(self, records, sms_record_values):
sms_create_vals = [sms_record_values[record.id] for record in records]
return self.env['sms.sms'].sudo().create(sms_create_vals)
def _prepare_log_body_values(self, sms_records_values):
result = {}
for record_id, sms_values in sms_records_values.items():
result[record_id] = sms_values['body']
return result
def _prepare_mass_log_values(self, sms_records_values):
def _prepare_mass_log_values(self, records, sms_records_values):
return {
'bodies': self._prepare_log_body_values(sms_records_values),
'message_type': 'sms',
+6 -1
View File
@@ -39,7 +39,12 @@
</group>
</sheet>
<footer>
<button string="Send SMS" type="object" class="oe_highlight" name="action_send_sms"/>
<button string="Send SMS" type="object" class="oe_highlight" name="action_send_sms"
attrs="{'invisible': [('composition_mode', 'not in', ('comment', 'numbers'))]}"/>
<button string="Put in queue" type="object" class="oe_highlight" name="action_send_sms"
attrs="{'invisible': [('composition_mode', '!=', 'mass')]}"/>
<button string="Send Now" type="object" name="action_send_sms_mass_now"
attrs="{'invisible': [('composition_mode', '!=', 'mass')]}"/>
<button string="Close" class="btn btn-secondary" special="cancel"/>
</footer>
</form>