[IMP] sms: allow to create a sms context action

In the SMS template, we add the ability to create (and remove)
a contextual action on the targeted model from a sms template.
We change the sms composer to guess the composition mode,
and then to be usable by contextual action. The guess compostion mode
works differently if there are multiple target
selected (mass) or a single target (comment).

TASK_ID : 2053631
This commit is contained in:
ryv-odoo
2019-09-12 14:53:44 +00:00
parent 076e71cb5b
commit e4afa5d6d0
4 changed files with 150 additions and 12 deletions
+40 -2
View File
@@ -24,6 +24,9 @@ class SMSTemplate(models.Model):
model = fields.Char('Related Document Model', related='model_id.model', index=True, store=True, readonly=True)
body = fields.Char('Body', translate=True, required=True)
lang = fields.Char('Language', placeholder="${object.partner_id.lang}")
sidebar_action_id = fields.Many2one('ir.actions.act_window', 'Sidebar action', readonly=True, copy=False,
help="Sidebar action to make this template available on records "
"of the related document model")
# Fake fields used to implement the placeholder assistant
model_object_field = fields.Many2one('ir.model.fields', string="Field", store=False,
help="Select target field from the related document model.\n"
@@ -86,6 +89,32 @@ class SMSTemplate(models.Model):
name=_("%s (copy)") % self.name)
return super(SMSTemplate, self).copy(default=default)
def action_create_sidebar_action(self):
ActWindow = self.env['ir.actions.act_window']
view = self.env.ref('sms.sms_composer_view_form')
for template in self:
button_name = _('Send SMS (%s)') % template.name
action = ActWindow.create({
'name': button_name,
'type': 'ir.actions.act_window',
'res_model': 'sms.composer',
# Add default_composition_mode to guess to determine if need to use mass or comment composer
'context': "{'default_template_id' : %d, 'default_composition_mode': 'guess', 'default_res_ids': active_ids, 'default_res_id': active_id}" % (template.id),
'view_mode': 'form',
'view_id': view.id,
'target': 'new',
'binding_model_id': template.model_id.id,
})
template.write({'sidebar_action_id': action.id})
return True
def action_unlink_sidebar_action(self):
for template in self:
if template.sidebar_action_id:
template.sidebar_action_id.unlink()
return True
def _get_context_lang_per_id(self, res_ids):
self.ensure_one()
if res_ids is None:
@@ -96,8 +125,7 @@ class SMSTemplate(models.Model):
results = dict((res_id, self.with_context(lang=lang)) for res_id in res_ids)
else:
rendered_langs = self._render_template(self.lang, self.model, res_ids)
results = dict(
(res_id, self.with_context(lang=lang) if lang else self)
results = dict((res_id, self.with_context(lang=lang) if lang else self)
for res_id, lang in rendered_langs.items())
return results
@@ -112,6 +140,16 @@ class SMSTemplate(models.Model):
return tpl_to_rids
def _get_translated_bodies(self, res_ids):
""" return sms translated bodies into a dict {'res_id':'body'} """
self.ensure_one()
lang_to_rids = self._get_ids_per_lang(res_ids)
all_bodies = {}
for lang, rids in lang_to_rids.items():
template = self.with_context(lang=lang)
all_bodies.update(template._render_template(template.body, self.model, rids))
return all_bodies
@api.model
def _render_template(self, template_txt, model, res_ids):
""" Render the jinja template """
+22 -3
View File
@@ -7,9 +7,28 @@
<form string="SMS Templates">
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="%(sms_template_preview_action)d" icon="fa-search-plus" string="Preview"
type="action" target="new"
context="{'template_id':active_id}"/>
<field name="sidebar_action_id" invisible="1"/>
<button name="action_create_sidebar_action" type="object"
groups="base.group_system"
class="oe_stat_button"
attrs="{'invisible':[('sidebar_action_id','!=',False)]}" icon="fa-plus"
help="Add a contextual action on the related model to open a sms composer with this template">
<div class="o_field_widget o_stat_info">
<span class="o_stat_text">Add</span>
<span class="o_stat_text">Context Action</span>
</div>
</button>
<button name="action_unlink_sidebar_action" type="object"
groups="base.group_system"
class="oe_stat_button" icon="fa-minus"
attrs="{'invisible':[('sidebar_action_id','=',False)]}"
help="Remove the contextual action of the related model" widget="statinfo">
<div class="o_field_widget o_stat_info">
<span class="o_stat_text">Remove</span>
<span class="o_stat_text">Context Action</span>
</div>
</button>
<button class="oe_stat_button" name="%(sms_template_preview_action)d" icon="fa-search-plus" string="Preview" type="action" target="new"/>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only" string="SMS Template"/>
+12 -7
View File
@@ -21,7 +21,16 @@ class SendSMS(models.TransientModel):
return result
result['res_model'] = result.get('res_model') or self.env.context.get('active_model')
result['composition_mode'] = result.get('composition_mode') or 'comment'
result['composition_mode'] = result.get('composition_mode')
# guess the composition mode, if multiples ids => mass composition else comment
if self.env.context.get('default_composition_mode') and self.env.context.get('default_composition_mode') == "guess":
if self.env.context.get('active_ids') and len(self.env.context.get('active_ids')) > 1:
result['composition_mode'] = 'mass'
result['res_id'] = False
else:
result['composition_mode'] = 'comment'
result['res_ids'] = False
if not result.get('active_domain'):
result['active_domain'] = repr(self.env.context.get('active_domain', []))
@@ -124,7 +133,7 @@ class SendSMS(models.TransientModel):
@api.onchange('composition_mode', 'res_model', 'res_id', 'template_id')
def _onchange_template_id(self):
if self.template_id and self.composition_mode == 'comment' and self.res_id:
self.body = self.template_id._render_template(self.template_id.body, self.res_model, [self.res_id])[self.res_id]
self.body = self.template_id._get_translated_bodies([self.res_id])[self.res_id]
elif self.template_id:
self.body = self.template_id.body
@@ -216,11 +225,7 @@ class SendSMS(models.TransientModel):
def _prepare_body_values(self, records):
if self.template_id and self.body == self.template_id.body:
lang_to_rids = self.template_id._get_ids_per_lang(records.ids)
all_bodies = {}
for lang, rids in lang_to_rids.items():
template = self.template_id.with_context(lang=lang)
all_bodies.update(template._render_template(template.body, records._name, rids))
all_bodies = self.template_id._get_translated_bodies(records.ids)
else:
all_bodies = self.env['mail.template']._render_template(self.body, records._name, records.ids)
return all_bodies
@@ -420,3 +420,79 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M
for record in self.records:
self.assertSMSOutgoing(record.customer_id, None, 'Dear %s this is an SMS.' % record.display_name)
self.assertSMSLogged(record, 'Dear %s this is an SMS.' % record.display_name)
def test_composer_template_context_action(self):
""" Test the context action from a SMS template (Add context action button)
and the usage with the sms composer """
# Create the lang info
self.env.ref('base.lang_fr').write({'active': True})
self.env['ir.translation'].create({
'type': 'model',
'name': 'sms.template,body',
'lang': 'fr_FR',
'res_id': self.sms_template.id,
'src': self.sms_template.body,
'value': "Hello ${object.display_name} ceci est en français.",
})
# set template to try to use customer lang
self.sms_template.write({
'lang': '${object.customer_id.lang}',
})
# create a second record linked to a customer in another language
self.partners[2].write({'lang': 'fr_FR'})
test_record_2 = self.env['mail.test.sms'].create({
'name': 'Test',
'customer_id': self.partners[2].id,
})
test_record_1 = self.env['mail.test.sms'].create({
'name': 'Test',
'customer_id': self.partners[1].id,
})
# Composer creation with context from a template context action (simulate) - comment (single recipient)
with self.sudo('employee'):
composer = self.env['sms.composer'].with_context(
default_composition_mode='guess',
default_res_ids=[test_record_2.id],
default_res_id=test_record_2.id,
active_ids=[test_record_2.id],
active_id=test_record_2.id,
active_model='mail.test.sms',
default_template_id=self.sms_template.id,
).create({
'mass_keep_log': False,
})
# Call manually the onchange
composer._onchange_template_id()
self.assertEquals(composer.composition_mode, "comment")
self.assertEquals(composer.body, "Hello %s ceci est en français." % test_record_2.display_name)
with self.mockSMSGateway():
messages = composer._action_send_sms()
number = self.partners[2].phone_get_sanitized_number()
self.assertSMSNotification([{'partner': test_record_2.customer_id, 'number': number}], "Hello %s ceci est en français." % test_record_2.display_name, messages)
# Composer creation with context from a template context action (simulate) - mass (multiple recipient)
with self.sudo('employee'):
composer = self.env['sms.composer'].with_context(
default_composition_mode='guess',
default_res_ids=[test_record_1.id, test_record_2.id],
default_res_id=test_record_1.id,
active_ids=[test_record_1.id, test_record_2.id],
active_id=test_record_1.id,
active_model='mail.test.sms',
default_template_id=self.sms_template.id,
).create({
'mass_keep_log': True,
})
# Call manually the onchange
composer._onchange_template_id()
self.assertEquals(composer.composition_mode, "mass")
# In english because by default but when sinding depending of record
self.assertEquals(composer.body, "Dear ${object.display_name} this is an SMS.")
with self.mockSMSGateway():
composer.action_send_sms()
self.assertSMSOutgoing(test_record_1.customer_id, None, 'Dear %s this is an SMS.' % test_record_1.display_name)
self.assertSMSOutgoing(test_record_2.customer_id, None, "Hello %s ceci est en français." % test_record_2.display_name)