Files
odoo_source/addons/mass_mailing/views/mailing_mailing_views.xml
T
Nasreddin (bon)andAurélien Warnon e598e74648 [IMP] mass_mailing(_sms): support calendar view and ease scheduling
PURPOSE

To ease the scheduling process, we introduce a new calendar view on mailings
that allows to easily schedule your communications. In addition, mailings and
sms views are improved to ease global usability especially about scheduling
configuration.

SPECIFICATIONSS

- add a calendar view to allow marketeers to either schedule or overview their
  ongoing mailings/sms;
  Side note: inspired by what has been done with Social Posts

- improve the scheduling flow by allowing to configure it directly in the form
  view using fields rather than action buttons that open an extra window. This
  implied removing the schedule wizard as everything is now configured directly
  from the mailing form view;

- add a constraint on 'schedule_date' to make sure it's not scheduled in the
  past;

- compute a calendar_date to be used by calendar view. It is either sent
date (if sent), next departure of cron (if in queue) or schedule date (if
scheduled).

- update mass_mailing_sms to match scheduling flow of emails and adjust the
  'sms_force_send' display to ease understanding ;

LINKS

Task ID-2202759
COM PR odoo/odoo#57000
ENT PR odoo/enterprise#12920
UPG PR odoo/upgrade#1736

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
Co-authored-by: Aurélien Warnon <awa@odoo.com>
2020-11-24 15:25:50 +00:00

515 lines
34 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- MAILING !-->
<record model="ir.ui.view" id="view_mail_mass_mailing_search">
<field name="name">mailing.mailing.search</field>
<field name="model">mailing.mailing</field>
<field name="arch" type="xml">
<search string="Mailings">
<field name="name" string="Mailing" filter_domain="['|', ('name', 'ilike', self), ('subject', 'ilike', self)]"/>
<field name="campaign_id" string="Campaign" groups="mass_mailing.group_mass_mailing_campaign"/>
<filter string="My Mailings" name="assigned_to_me"
domain="[('user_id', '=', uid)]"
help="Mailings that are assigned to me"/>
<separator/>
<filter name="filter_sent_date" date="sent_date"/>
<separator/>
<filter name="inactive" string="Archived" domain="[('active', '=', False)]"/>
<group expand="0" string="Group By">
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
<filter string="Sent By" name="sent_by" domain="[]" context="{'group_by': 'email_from'}"/>
<separator/>
<filter string="Sent Period" name="sent_date" domain="[]" context="{'group_by': 'sent_date'}"/>
</group>
</search>
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_tree">
<field name="name">mailing.mailing.tree</field>
<field name="model">mailing.mailing</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<tree string="Mailings" sample="1">
<field name="subject" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<field name="mailing_type" invisible="1"/>
<field name="mailing_model_id" string="Recipients"/>
<field name="user_id" widget="many2one_avatar_user"/>
<field name="schedule_date" string="Scheduled" widget="remaining_days"/>
<field name="sent_date" widget="date"/>
<field name="state" decoration-info="state == 'draft' or state == 'in_queue'" decoration-success="state == 'sending' or state == 'done'" widget="badge"/>
<field name="campaign_id" string="Campaign"
groups="mass_mailing.group_mass_mailing_campaign"/>
<field name="sent"/>
<field name="bounced_ratio" string="Bounced (%)"/>
<field name="received_ratio" string="Delivered (%)"/>
<field name="opened_ratio" string="Opened (%)"/>
<field name="clicks_ratio" string="Clicked (%)"/>
<field name="replied_ratio" string="Replied (%)"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_form">
<field name="name">mailing.mailing.form</field>
<field name="model">mailing.mailing</field>
<field name="arch" type="xml">
<form string="Mailing">
<header style="min-height:31px;">
<button name="action_launch" type="object" class="oe_highlight" string="Launch"
attrs="{'invisible': ['|', ('state', 'in', ('in_queue', 'done')), ('schedule_type', '=', 'scheduled')]}"
confirm="This will send the email to all recipients. Do you still want to proceed ?"/>
<button name="action_schedule" type="object" class="oe_highlight" string="Schedule"
attrs="{'invisible': ['|', ('state', 'in', ('in_queue', 'done')), ('schedule_type', '=', 'now')]}"/>
<button name="action_test" type="object" class="btn-secondary" string="Test"/>
<button name="action_cancel" type="object" attrs="{'invisible': [('state', '!=', 'in_queue')]}" class="btn-secondary" string="Cancel"/>
<button name="action_retry_failed" type="object" attrs="{'invisible': ['|', ('state', '!=', 'done'), ('failed', '=', 0)]}" class="oe_highlight" string="Retry"/>
<field name="state" readonly="1" widget="statusbar"/>
</header>
<div class="alert alert-info text-center" role="alert" attrs="{'invisible': ['|','&amp;','&amp;','&amp;',('state', '!=', 'in_queue'),('sent', '=', 0),('ignored', '=', 0),('scheduled', '=', 0),('failed', '=', 0)]}">
<div attrs="{'invisible': [('ignored', '=', 0)]}">
<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 attrs="{'invisible': [('scheduled', '=', 0)]}">
<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 attrs="{'invisible': ['&amp;', ('sent', '=', 0), ('state', 'in', ('draft', 'test', 'in_queue'))]}">
<button class="btn-link py-0"
name="action_view_traces_sent"
type="object">
<strong>
<field name="sent" class="oe_inline mr-2"/>
<span name="sent">emails have been sent.</span>
</strong>
</button>
</div>
<div attrs="{'invisible': ['|', ('state', '!=', 'done'), ('failed', '=', 0)]}">
<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 attrs="{'invisible': [('state', '!=', 'in_queue')]}">
<strong>
<span name="next_departure_text">This mailing is scheduled for </span>
<field name="next_departure" class="oe_inline"/>.
</strong>
</div>
</div>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_delivered"
id="button_view_delivered"
type="object"
context="{'search_default_filter_delivered': True}"
attrs="{'invisible': [('state', 'in', ('draft','test'))]}"
class="oe_stat_button">
<field name="received_ratio" string="Received" widget="percentpie"/>
</button>
<button name="action_view_opened"
type="object"
context="{'search_default_filter_opened': True}"
attrs="{'invisible': [('state', 'in', ('draft','test'))]}"
class="oe_stat_button">
<field name="opened_ratio" string="Opened" widget="percentpie"/>
</button>
<button name="action_view_clicked"
type="object"
context="{'search_default_filter_clicked': True}"
attrs="{'invisible': [('state', 'in', ('draft','test'))]}"
class="oe_stat_button">
<field name="clicks_ratio" string="Clicked" widget="percentpie"/>
</button>
<button name="action_view_replied"
type="object"
context="{'search_default_filter_replied': True}"
attrs="{'invisible': [('state', 'in', ('draft','test'))]}"
class="oe_stat_button">
<field name="replied_ratio" string="Replied" widget="percentpie"/>
</button>
<button name="action_view_bounced"
type="object"
context="{'search_default_filter_bounced': True}"
attrs="{'invisible': [('state', 'in', ('draft','test'))]}"
class="oe_stat_button">
<field name="bounced_ratio" string="Bounced" widget="percentpie"/>
</button>
</div>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<group>
<field name="active" invisible="1"/>
<field name="mailing_type" widget="radio" options="{'horizontal': true}" invisible="1"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<field class="o_text_overflow" name="subject" string="Subject" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}" widget="char_emojis" placeholder="e.g. New Sale on all T-shirts"/>
<field class="o_text_overflow" name="preview" string="Preview Text" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}" widget="char_emojis" placeholder="e.g. Check it out before it's too late!"/>
<label for="mailing_model_id" string="Recipients"/>
<div name="mailing_model_id_container">
<div class="row">
<div class="col-xs-12 col-md-3" >
<field name="mailing_model_id" widget="selection"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
</div>
<div attrs="{'invisible': [('mailing_model_name', '!=', 'mailing.list')]}" class="col-xs-12 col-md-9 pt-1">
<label for="contact_list_ids" string="Select mailing lists:" class="oe_edit_only"/>
<field name="contact_list_ids" widget="many2many_tags"
placeholder="Select mailing lists..." class="oe_inline"
attrs="{
'required':[('mailing_model_name','=','mailing.list')],
'readonly': [('state', 'in', ('sending', 'done'))]
}"/>
</div>
</div>
<field name="mailing_model_name" invisible="1"/>
<field name="mailing_model_real" invisible="1"/>
<div attrs="{'invisible': [('mailing_model_name', '=', 'mailing.list')]}">
<field name="mailing_domain" widget="domain" options="{'model': 'mailing_model_real'}"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
</div>
</div>
<label for="schedule_type" string="Schedule"/>
<div name="schedule_block">
<div class="row">
<div class="col-xs-12 col-md-3">
<field name="schedule_type"/>
</div>
<div class="col-xs-12 col-md-3" attrs="{'invisible': [('schedule_type', '=', 'now')]}">
<field name="schedule_date" attrs="{'required': [('schedule_type', '=', 'scheduled')]}" />
</div>
</div>
</div>
</group>
<notebook>
<page string="Mail Body" name="mail_body">
<field name="body_html" class="oe_read_only" widget="html"
options="{'cssReadonly': 'mass_mailing.iframe_css_assets_readonly'}"/>
<field name="body_arch" class="o_mail_body oe_edit_only" widget="mass_mailing_html"
options="{
'snippets': 'mass_mailing.email_designer_snippets',
'cssEdit': 'mass_mailing.iframe_css_assets_edit',
'inline-field': 'body_html'
}" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
</page>
<page string="Dynamic Placeholder Generator"
name="dynamic_placeholder_generator"
groups="base.group_no_one">
<group>
<field name="model_object_field" attrs="{'invisible': True}"/>
<field name="model_object_field"
domain="[('model_id','=',mailing_model_real),('ttype','!=','one2many'),('ttype','!=','many2many')]"/>
<field name="sub_object" readonly="1"/>
<field name="sub_model_object_field"
domain="[('model_id','=',sub_object),('ttype','!=','one2many'),('ttype','!=','many2many')]"
attrs="{'readonly':[('sub_object','=',False)],'required':[('sub_object','!=',False)]}"/>
<field name="null_value"/>
<field name="copyvalue"/>
</group>
</page>
<page string="Settings" name="settings">
<group>
<group>
<field name="id" invisible="1"/>
<field name="name" required="False" groups="base.group_no_one" class="o_text_overflow" string="Name"/>
<field name="user_id" domain="[('share', '=', False)]"/>
<field name="email_from" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<label for="reply_to"/>
<div name="reply_to_details">
<field name="reply_to_mode" widget="radio"
attrs="{
'invisible': [('mailing_model_name', 'in', ['mailing.contact', 'res.partner', 'mailing.list'])],
'readonly': [('state', 'in', ('sending', 'done'))]
}"/>
<field name="reply_to"
attrs="{
'required': [('reply_to_mode', '=', 'email')],
'invisible': [('reply_to_mode', '=', 'thread')],
'readonly': [('state', 'in', ('sending', 'done'))]
}"/>
<div style="margin-top:-5px">
<small class="oe_edit_only text-muted mb-2"
style="font-size:74%"
attrs="{'invisible': ['|', ('reply_to_mode', '=', 'thread'), ('mailing_model_name', 'in', ['mailing.contact', 'res.partner', 'mailing.list'])],}">
To track replies, this address must belong to this database.
</small>
</div>
</div>
<label for="attachment_ids"/>
<div name="attachment_ids_details">
<field name="attachment_ids" widget="many2many_binary" string="Attach a file" class="oe_inline"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
</div>
<field name="mail_server_id" groups="base.group_no_one" options="{'no_create': True, 'no_open': True}"/>
<field name="keep_archives" groups="base.group_no_one" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
</group>
<group string="Marketing" groups="base.group_no_one,mass_mailing.group_mass_mailing_campaign">
<field name="campaign_id"
string="Mailing Campaign"
groups="mass_mailing.group_mass_mailing_campaign"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))],
'required': [('unique_ab_testing', '=', True)]}"/>
<field name="source_id"
string="Source"
readonly="1"
required="False"
class="o_text_overflow"
groups="base.group_no_one"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<field name="medium_id"
string="Medium"
required="True"
groups="base.group_no_one"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<field name="unique_ab_testing"
groups="mass_mailing.group_mass_mailing_campaign"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<label for="contact_ab_pc" groups="mass_mailing.group_mass_mailing_campaign"/>
<div groups="mass_mailing.group_mass_mailing_campaign">
<field name="contact_ab_pc"
class="oe_inline"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/> %
</div>
</group>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" groups="base.group_user"/>
<field name="message_ids"/>
<field name="activity_ids"/>
</div>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_kanban">
<field name="name">mailing.mailing.kanban</field>
<field name="model">mailing.mailing</field>
<field name="arch" type="xml">
<kanban default_group_by="state" quick_create="false" sample="1">
<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='mailing_model_name'/>
<field name='sent_date'/>
<field name='schedule_date'/>
<field name='next_departure'/>
<field name='active'/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="{{!selection_mode ? 'oe_kanban_color_' + kanban_getcolor(record.color.raw_value) : ''}} oe_kanban_card oe_kanban_global_click oe_kanban_mass_mailing">
<div class="o_dropdown_kanban dropdown" t-if="!selection_mode">
<a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" data-display="static" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<ul class="oe_kanban_colorpicker" data-field="color"/>
<t t-if="widget.deletable">
<a role="menuitem" type="delete" class="dropdown-item">Delete</a>
</t>
<a role="menuitem" class="dropdown-item o_kanban_mailing_active" name="toggle_active" type="object">
<t t-if="record.active.raw_value">Archive</t>
<t t-if="!record.active.raw_value">Restore</t>
</a>
</div>
</div>
<div class="oe_kanban_content">
<div class="o_kanban_record_top">
<div class="o_kanban_record_headings">
<div class="row" attrs="{'invisible': [('sent_date', '=', False)]}">
<h3 class="my-1 col-8 o_text_overflow">
<field name="subject"/>
</h3>
<div class="progress border col-3 px-0 mt-2" style="background-color: inherit; height:12px;">
<div class="progress-bar" role="progressbar"
aria-valuemin="0"
t-att-aria-valuenow="record.delivered.raw_value"
t-att-aria-valuemax="record.expected.raw_value"
t-attf-style="width: #{record.delivered.raw_value * 100 / record.expected.raw_value}%"/>
</div>
</div>
<h3 class="my-1" attrs="{'invisible': [('sent_date', '!=', False)]}">
<field name="subject"/>
</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>
</div>
</div>
</div>
<div class="o_kanban_record_body" t-if="!selection_mode" attrs="{'invisible': [('sent_date', '=', False), ('schedule_date', '=', False), ('state', '!=', 'in_queue')]}">
<div>
<span attrs="{'invisible': [('sent_date', '=', False)]}"><b><field name="delivered"/> / <field name="expected"/></b> Delivered to</span>
<span attrs="{'invisible': [('sent_date', '!=', False)]}"><b><field name='total'/></b></span>
<field name='mailing_model_id' attrs="{'invisible': [('mailing_model_name','=','mailing.list')]}"/>
<span attrs="{'invisible': [('mailing_model_name','!=','mailing.list')]}">Mailing Contact</span>
</div>
<div attrs="{'invisible': [('sent_date', '=', False)]}" class="d-flex justify-content-between">
<div name="stat_opened">
<b><field name="opened_ratio" />%</b> Opened
</div>
<div name="stat_replied">
<b><field name="replied_ratio" />%</b> Replied
</div>
<div name="stat_clicks">
<b><field name="clicks_ratio" />%</b> Clicks
</div>
</div>
</div>
</div>
<div name="div_responsible_avatar" class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<span attrs="{'invisible': [('sent_date', '=', False)]}"
t-attf-title="Sent on #{record.sent_date.value}" class="d-inline-flex">
<span class="fa fa-calendar-check-o mr-2 small my-auto" aria-label="Sent date"/>
<span class="align-self-baseline"><field name="sent_date" widget="date"/></span>
</span>
<span attrs="{'invisible': [('schedule_date', '=', False)]}"
t-attf-title="Scheduled on #{record.schedule_date.value}" class="d-inline-flex">
<span class="fa fa-hourglass-half mr-2 small my-auto" aria-label="Scheduled date"/>
<span class="align-self-baseline"><field name="schedule_date" widget="date"/></span>
</span>
<span attrs="{'invisible': ['|', '|', ('sent_date', '!=', False), ('schedule_date', '!=', False), ('state', '=', 'in_queue')]}"
class="oe_clear">
<b><field name='total'/></b>
<field name='mailing_model_id' attrs="{'invisible': [('mailing_model_name','=','mailing.list')]}"/>
<span attrs="{'invisible': [('mailing_model_name','!=','mailing.list')]}">Mailing Contact</span>
</span>
<span attrs="{'invisible': ['|', '|', ('schedule_date', '!=', False), ('state', '!=', 'in_queue'), ('next_departure', '=', False)]}"
t-attf-title="Scheduled on #{record.next_departure.value}" class="d-inline-flex">
<span class="fa fa-hourglass-o mr-2 small my-auto" aria-label="Scheduled date"/>
<span class="align-self-baseline">Next Batch</span>
</span>
</div>
<div class="oe_kanban_bottom_right">
<field name="user_id" widget="many2one_avatar_user"/>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="mailing_mailing_view_calendar" model="ir.ui.view">
<field name="name">mailing.mailing.view.calendar</field>
<field name="model">mailing.mailing</field>
<field name="arch" type="xml">
<calendar date_start="calendar_date" string="Mailings" hide_time="true" mode="month" color="state" quick_add="False">
<field name="mailing_model_id" string="Recipient" options="{'no_open': True}"/>
<field name="user_id" filters="1" invisible="1"/>
<field name="state" filters="1" invisible="1"/>
</calendar>
</field>
</record>
<record id="view_mail_mass_mailing_graph" model="ir.ui.view">
<field name="name">mailing.mailing.graph</field>
<field name="model">mailing.mailing</field>
<field name="arch" type="xml">
<graph string="Mailing" type="bar" sample="1">
<field name="state" type="row"/>
<field name="color" invisible="1"/>
</graph>
</field>
</record>
<record id="mailing_mailing_action_mail" model="ir.actions.act_window">
<field name="name">Mailings</field>
<field name="res_model">mailing.mailing</field>
<field name="view_mode">kanban,tree,form,calendar,graph</field>
<field name="domain">[('mailing_type', '=', 'mail')]</field>
<field name="context">{
'search_default_assigned_to_me': 1,
'default_user_id': uid,
'default_mailing_type': 'mail',
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new mailing
</p><p>
You don't need to import your mailing lists, you can easily
send emails<br/> to any contact saved in other Odoo apps.
</p>
</field>
</record>
<record id="action_view_mass_mailings_from_campaign" model="ir.actions.act_window">
<field name="name">Mailings</field>
<field name="res_model">mailing.mailing</field>
<field name="view_mode">kanban,tree,form,calendar</field>
<field name="context">{
'search_default_assigned_to_me': 1,
'search_default_campaign_id': [active_id],
'default_campaign_id': active_id,
'default_user_id': uid,
}
</field>
<field name="domain">[('mailing_type', '=', 'mail')]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new mailing
</p><p>
You don't need to import your mailing lists, you can easily
send emails<br/> to any contact saved in other Odoo apps.
</p>
</field>
</record>
<record id="action_create_mass_mailings_from_campaign" model="ir.actions.act_window">
<field name="name">Mailings</field>
<field name="res_model">mailing.mailing</field>
<field name="view_mode">form,kanban,tree</field>
<field name="context">{
'search_default_assigned_to_me': 1,
'search_default_campaign_id': [active_id],
'default_campaign_id': active_id,
'default_user_id': uid,
}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new mailing
</p><p>
You don't need to import your mailing lists, you can easily
send emails<br/> to any contact saved in other Odoo apps.
</p>
</field>
</record>
<menuitem name="Mailings" id="mass_mailing_menu"
parent="mass_mailing_menu_root"
sequence="1"
action="mailing_mailing_action_mail"
groups="mass_mailing.group_mass_mailing_user"/>
</odoo>