summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOdoo's Mergebot <xmo+roboto@odoo.com>2019-09-13 10:44:08 +0200
committerGitHub <noreply@github.com>2019-09-13 10:44:08 +0200
commit5dc97ee7daa1772f1b895976b0eb17425de11c52 (patch)
tree3501a0838626c07a2c27ee7ba4d2ee831032ffb1
parentf97c902847c9897852f2928aae316f91ff6f92bc (diff)
parent8e2da3b4d6f88160041526676fd77a97256cd6e7 (diff)
[MERGE] sms, mass_mailing{_sms}: implement YAB2B
PURPOSE Once SMS Marketing has landed, a few comments/reviews to improve the interface, clarify buttons and actions and ease the onboarding. SPECIFICATIONS [FIX] sms - mass_mailing: fix some bugs - Cancel button in the sms form view not related to a action : When we click on the cancel button of the sms form view, get traceback. Fix the issue by adding the cancel action in sms model. - Mass mailing contact - False as the display name : The display name was the mail address but when the contact didn't have mail address a ugly False was print to described the contact (form view). Then change the _rec_name to print the name as the display name. - Fix SMS composer for the res.partner tree Only the first record was truly selected when open the sms composer (mass) from the tree view of partner. Fix that by bind the actives_ids in the context and also fix the names of related actions. [IMP] sms: allow to preview a sms template Add a way for the user to preview sms content from a sms template depending of a record (targeted model) and the installed languages (as for the mail template). Add button to open this preview on the form of the sms template. [IMP] sms: allow to create a sms context action from template 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 composition mode works differently if there are multiple target selected (mass) or a single target (comment). [IMP] (sms_)mass_mailing: improve interface - Change the name of "Mass SMS" by "SMS Marketing" - Improve the Mass Mailing views (kanban, form) - Improve the Mass SMS views (kanban, search, form) - Hide the sanitized numbers from views (sometime replace by mobile) - Fix demo data of sms - Explain the language field of sms template - Improve the render of the sms composer wizard LINKS TASK_ID : 2053631 closes odoo/odoo#36286 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
-rw-r--r--addons/mass_mailing/models/mailing_contact.py1
-rw-r--r--addons/mass_mailing/views/mailing_mailing_views.xml96
-rw-r--r--addons/mass_mailing_sms/controllers/main.py2
-rw-r--r--addons/mass_mailing_sms/models/mailing_mailing.py2
-rw-r--r--addons/mass_mailing_sms/report/mailing_trace_report_views.xml6
-rw-r--r--addons/mass_mailing_sms/views/mailing_contact_views.xml26
-rw-r--r--addons/mass_mailing_sms/views/mailing_list_views.xml2
-rw-r--r--addons/mass_mailing_sms/views/mailing_mailing_views.xml64
-rw-r--r--addons/mass_mailing_sms/views/mailing_sms_menus.xml22
-rw-r--r--addons/mass_mailing_sms/wizard/mailing_sms_test.py2
-rw-r--r--addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml8
-rw-r--r--addons/phone_validation/views/phone_blacklist_views.xml2
-rw-r--r--addons/sms/__manifest__.py1
-rw-r--r--addons/sms/data/mail_demo.xml46
-rw-r--r--addons/sms/models/sms_sms.py3
-rw-r--r--addons/sms/models/sms_template.py47
-rw-r--r--addons/sms/views/res_partner_views.xml7
-rw-r--r--addons/sms/views/sms_template_views.xml29
-rw-r--r--addons/sms/wizard/__init__.py1
-rw-r--r--addons/sms/wizard/sms_composer.py19
-rw-r--r--addons/sms/wizard/sms_composer_views.xml11
-rw-r--r--addons/sms/wizard/sms_template_preview.py53
-rw-r--r--addons/sms/wizard/sms_template_preview_views.xml42
-rw-r--r--addons/test_mail_full/tests/test_sms_composer.py76
24 files changed, 412 insertions, 156 deletions
diff --git a/addons/mass_mailing/models/mailing_contact.py b/addons/mass_mailing/models/mailing_contact.py
index e50b36534e4..ea25ce31936 100644
--- a/addons/mass_mailing/models/mailing_contact.py
+++ b/addons/mass_mailing/models/mailing_contact.py
@@ -48,7 +48,6 @@ class MassMailingContact(models.Model):
_inherit = ['mail.thread.blacklist']
_description = 'Mass Mailing Contact'
_order = 'email'
- _rec_name = 'email'
name = fields.Char()
company_name = fields.Char(string='Company Name')
diff --git a/addons/mass_mailing/views/mailing_mailing_views.xml b/addons/mass_mailing/views/mailing_mailing_views.xml
index 96456cf2afa..6f5ac26adde 100644
--- a/addons/mass_mailing/views/mailing_mailing_views.xml
+++ b/addons/mass_mailing/views/mailing_mailing_views.xml
@@ -57,46 +57,40 @@
<field name="state" readonly="1" widget="statusbar"/>
</header>
<div class="alert alert-info text-center" attrs="{'invisible': [('ignored', '=', 0)]}" role="alert">
- <p>
- <button class="btn-link"
- name="action_view_traces_ignored"
- type="object">
- <strong>
- <field name="ignored" class="oe_inline mr-2"/>
- <span name="ignored_text">emails have been ignored and will not be sent.</span>
- </strong>
- </button>
- </p>
+ <button class="btn-link py-0"
+ name="action_view_traces_ignored"
+ type="object">
+ <strong>
+ <field name="ignored" class="oe_inline mr-2"/>
+ <span name="ignored_text">emails have been ignored and will not be sent.</span>
+ </strong>
+ </button>
</div>
<div class="alert alert-info text-center" attrs="{'invisible': [('scheduled', '=', 0)]}" role="alert">
- <p>
- <button class="btn-link"
- name="action_view_traces_scheduled"
- type="object">
- <strong>
- <field name="scheduled" class="oe_inline mr-2"/>
- <span name="scheduled_text">emails are in queue and will be sent soon.</span>
- </strong>
- </button>
- </p>
+ <button class="btn-link py-0"
+ name="action_view_traces_scheduled"
+ type="object">
+ <strong>
+ <field name="scheduled" class="oe_inline mr-2"/>
+ <span name="scheduled_text">emails are in queue and will be sent soon.</span>
+ </strong>
+ </button>
</div>
<div class="alert alert-info text-center" attrs="{'invisible': ['|', ('state', '!=', 'done'), ('failed', '=', 0)]}" role="alert">
- <p>
- <button class="btn-link"
- name="action_view_traces_failed"
- type="object">
- <strong>
- <field name="failed" class="oe_inline mr-2"/>
- <span name="failed_text">emails could not be sent.</span>
- </strong>
- </button>
- </p>
+ <button class="btn-link py-0"
+ name="action_view_traces_failed"
+ type="object">
+ <strong>
+ <field name="failed" class="oe_inline mr-2"/>
+ <span name="failed_text">emails could not be sent.</span>
+ </strong>
+ </button>
</div>
<div class="alert alert-info text-center" attrs="{'invisible': [('state', '!=', 'in_queue')]}" role="alert">
- <p><strong>
- This mailing is scheduled for
+ <strong>
+ <span name="next_departure_text">This mailing is scheduled for</span>
<field name="next_departure" class="oe_inline"/>.
- </strong></p>
+ </strong>
</div>
<sheet>
<div class="oe_button_box" name="button_box">
@@ -140,7 +134,7 @@
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<group>
<field name="active" invisible="1"/>
- <field name="name" required="1" string="Mailing Name"/>
+ <field name="name" required="1" string="Name"/>
<field name="mailing_type" widget="radio" options="{'horizontal': true}" invisible="1"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<field name="subject" string="Subject" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
@@ -261,8 +255,11 @@
<field name='state' readonly="1"/>
<field name='email_from' readonly="1"/>
<field name='color'/>
+ <field name='user_id'/>
<field name='expected'/>
<field name='failed'/>
+ <field name='total'/>
+ <field name='mailing_model_id'/>
<field name='sent_date'/>
<field name='active'/>
<templates>
@@ -286,11 +283,14 @@
<div class="oe_kanban_content">
<div class="o_kanban_record_top">
<div class="o_kanban_record_headings">
- <h3 class="mt4 mb4"><field name="name"/></h3>
+ <h3 class="mt4 mb4">
+ <field name="name"/>
+ </h3>
<field name="mailing_type" invisible="1"/>
<div class="o_kanban_record_subtitle" attrs="{'invisible': [('sent_date', '=', False)]}">
- <h5 style="display: inline;"><field name="campaign_id" groups="mass_mailing.group_mass_mailing_campaign"/></h5>
- <t t-if="record.campaign_id.raw_value" groups="mass_mailing.group_mass_mailing_campaign"> - </t><field name="sent_date"/>
+ <h5 style="display: inline;">
+ <field name="campaign_id" groups="mass_mailing.group_mass_mailing_campaign"/>
+ </h5>
</div>
</div>
</div>
@@ -298,9 +298,7 @@
<t t-if="record.sent_date">
<div class="row" name="stat_opened">
<div class="col-8">
- <a name="%(action_view_mail_mail_statistics_mailing)d"
- context="{'search_default_filter_opened': True}"
- type="action">Opened</a>
+ Opened
</div>
<div class="col-4 text-right">
<field name="opened_ratio"/> %
@@ -308,9 +306,7 @@
</div>
<div class="row" name="stat_replied">
<div class="col-8">
- <a name="%(action_view_mail_mail_statistics_mailing)d"
- context="{'search_default_filter_replied': True}"
- type="action">Replied</a>
+ Replied
</div>
<div class="col-4 text-right">
<field name="replied_ratio"/> %
@@ -318,20 +314,26 @@
</div>
<div class="row" name="stat_clicks">
<div class="col-8">
- <a name="%(link_tracker_action_mass_mailing)d"
- type="action">Clicks</a>
+ Clicks
</div>
<div class="col-4 text-right">
<field name="clicks_ratio"/> %
</div>
</div>
- <div class='o_kanban_primary_bottom' name="stat_delivered">
+ <div class='mt-1' name="stat_delivered">
<field name="delivered" widget="progressbar" title="Delivered" options="{'current_value': 'delivered', 'max_value': 'expected', 'editable': false}"/>
</div>
</t>
</div>
</div>
- <div class="oe_clear"></div>
+ <div>
+ <span class="fa fa-clock-o text-muted mr-2" attrs="{'invisible': [('sent_date', '=', False)]}"/><field name="sent_date"/>
+ <img t-att-src="kanban_image('res.users', 'image_64', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value" width="24" height="24" class="oe_kanban_avatar float-right"/>
+ </div>
+ <div name="div_total_model" class="oe_clear text-muted" style="display: inline;">
+ <field name='total'/>
+ <field name='mailing_model_id'/>
+ </div>
</div>
</t>
</templates>
diff --git a/addons/mass_mailing_sms/controllers/main.py b/addons/mass_mailing_sms/controllers/main.py
index 9f03af5c6d2..0fc9569641f 100644
--- a/addons/mass_mailing_sms/controllers/main.py
+++ b/addons/mass_mailing_sms/controllers/main.py
@@ -65,7 +65,7 @@ class MailingSMSController(http.Controller):
else:
blacklist_rec = request.env['phone.blacklist'].sudo().add(tocheck_number)
blacklist_rec._message_log(
- body=_('Blacklist through mass sms unsubscribe (mailing ID: %s - model: %s)') %
+ body=_('Blacklist through SMS Marketing unsubscribe (mailing ID: %s - model: %s)') %
(trace.mass_mailing_id.id, trace.mass_mailing_id.mailing_model_id.display_name))
lists_optin = request.env['mailing.contact.subscription'].sudo().search([
('contact_id.phone_sanitized', '=', tocheck_number),
diff --git a/addons/mass_mailing_sms/models/mailing_mailing.py b/addons/mass_mailing_sms/models/mailing_mailing.py
index 96e3eb4c8d1..374fac93c77 100644
--- a/addons/mass_mailing_sms/models/mailing_mailing.py
+++ b/addons/mass_mailing_sms/models/mailing_mailing.py
@@ -69,7 +69,7 @@ class Mailing(models.Model):
if self.mailing_type == 'sms':
ctx = dict(self.env.context, default_mailing_id=self.id)
return {
- 'name': _('Test Mailing'),
+ 'name': _('Test SMS marketing'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'mailing.sms.test',
diff --git a/addons/mass_mailing_sms/report/mailing_trace_report_views.xml b/addons/mass_mailing_sms/report/mailing_trace_report_views.xml
index 2adf5953faf..565ccfc79e0 100644
--- a/addons/mass_mailing_sms/report/mailing_trace_report_views.xml
+++ b/addons/mass_mailing_sms/report/mailing_trace_report_views.xml
@@ -52,19 +52,19 @@
<!-- Actions and Menuitems -->
<record id="mailing_trace_report_action_sms" model="ir.actions.act_window">
- <field name="name">Mass SMS Analysis</field>
+ <field name="name">SMS Marketing Analysis</field>
<field name="res_model">mailing.trace.report</field>
<field name="view_mode">graph,pivot</field>
<field name="domain">[('mailing_type', '=', 'sms')]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
- Mass SMS Statistics allows you to check different mailing related information like number of sent SMS or bounced SMS.
+ SMS Marketing Statistics allows you to check different mailing related information like number of sent SMS or bounced SMS.
You can sort out your analysis by different groups to get accurate grained analysis.
</p>
</field>
</record>
- <!-- Mass SMS / Reporting -->
+ <!-- SMS Marketing / Reporting -->
<record id="mass_mailing_sms_menu_reporting" model="ir.ui.menu">
<field name="action" ref="mailing_trace_report_action_sms"/>
</record>
diff --git a/addons/mass_mailing_sms/views/mailing_contact_views.xml b/addons/mass_mailing_sms/views/mailing_contact_views.xml
index 374191ec91f..eb2d7e57384 100644
--- a/addons/mass_mailing_sms/views/mailing_contact_views.xml
+++ b/addons/mass_mailing_sms/views/mailing_contact_views.xml
@@ -6,7 +6,8 @@
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
- <field name="phone_sanitized"/>
+ <field name="mobile"/>
+ <field name="phone_sanitized" invisible="1"/>
</xpath>
<xpath expr="//filter[@name='filter_not_optout']" position="after">
<separator/>
@@ -31,7 +32,8 @@
<field name="create_date"/>
<field name="name"/>
<field name="company_name"/>
- <field name="phone_sanitized"/>
+ <field name="mobile"/>
+ <field name="phone_sanitized" invisible="1"/>
<field name="phone_blacklisted"/>
<field name="opt_out" invisible="'default_list_ids' not in context"/>
</tree>
@@ -43,18 +45,13 @@
<field name="model">mailing.contact</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_form"/>
<field name="arch" type="xml">
- <xpath expr="//label[@for='email']" position="attributes">
- <attribute name="invisible">context.get('mailing_sms') == True</attribute>
- </xpath>
- <xpath expr="//div[@name='email_details']" position="attributes">
- <attribute name="invisible">context.get('mailing_sms') == True</attribute>
- </xpath>
<xpath expr="//div[@name='email_details']" position="after">
- <label for="phone_sanitized" class="oe_inline"/>
+ <label for="mobile" class="oe_inline"/>
<div class="o_row o_row_readonly" name="phone_details">
- <i class="fa fa-ban" style="color: red;" role="img" title="This number is blacklisted for mass SMS"
+ <i class="fa fa-ban" style="color: red;" role="img" title="This number is blacklisted for SMS Marketing"
aria-label="Phone Blacklisted" attrs="{'invisible': [('phone_blacklisted', '=', False)]}" groups="base.group_user"></i>
- <field name="phone_sanitized" widget="phone"/>
+ <field name="mobile" widget="phone"/>
+ <field name="phone_sanitized" invisible="1"/>
<field name="phone_blacklisted" invisible="1"/>
</div>
</xpath>
@@ -67,10 +64,11 @@
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_kanban"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='email']" position="after">
- <field name="phone_sanitized"/>
+ <field name="mobile"/>
+ <field name="phone_sanitized" invisible="1"/>
</xpath>
<xpath expr="//t[@t-esc='record.email.value']" position="after">
- <t t-esc="record.phone_sanitized.value"/>
+ <t t-esc="record.mobile.value"/>
</xpath>
</field>
</record>
@@ -88,7 +86,7 @@
</field>
</record>
- <!-- Mass SMS / Contacts Lists / Contacts Lists -->
+ <!-- SMS Marketing / Contacts Lists / Contacts Lists -->
<record id="mass_mailing_sms.mailing_contact_menu_sms" model="ir.ui.menu">
<field name="action" ref="mailing_contact_action_sms"/>
</record>
diff --git a/addons/mass_mailing_sms/views/mailing_list_views.xml b/addons/mass_mailing_sms/views/mailing_list_views.xml
index d1b88669078..de57e354b01 100644
--- a/addons/mass_mailing_sms/views/mailing_list_views.xml
+++ b/addons/mass_mailing_sms/views/mailing_list_views.xml
@@ -15,7 +15,7 @@
</field>
</record>
- <!-- Mass SMS / Contacts Lists / Contacts Lists -->
+ <!-- SMS Marketing / Contacts Lists / Contacts Lists -->
<record id="mass_mailing_sms.mailing_list_menu_sms" model="ir.ui.menu">
<field name="action" ref="mailing_list_action_sms"/>
</record>
diff --git a/addons/mass_mailing_sms/views/mailing_mailing_views.xml b/addons/mass_mailing_sms/views/mailing_mailing_views.xml
index 964af78e5f6..0b8d47637f1 100644
--- a/addons/mass_mailing_sms/views/mailing_mailing_views.xml
+++ b/addons/mass_mailing_sms/views/mailing_mailing_views.xml
@@ -1,6 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
- <record id="mailing_mailing_view_form" model="ir.ui.view">
+
+ <record model="ir.ui.view" id="mailing_mailing_view_search_sms">
+ <field name="name">mailing.mailing.search</field>
+ <field name="model">mailing.mailing</field>
+ <field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_search"/>
+ <field name="mode">primary</field>
+ <field name="arch" type="xml">
+ <xpath expr="//filter[@name='assigned_to_me']" position="attributes">
+ <attribute name="string">My SMS Marketing</attribute>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="mailing_mailing_view_form_sms" model="ir.ui.view">
<field name="name">mailing.mailing.view.form.inherit.sms</field>
<field name="model">mailing.mailing</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_form"/>
@@ -19,15 +32,13 @@
<xpath expr="//header" position="after">
<field name="sms_has_insufficient_credit" invisible="1"/>
<div class="alert alert-warning text-center" attrs="{'invisible': [('sms_has_insufficient_credit', '=', False)]}" role="alert">
- <p>
- <button class="btn-link"
- name="action_buy_sms_credits"
- type="object">
- <strong>
- It appears you don't have enough IAP credits. Click here to buy credits.
- </strong>
- </button>
- </p>
+ <button class="btn-link py-0"
+ name="action_buy_sms_credits"
+ type="object">
+ <strong>
+ It appears you don't have enough IAP credits. Click here to buy credits.
+ </strong>
+ </button>
</div>
</xpath>
<xpath expr="//span[@name='ignored_text']" position="attributes">
@@ -48,6 +59,12 @@
<xpath expr="//span[@name='failed_text']" position="after">
<span name="failed_text_sms" attrs="{'invisible': [('mailing_type', '!=', 'sms')]}">SMS Text Message could not be sent.</span>
</xpath>
+ <xpath expr="//span[@name='next_departure_text']" position='attributes'>
+ <attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
+ </xpath>
+ <xpath expr="//span[@name='next_departure_text']" position='after'>
+ <span name="next_departure_text" attrs="{'invisible': [('mailing_type', '!=', 'sms')]}">This SMS marketing is scheduled for</span>
+ </xpath>
<!-- Stat Buttons -->
<xpath expr="//button[@id='button_view_sent']" position="attributes">
<attribute name="attrs">{'invisible': ['|', '&amp;', ('sent', '=', 0), ('state', 'in', ('draft', 'test')), ('mailing_type', '!=', 'mail')]}</attribute>
@@ -81,7 +98,6 @@
<page string="SMS Content" name="sms_body" attrs="{'invisible': [('mailing_type', '!=', 'sms')]}">
<field name="body_plaintext" widget="sms_widget"/>
<group>
- <field name="sms_template_id" domain="[('model', '=', mailing_model_real)]"/>
<field name="sms_allow_unsubscribe"/>
</group>
</page>
@@ -114,7 +130,7 @@
</field>
</record>
- <record id="mailing_mailing_view_kanban" model="ir.ui.view">
+ <record id="mailing_mailing_view_kanban_sms" model="ir.ui.view">
<field name="name">mailing.mailing.view.kanban.inherit.sms</field>
<field name="model">mailing.mailing</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_kanban"/>
@@ -122,18 +138,17 @@
<xpath expr="//field[@name='active']" position="after">
<field name="sms_has_insufficient_credit"/>
</xpath>
- <xpath expr="//h3" position="after">
- <div class="alert alert-warning" role="alert" attrs="{'invisible': [('sms_has_insufficient_credit', '=', False)]}">
- <a name="action_buy_sms_credits"
- type="object">Insufficient credits</a>
- </div>
- </xpath>
<xpath expr="//div[@name='stat_opened']" position="attributes">
<attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
</xpath>
<xpath expr="//div[@name='stat_replied']" position="attributes">
<attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
- </xpath>
+ </xpath>
+ <xpath expr="//div[@name='div_total_model']" position="after">
+ <div class="alert alert-warning" role="alert" attrs="{'invisible': ['&amp;',('sms_has_insufficient_credit', '=', False), ('mailing_type', '!=', 'mail')]}">
+ <a name="action_buy_sms_credits" type="object">Insufficient credits</a>
+ </div>
+ </xpath>
</field>
</record>
@@ -142,7 +157,7 @@
<field name="model">mailing.mailing</field>
<field name="priority">20</field>
<field name="arch" type="xml">
- <tree string="Mass SMS">
+ <tree string="SMS Marketing">
<field name="name"/>
<field name="mailing_type" invisible="1"/>
<field name="sent"/>
@@ -154,9 +169,10 @@
</record>
<record id="mailing_mailing_action_sms" model="ir.actions.act_window">
- <field name="name">Mass SMS</field>
+ <field name="name">SMS Marketing</field>
<field name="res_model">mailing.mailing</field>
<field name="view_mode">kanban,tree,form,graph</field>
+ <field name="search_view_id" ref="mailing_mailing_view_search_sms"/>
<field name="domain">[('mailing_type', '=', 'sms')]</field>
<field name="context">{
'search_default_assigned_to_me': 1,
@@ -165,7 +181,7 @@
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
- Create a new mass SMS
+ Create a new SMS Marketing
</p><p>
You can easily send SMS to any contact saved in other Odoo apps.
</p>
@@ -174,7 +190,7 @@
<record id="mailing_mailing_action_sms_view_kanban" model="ir.actions.act_window.view">
<field name="sequence">1</field>
<field name="view_mode">kanban</field>
- <field name="view_id" ref="mass_mailing.view_mail_mass_mailing_kanban"/>
+ <field name="view_id" ref="mailing_mailing_view_kanban_sms"/>
<field name="act_window_id" ref="mailing_mailing_action_sms"/>
</record>
<record id="mailing_mailing_action_sms_view_tree" model="ir.actions.act_window.view">
@@ -184,7 +200,7 @@
<field name="act_window_id" ref="mailing_mailing_action_sms"/>
</record>
- <!-- Mass SMS / Mass SMS -->
+ <!-- SMS Marketing / SMS Marketing -->
<record id="mass_mailing_sms_menu_mass_sms" model="ir.ui.menu">
<field name="action" ref="mailing_mailing_action_sms"/>
</record>
diff --git a/addons/mass_mailing_sms/views/mailing_sms_menus.xml b/addons/mass_mailing_sms/views/mailing_sms_menus.xml
index f7cc0f05edb..f009c9df330 100644
--- a/addons/mass_mailing_sms/views/mailing_sms_menus.xml
+++ b/addons/mass_mailing_sms/views/mailing_sms_menus.xml
@@ -1,59 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
- <!-- Mass SMS -->
+ <!-- SMS Marketing -->
<menuitem id="mass_mailing_sms_menu_root"
- name="Mass SMS"
+ name="SMS Marketing"
sequence="60"
web_icon="mass_mailing_sms,static/description/icon.png"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Mass SMS -->
+ <!-- SMS Marketing / SMS Marketing -->
<menuitem id="mass_mailing_sms_menu_mass_sms"
- name="Mass SMS"
+ name="SMS Marketing"
parent="mass_mailing_sms_menu_root"
sequence="1"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Contacts Lists -->
+ <!-- SMS Marketing / Contacts Lists -->
<menuitem id="mass_mailing_sms_menu_contacts"
name="Contacts Lists"
parent="mass_mailing_sms_menu_root"
sequence="2"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Contacts Lists / Contacts Lists -->
+ <!-- SMS Marketing / Contacts Lists / Contacts Lists -->
<menuitem id="mailing_list_menu_sms"
name="Contacts Lists"
parent="mass_mailing_sms_menu_contacts"
sequence="1"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Contacts Lists / Contacts -->
+ <!-- SMS Marketing / Contacts Lists / Contacts -->
<menuitem id="mailing_contact_menu_sms"
name="Contacts"
parent="mass_mailing_sms_menu_contacts"
sequence="2"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Reporting -->
+ <!-- SMS Marketing / Reporting -->
<menuitem id="mass_mailing_sms_menu_reporting"
name="Reporting"
parent="mass_mailing_sms_menu_root"
sequence="80"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Configuration -->
+ <!-- SMS Marketing / Configuration -->
<menuitem id="mass_mailing_sms_menu_configuration"
name="Configuration"
parent="mass_mailing_sms_menu_root"
sequence="100"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Configuration / Blacklist -->
+ <!-- SMS Marketing / Configuration / Blacklist -->
<menuitem id="phone_blacklist_menu"
name="Phone Blacklist"
parent="mass_mailing_sms_menu_configuration"
sequence="1"
action="phone_validation.phone_blacklist_action"
groups="mass_mailing.group_mass_mailing_user"/>
- <!-- Mass SMS / Configuration / Link Tracker -->
+ <!-- SMS Marketing / Configuration / Link Tracker -->
<menuitem id="link_tracker_menu"
name="Link Tracker Blacklist"
parent="mass_mailing_sms_menu_configuration"
diff --git a/addons/mass_mailing_sms/wizard/mailing_sms_test.py b/addons/mass_mailing_sms/wizard/mailing_sms_test.py
index 72552715b59..bfdc3c02a70 100644
--- a/addons/mass_mailing_sms/wizard/mailing_sms_test.py
+++ b/addons/mass_mailing_sms/wizard/mailing_sms_test.py
@@ -25,7 +25,7 @@ class MassSMSTest(models.TransientModel):
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:
- raise exceptions.UserError(_('Following numbers are not correctly encoded: %s') % repr(invalid_numbers))
+ raise exceptions.UserError(_('Following numbers are not correctly encoded: %s, example : "+32 495 85 85 77, +33 545 55 55 55"') % repr(invalid_numbers))
self.sanitized_numbers = ','.join(sanitized_numbers)
else:
self.sanitized_numbers = False
diff --git a/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml b/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml
index 97ed90df4e5..8224f8ae1fb 100644
--- a/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml
+++ b/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml
@@ -6,11 +6,11 @@
<field name="arch" type="xml">
<form string="Send a Sample SMS">
<p class="text-muted">
- Send a sample SMS for testing purpose to the numbers below.
+ Send a sample SMS for testing purpose to the numbers below (comma-separated list).
</p>
<group>
- <field name="numbers"/>
- <field name="sanitized_numbers" invisible="0"/>
+ <field name="numbers" placeholder="+32 495 85 85 77, +33 545 55 55 55"/>
+ <field name="sanitized_numbers" invisible="1"/>
<field name="mailing_id" invisible="1"/>
</group>
<footer>
@@ -22,7 +22,7 @@
</record>
<record id="mailing_sms_test_action" model="ir.actions.act_window">
- <field name="name">Test SMS Mailing</field>
+ <field name="name">Test SMS Marketing</field>
<field name="res_model">mailing.sms.test</field>
<field name="view_mode">form</field>
<field name="target">new</field>
diff --git a/addons/phone_validation/views/phone_blacklist_views.xml b/addons/phone_validation/views/phone_blacklist_views.xml
index 4d76576cefa..d0b3b47eb04 100644
--- a/addons/phone_validation/views/phone_blacklist_views.xml
+++ b/addons/phone_validation/views/phone_blacklist_views.xml
@@ -52,7 +52,7 @@
<p class="o_view_nocontent_smiling_face">
Add a phone number in the blacklist
</p><p>
- Blacklisted phone numbers means that the recipient won't receive mass SMS anymore.
+ Blacklisted phone numbers means that the recipient won't receive SMS Marketing anymore.
</p>
</field>
</record>
diff --git a/addons/sms/__manifest__.py b/addons/sms/__manifest__.py
index 2b15020a219..90462b4c8fb 100644
--- a/addons/sms/__manifest__.py
+++ b/addons/sms/__manifest__.py
@@ -16,6 +16,7 @@ The service is provided by the In App Purchase Odoo platform.
'data/ir_cron_data.xml',
'wizard/sms_cancel_views.xml',
'wizard/sms_composer_views.xml',
+ 'wizard/sms_template_preview_views.xml',
'wizard/sms_resend_views.xml',
'views/ir_actions_views.xml',
'views/res_config_settings_views.xml',
diff --git a/addons/sms/data/mail_demo.xml b/addons/sms/data/mail_demo.xml
index 103a89fbd8c..8086bef21fe 100644
--- a/addons/sms/data/mail_demo.xml
+++ b/addons/sms/data/mail_demo.xml
@@ -18,20 +18,20 @@
<field name="author_id" ref="base.partner_admin"/>
<field name="date" eval="(DateTime.today() - timedelta(days=4)).strftime('%Y-%m-%d %H:%M:00')"/>
</record>
- <record id="message_demo_partner_1_2_notif_0" model="mail.notification">
- <field name="mail_message_id" ref="message_demo_partner_1_1"/>
- <field name="res_partner_id" ref="base.res_partner_address_28"/>
- <field name="notification_type">email</field>
- <field name="notification_status">exception</field>
- <field name="failure_type">SMTP</field>
- </record>
+ <record id="message_demo_partner_1_2_notif_0" model="mail.notification">
+ <field name="mail_message_id" ref="message_demo_partner_1_1"/>
+ <field name="res_partner_id" ref="base.res_partner_address_28"/>
+ <field name="notification_type">email</field>
+ <field name="notification_status">exception</field>
+ <field name="failure_type">SMTP</field>
+ </record>
<record id="message_demo_partner_1_2" model="mail.message">
<field name="model">res.partner</field>
<field name="res_id" ref="base.res_partner_address_28"/>
<field name="body" type="html"><p>Hello! This is an example of SMS.</p></field>
<field name="message_type">sms</field>
- <field name="subtype_id" ref="mail.mt_comment"/>
+ <field name="subtype_id" ref="mail.mt_note"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="date" eval="(DateTime.today() - timedelta(days=3)).strftime('%Y-%m-%d %H:%M:00')"/>
</record>
@@ -40,23 +40,23 @@
<field name="res_id" ref="base.res_partner_address_28"/>
<field name="body" type="html"><p>Hello! This is an example of another SMS with notifications.</p></field>
<field name="message_type">sms</field>
- <field name="subtype_id" ref="mail.mt_comment"/>
+ <field name="subtype_id" ref="mail.mt_note"/>
<field name="author_id" ref="base.partner_admin"/>
<field name="date" eval="(DateTime.today() - timedelta(days=2)).strftime('%Y-%m-%d %H:%M:00')"/>
</record>
- <record id="message_demo_partner_1_3_notif_0" model="mail.notification">
- <field name="mail_message_id" ref="message_demo_partner_1_3"/>
- <field name="res_partner_id" ref="base.res_partner_address_28"/>
- <field name="notification_type">sms</field>
- <field name="notification_status">exception</field>
- <field name="failure_type">sms_credit</field>
- </record>
- <record id="message_demo_partner_1_3_notif_1" model="mail.notification">
- <field name="mail_message_id" ref="message_demo_partner_1_3"/>
- <field name="res_partner_id" ref="base.res_partner_address_16"/>
- <field name="notification_type">sms</field>
- <field name="notification_status">exception</field>
- <field name="failure_type">sms_credit</field>
- </record>
+ <record id="message_demo_partner_1_3_notif_0" model="mail.notification">
+ <field name="mail_message_id" ref="message_demo_partner_1_3"/>
+ <field name="res_partner_id" ref="base.res_partner_address_28"/>
+ <field name="notification_type">sms</field>
+ <field name="notification_status">exception</field>
+ <field name="failure_type">sms_credit</field>
+ </record>
+ <record id="message_demo_partner_1_3_notif_1" model="mail.notification">
+ <field name="mail_message_id" ref="message_demo_partner_1_3"/>
+ <field name="res_partner_id" ref="base.res_partner_address_16"/>
+ <field name="notification_type">sms</field>
+ <field name="notification_status">exception</field>
+ <field name="failure_type">sms_credit</field>
+ </record>
</data></odoo>
diff --git a/addons/sms/models/sms_sms.py b/addons/sms/models/sms_sms.py
index 9ea635b2070..8bf9c8c2924 100644
--- a/addons/sms/models/sms_sms.py
+++ b/addons/sms/models/sms_sms.py
@@ -56,6 +56,9 @@ class SmsSms(models.Model):
if auto_commit is True and not getattr(threading.currentThread(), 'testing', False):
self._cr.commit()
+ def cancel(self):
+ self.state = 'canceled'
+
@api.model
def _process_queue(self, ids=None):
""" Send immediately queued messages, committing after each message is sent.
diff --git a/addons/sms/models/sms_template.py b/addons/sms/models/sms_template.py
index 5439c9cbb22..e1ba74d3bb1 100644
--- a/addons/sms/models/sms_template.py
+++ b/addons/sms/models/sms_template.py
@@ -23,7 +23,13 @@ class SMSTemplate(models.Model):
help="The type of document this template can be used with")
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}")
+ lang = fields.Char('Language', help="Use this field to either force a specific language (ISO code) or dynamically "
+ "detect the language of your recipient by a placeholder expression "
+ "(e.g. ${object.partner_id.lang})")
+ # Use to create contextual action (same as for email template)
+ 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 +92,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 +128,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 +143,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 """
diff --git a/addons/sms/views/res_partner_views.xml b/addons/sms/views/res_partner_views.xml
index c696531fc3b..91c6b77c9b2 100644
--- a/addons/sms/views/res_partner_views.xml
+++ b/addons/sms/views/res_partner_views.xml
@@ -9,7 +9,7 @@
<field name="priority">10</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='mobile']" position="after">
- <field name="phone_sanitized" groups="base.group_no_one"/>
+ <field name="phone_sanitized" groups="base.group_no_one" invisible="1"/>
</xpath>
<xpath expr="//field[@name='phone']" position="replace">
<label for="phone"/>
@@ -27,7 +27,7 @@
</record>
<!-- Add action entry in the Action Menu for Partners -->
- <act_window id="res_partner_act_window_sms_composer_single"
+ <act_window id="res_partner_act_window_sms_composer_multi"
name="Send SMS Text Message"
binding_model="res.partner"
res_model="sms.composer"
@@ -37,9 +37,10 @@
context="{
'default_composition_mode': 'mass',
'default_mass_keep_log': True,
+ 'default_res_ids': active_ids
}"
/>
- <act_window id="res_partner_act_window_sms_composer_multi"
+ <act_window id="res_partner_act_window_sms_composer_single"
name="Send SMS Text Message"
binding_model="res.partner"
res_model="sms.composer"
diff --git a/addons/sms/views/sms_template_views.xml b/addons/sms/views/sms_template_views.xml
index 1f339e4d944..28d76d30e13 100644
--- a/addons/sms/views/sms_template_views.xml
+++ b/addons/sms/views/sms_template_views.xml
@@ -6,13 +6,37 @@
<field name="arch" type="xml">
<form string="SMS Templates">
<sheet>
+ <div class="oe_button_box" name="button_box">
+ <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"/>
<h1><field name="name" required="1"/></h1>
<group>
<field name="model_id" required="1" options="{'no_create': True}"/>
<field name="model" invisible="1"/>
- <field name="lang"/>
+ <field name="lang" groups="base.group_no_one" placeholder="e.g. en_US or ${object.partner_id.lang}"/>
</group>
</div>
<notebook>
@@ -73,5 +97,4 @@
sequence="2"
action="sms_template_action"/>
-</data>
-</odoo>
+</data></odoo>
diff --git a/addons/sms/wizard/__init__.py b/addons/sms/wizard/__init__.py
index e6669ba5997..bd0a5c11647 100644
--- a/addons/sms/wizard/__init__.py
+++ b/addons/sms/wizard/__init__.py
@@ -3,3 +3,4 @@
from . import sms_cancel
from . import sms_composer
from . import sms_resend
+from . import sms_template_preview
diff --git a/addons/sms/wizard/sms_composer.py b/addons/sms/wizard/sms_composer.py
index e14b8efc053..88d38e5662e 100644
--- a/addons/sms/wizard/sms_composer.py
+++ b/addons/sms/wizard/sms_composer.py
@@ -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
diff --git a/addons/sms/wizard/sms_composer_views.xml b/addons/sms/wizard/sms_composer_views.xml
index abaa3e7af09..1cb59d99dd3 100644
--- a/addons/sms/wizard/sms_composer_views.xml
+++ b/addons/sms/wizard/sms_composer_views.xml
@@ -21,9 +21,9 @@
<!-- Mass mode information (res_ids versus active domain) -->
<div colspan="2" class="alert alert-info text-center mb-3" role="alert"
attrs="{'invisible': [('composition_mode', '=', 'comment')]}">
- <p>
+ <p class="my-0">
<span attrs="{'invisible': [('use_active_domain', '=', 'True')]}">
- <field class="oe_inline font-weight-bold" name="res_ids_count"/> visible records selected.
+ <field class="oe_inline font-weight-bold" name="res_ids_count"/> records selected.
</span>
Check <field class="oe_inline ml-2" name="use_active_domain"/> to send to all
<field class="oe_inline font-weight-bold ml-2" name="active_domain_count"/> records instead. <br/>
@@ -31,16 +31,11 @@
and <field class="oe_inline font-weight-bold" name="recipient_invalid_count"/> are invalid.
</p>
</div>
- <field name="template_id" options="{'no_create': True}" attrs="{'invisible': [('composition_mode', '!=', 'mass')]}"/>
<field name="recipient_description" string="Recipients"
readonly="1"
attrs="{'invisible': [('composition_mode', '!=', 'comment')]}"/>
<field name="body" widget="sms_widget"/>
- </group>
- <group>
- <group>
- <field name="mass_keep_log" invisible="1"/>
- </group>
+ <field name="mass_keep_log" invisible="1"/>
</group>
</sheet>
<footer>
diff --git a/addons/sms/wizard/sms_template_preview.py b/addons/sms/wizard/sms_template_preview.py
new file mode 100644
index 00000000000..65060f4848a
--- /dev/null
+++ b/addons/sms/wizard/sms_template_preview.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class SMSTemplatePreview(models.TransientModel):
+ _inherit = "sms.template"
+ _name = "sms.template.preview"
+ _description = "SMS Template Preview"
+
+ @api.model
+ def _selection_target_model(self):
+ models = self.env['ir.model'].search([])
+ return [(model.model, model.name) for model in models]
+
+ @api.model
+ def _selection_languages(self):
+ return self.env['res.lang'].get_installed()
+
+ @api.model
+ def default_get(self, fields):
+ result = super(SMSTemplatePreview, self).default_get(fields)
+ sms_template = self._context.get('default_sms_template_id') and self.env['sms.template'].browse(self._context['default_sms_template_id']) or False
+ if sms_template and not result.get('res_id'):
+ result['res_id'] = self.env[sms_template.model].search([], limit=1)
+ return result
+
+ sms_template_id = fields.Many2one('sms.template')
+
+ lang = fields.Selection(_selection_languages, string='Template Preview Language')
+ model_id = fields.Many2one('ir.model', related="sms_template_id.model_id")
+ res_id = fields.Integer(string='Record ID')
+ resource_ref = fields.Reference(string='Record reference', selection='_selection_target_model', compute='_compute_resource_ref', inverse='_inverse_resource_ref')
+
+ @api.depends('model_id', 'res_id')
+ def _compute_resource_ref(self):
+ for preview in self:
+ if preview.model_id:
+ preview.resource_ref = '%s,%s' % (preview.model_id.model, preview.res_id or 0)
+
+ def _inverse_resource_ref(self):
+ for preview in self:
+ if preview.resource_ref:
+ preview.res_id = preview.resource_ref.id
+
+ @api.onchange('lang', 'resource_ref')
+ def on_change_resource_ref(self):
+ # Update res_id and body depending of the resource_ref
+ if self.resource_ref:
+ self.res_id = self.resource_ref.id
+ template = self.sms_template_id.with_context(lang=self.lang)
+ self.body = template._render_template(template.body, template.model, self.res_id)
diff --git a/addons/sms/wizard/sms_template_preview_views.xml b/addons/sms/wizard/sms_template_preview_views.xml
new file mode 100644
index 00000000000..ab9d3f90127
--- /dev/null
+++ b/addons/sms/wizard/sms_template_preview_views.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data>
+ <!-- SMS Template Preview -->
+ <record model="ir.ui.view" id="sms_template_preview_form">
+ <field name="name">sms.template.preview.form</field>
+ <field name="model">sms.template.preview</field>
+ <field name="arch" type="xml">
+ <form string="SMS Preview">
+ <h3>Preview of <field name="sms_template_id" readonly="1" nolabel="1" class="oe_inline"/></h3>
+ <div class="o_row">
+ <span>Choose an example <field name="model_id" readonly="1"/> record:</span>
+ <div>
+ <field name="resource_ref" class="oe_inline" options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"/>
+ <span class="text-warning" attrs="{'invisible':[('resource_ref','!=', False)]}">No records</span>
+ </div>
+ </div>
+ <p>Choose a language: <field name="lang" class="oe_inline ml8"/></p>
+ <label for="body" string="SMS content"/>
+ <hr/>
+ <field name="body" readonly="1" nolabel="1" options='{"safe": True}'/>
+ <hr/>
+ <footer>
+ <button string="Discard" class="btn-secondary" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="sms_template_preview_action" model="ir.actions.act_window">
+ <field name="name">Template Preview</field>
+ <field name="res_model">sms.template.preview</field>
+ <field name="binding_model_id" ref="sms.model_sms_template"/>
+ <field name="type">ir.actions.act_window</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="sms_template_preview_form"/>
+ <field name="target">new</field>
+ <field name="context">{'default_sms_template_id':active_id}</field>
+ </record>
+
+ </data>
+</odoo>
diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py
index 4e8e7e70edb..04ff799408f 100644
--- a/addons/test_mail_full/tests/test_sms_composer.py
+++ b/addons/test_mail_full/tests/test_sms_composer.py
@@ -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)