[MERGE][ADD-IMP] website_* : apply multiple improvements on visitor

PURPOSE

Perform a back2basics on recently-merged and recently-under-merged website
visitor concept. Main points to add are sending emails and sms to visitors
as well as improving integration.

SPECIFICATIONS

Applies various improvements:
  - Add graph view on visitors;
  - Remove welcome message if chat request usecase;
  - If a visitor is online: line is green / if offline: line is red (in the
    list view);
  - Visitors list view: add first / last connection fields and remove
    time_since_last_action;
  - Avoid useless leave notification if the livechat channel is empty;
  - Delete the first message "hello, how may i help you?" on a live chat
    request when I start a live chat;
  - Send mail to visitor linked to res.partner or linked to a crm.lead. New
    simplified wizard to post_message on lead or on res_partner if any
    (priority to lead). If the lead has a res_partner and not the visitor, set
    the partner on the visitor If the lead has no partner, create a partner
    based on the lead and link it to the visitor;
  - send a sms to a visitor if the visitor is linked to a least 1 lead that
    has a mobile number or a phone number or is linked to a res.partner with
    mobile number. As in contact form from website, there is only a phone field
    and not a mobile one, we allow to send sms if the lead phone field is
    filled;

Fixes :
  - ACLS on website_visitor_page model (for im_livechat_group_user)
  - Update visitor lang if visitor change the website lang.
  - Create visitor twice when translated website (due to rerouting)

See sub commit for more informations

LINKS

Task ID : 2070272

closes odoo/odoo#36290

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2019-09-17 18:25:40 +02:00
committed by GitHub
22 changed files with 308 additions and 31 deletions
+11 -6
View File
@@ -764,12 +764,7 @@ class Lead(models.Model):
if len(self.ids) <= 1:
raise UserError(_('Please select more than one element (lead or opportunity) from the list view.'))
# Sorting the leads/opps according to the confidence level of its stage, which relates to the probability of winning it
# The confidence level increases with the stage sequence
# An Opportunity always has higher confidence level than a lead
def opps_key(opportunity):
return opportunity.type == 'opportunity', opportunity.stage_id.sequence, -opportunity.id
opportunities = self.sorted(key=opps_key, reverse=True)
opportunities = self._sort_by_confidence_level(reverse=True)
# get SORTED recordset of head and tail, and complete list
opportunities_head = opportunities[0]
@@ -803,6 +798,16 @@ class Lead(models.Model):
return opportunities_head
def _sort_by_confidence_level(self, reverse=False):
""" Sorting the leads/opps according to the confidence level of its stage, which relates to the probability of winning it
The confidence level increases with the stage sequence
An Opportunity always has higher confidence level than a lead
"""
def opps_key(opportunity):
return opportunity.type == 'opportunity', opportunity.stage_id.sequence, -opportunity._origin.id
return self.sorted(key=opps_key, reverse=reverse)
def get_duplicated_leads(self, partner_id, include_lost=False):
""" Search for opportunities that have the same partner and that arent done or cancelled
:param partner_id : partner to search
@@ -125,7 +125,7 @@ var LivechatButton = Widget.extend({
var message = new WebsiteLivechatMessage(this, data, options);
var hasAlreadyMessage = _.some(this._messages, function (msg) {
return message.id === msg.id;
return message.getID() === msg.getID();
});
if (hasAlreadyMessage) {
return;
@@ -240,7 +240,9 @@ var LivechatButton = Widget.extend({
data: livechatData
});
return self._openChatWindow().then(function () {
self._sendWelcomeMessage();
if (!self._history) {
self._sendWelcomeMessage();
}
self._renderMessages();
self.call('bus_service', 'addChannel', self._livechat.getUUID());
self.call('bus_service', 'startPolling');
+16 -4
View File
@@ -12,13 +12,12 @@ import werkzeug.routing
import werkzeug.utils
import odoo
from odoo import api, models, registry, fields
from odoo import api, models, registry
from odoo import SUPERUSER_ID
from odoo.http import request
from odoo.tools import config, ormcache
from odoo.tools import config
from odoo.tools.safe_eval import safe_eval
from odoo.osv.expression import FALSE_DOMAIN, OR
from odoo.osv import osv
from odoo.addons.base.models.qweb import QWebException
from odoo.addons.http_routing.models.ir_http import ModelConverter, _guess_mimetype
@@ -100,8 +99,21 @@ class Http(models.AbstractModel):
@classmethod
def _dispatch(cls):
"""
In case of rerouting for translate (e.g. when visiting odoo.com/fr_BE/),
_dispatch calls reroute() that returns _dispatch with altered request properties.
The second _dispatch will continue until end of process. When second _dispatch is finished, the first _dispatch
call receive the new altered request and continue.
At the end, 2 calls of _dispatch (and this override) are made with exact same request properties, instead of one.
As the response has not been sent back to the client, the visitor cookie does not exist yet when second _dispatch call
is treated in _handle_webpage_dispatch, leading to create 2 visitors with exact same properties.
To avoid this, we check if, !!! before calling super !!!, we are in a rerouting request. If not, it means that we are
handling the original request, in which we should create the visitor. We ignore every other rerouting requests.
"""
is_rerouting = hasattr(request, 'routing_iteration')
response = super(Http, cls)._dispatch()
cls._register_website_track(response)
if not is_rerouting:
cls._register_website_track(response)
return response
@classmethod
+71 -8
View File
@@ -2,12 +2,11 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta
from hashlib import sha256
import hmac
import uuid
from odoo import fields, models, api, _
from odoo.tools.misc import _consteq, _format_time_ago
from odoo.exceptions import UserError
from odoo.tools.misc import _format_time_ago
from odoo.http import request
from odoo.osv import expression
@@ -42,17 +41,25 @@ class WebsiteVisitor(models.Model):
active = fields.Boolean('Active', default=True)
website_id = fields.Many2one('website', "Website", readonly=True)
user_partner_id = fields.Many2one('res.partner', string="Linked Partner", help="Partner of the last logged in user.")
create_date = fields.Datetime('First connection date', readonly=True)
last_connection_datetime = fields.Datetime('Last Connection', compute="_compute_time_statistics", help="Last page view date", search='_search_last_connection', readonly=True)
last_connections_ids = fields.One2many('website.visitor.lastconnection', 'visitor_id', readonly=True)
# localisation and info
country_id = fields.Many2one('res.country', 'Country', readonly=True)
country_flag = fields.Binary(related="country_id.image", string="Country Flag")
lang_id = fields.Many2one('res.lang', string='Language', help="Language from the website when visitor has been created")
email = fields.Char(string='Email', compute='_compute_email_phone')
mobile = fields.Char(string='Mobile Phone', compute='_compute_email_phone')
# Visit fields
visit_count = fields.Integer('Number of visits', default=1, readonly=True, help="A new visit is considered if last connection was more than 24 hours ago.")
website_track_ids = fields.One2many('website.track', 'visitor_id', string='Visited Pages History', readonly=True)
visitor_page_count = fields.Integer('Page Views', compute="_compute_page_statistics", help="Total number of visits on tracked pages")
page_ids = fields.Many2many('website.page', string="Visited Pages", compute="_compute_page_statistics")
page_count = fields.Integer('# Visited Pages', compute="_compute_page_statistics", help="Total number of tracked page visited")
# Time fields
create_date = fields.Datetime('First connection date', readonly=True)
last_connection_datetime = fields.Datetime('Last Connection', compute="_compute_time_statistics", help="Last page view date", search='_search_last_connection', readonly=True)
last_connections_ids = fields.One2many('website.visitor.lastconnection', 'visitor_id', readonly=True)
time_since_last_action = fields.Char('Last action', compute="_compute_time_statistics", help='Time since last page view. E.g.: 2 minutes ago')
is_connected = fields.Boolean('Is connected ?', compute='_compute_time_statistics', help='A visitor is considered as connected if his last page view was within the last 5 minutes.')
@@ -77,6 +84,23 @@ class WebsiteVisitor(models.Model):
""" % (operator,)
return [('id', 'inselect', (query, [value]))]
@api.depends('user_partner_id.email_normalized', 'user_partner_id.mobile')
def _compute_email_phone(self):
results = self.env['res.partner'].search_read(
[('id', 'in', self.user_partner_id.ids)],
['id', 'email_normalized', 'mobile'],
)
mapped_data = {
result['id']: {
'email_normalized': result['email_normalized'],
'mobile': result['mobile']
} for result in results
}
for visitor in self:
visitor.email = mapped_data.get(visitor.user_partner_id.id, {}).get('email_normalized')
visitor.mobile = mapped_data.get(visitor.user_partner_id.id, {}).get('mobile')
@api.depends('website_track_ids')
def _compute_page_statistics(self):
results = self.env['website.track'].read_group(
@@ -107,6 +131,40 @@ class WebsiteVisitor(models.Model):
visitor.time_since_last_action = _format_time_ago(self.env, (datetime.now() - last_connection_datetime))
visitor.is_connected = (datetime.now() - last_connection_datetime) < timedelta(minutes=5)
def _prepare_visitor_send_mail_values(self):
if self.user_partner_id.email:
return {
'res_model': 'res.partner',
'res_id': self.user_partner_id.id,
'partner_ids': [self.user_partner_id.id],
}
return {}
def action_send_mail(self):
self.ensure_one()
visitor_mail_values = self._prepare_visitor_send_mail_values()
if not visitor_mail_values:
raise UserError(_("There is no email linked this visitor."))
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
ctx = dict(
default_model=visitor_mail_values.get('res_model'),
default_res_id=visitor_mail_values.get('res_id'),
default_use_template=False,
default_partner_ids=[(6, 0, visitor_mail_values.get('partner_ids'))],
default_composition_mode='comment',
default_reply_to=self.env.user.partner_id.email,
)
return {
'name': _('Compose Email'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'mail.compose.message',
'views': [(compose_form.id, 'form')],
'view_id': compose_form.id,
'target': 'new',
'context': ctx,
}
def _get_visitor_from_request(self, with_previous_visitors=False):
if not request:
return None
@@ -147,6 +205,8 @@ class WebsiteVisitor(models.Model):
response.set_cookie('visitor_id', visitor_sudo.access_token, expires=expiration_date)
else:
visitor_sudo._add_tracking(domain, website_track_values)
if visitor_sudo.lang_id.id != request.lang.id:
visitor_sudo.write({'lang_id': request.lang.id})
def _add_tracking(self, domain, website_track_values):
""" Update the visitor when a website_track is added"""
@@ -155,8 +215,10 @@ class WebsiteVisitor(models.Model):
if not last_view or last_view.visit_datetime < datetime.now() - timedelta(minutes=30):
website_track_values['visitor_id'] = self.id
self.env['website.track'].create(website_track_values)
self.env['website.visitor.lastconnection'].create({'visitor_id': self.id,
'connection_datetime': website_track_values['visit_datetime']})
self.env['website.visitor.lastconnection'].create({
'visitor_id': self.id,
'connection_datetime': website_track_values['visit_datetime']
})
def _create_visitor(self, website_track_values=None):
""" Create a visitor and add a track to it if website_track_values is set."""
@@ -165,6 +227,7 @@ class WebsiteVisitor(models.Model):
vals = {
'lang_id': request.lang.id,
'country_id': country_id,
'website_id': request.website.id,
'last_connections_ids': [(0, 0, {'connection_datetime': website_track_values['visit_datetime'] or datetime.now()})],
}
if not self.env.user._is_public():
+20 -4
View File
@@ -85,6 +85,7 @@
<field name="is_connected"/>
<field name="display_name"/>
<field name="page_ids"/>
<field name="user_partner_id"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
@@ -114,7 +115,10 @@
<field name="model">website.visitor</field>
<field name="arch" type="xml">
<form string="Website Visitor" create="0">
<header></header>
<header>
<button name="action_send_mail" type="object" class="btn btn-primary"
attrs="{'invisible': [('email', '=', False)]}" string="Send Email"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button o_stat_button_info" attrs="{'invisible': [('is_connected', '=', False)]}">
@@ -143,9 +147,10 @@
<group string="Visitor Informations">
<field name="is_connected" invisible="1"/>
<field name="user_partner_id" attrs="{'invisible': [('user_partner_id', '=', False)]}"/>
<field name="email"/>
<field name="mobile"/>
<field name="country_id" attrs="{'invisible': [('country_id', '=', False)]}"/>
<field name="lang_id"/>
</group>
<group id="visits" string="Visits">
<field name="create_date"/>
@@ -162,11 +167,12 @@
<field name="name">website.visitor.view.tree</field>
<field name="model">website.visitor</field>
<field name="arch" type="xml">
<tree string="Web Visitors" create="0">
<tree string="Web Visitors" create="0" decoration-success="is_connected" decoration-danger="not is_connected">
<!--TODO DBE : Handle no_label in treeview-->
<field name="country_flag" widget="image" options='{"size": [20, 20]}' string=" "/>
<field name="display_name"/>
<field name="time_since_last_action"/>
<field name="create_date"/>
<field name="last_connection_datetime"/>
<field name="lang_id"/>
<field name="visit_count"/>
<field name="page_ids" widget="many2many_tags"/>
@@ -210,6 +216,16 @@
</field>
</record>
<record id="website_visitor_view_graph" model="ir.ui.view">
<field name="name">website.visitor.view.graph</field>
<field name="model">website.visitor</field>
<field name="arch" type="xml">
<graph string="Visitors">
<field name="create_date" type="row"/>
</graph>
</field>
</record>
<record id="website_visitors_action" model="ir.actions.act_window">
<field name="name">Visitors</field>
<field name="type">ir.actions.act_window</field>
+1 -1
View File
@@ -31,7 +31,7 @@ class Lead(models.Model):
action = self.env.ref('website.website_visitor_page_action').read()[0]
action['domain'] = [('visitor_id', 'in', visitors.ids)]
# avoid grouping if only few records
if len(visitors.visitor_page_ids.ids) > 15 and len(visitors.page_ids.ids) > 1:
if len(visitors.website_track_ids.ids) > 15 and len(visitors.page_ids.ids) > 1:
action['context'] = {'search_default_group_by_page': '1'}
return action
@@ -14,3 +14,47 @@ class WebsiteVisitor(models.Model):
def _compute_lead_count(self):
for visitor in self:
visitor.lead_count = len(visitor.lead_ids)
@api.depends('user_partner_id.email_normalized', 'user_partner_id.mobile', 'lead_ids.email_normalized', 'lead_ids.mobile')
def _compute_email_phone(self):
super(WebsiteVisitor, self)._compute_email_phone()
self.flush()
sql = """ SELECT v.id as visitor_id, l.id as lead_id,
CASE WHEN p.email_normalized is not null THEN p.email_normalized ELSE l.email_normalized END as email,
CASE WHEN p.mobile is not null THEN p.mobile WHEN l.mobile is not null THEN l.mobile ELSE l.phone END as mobile
FROM website_visitor v
JOIN crm_lead_website_visitor_rel lv on lv.website_visitor_id = v.id
JOIN crm_lead l ON lv.crm_lead_id = l.id
LEFT JOIN res_partner p on p.id = v.user_partner_id
WHERE v.id in %s
ORDER BY l.create_date ASC"""
self.env.cr.execute(sql, (tuple(self.ids),))
results = self.env.cr.dictfetchall()
mapped_data = {}
for result in results:
visitor_info = mapped_data.get(result['visitor_id'], {'email': '', 'mobile': ''})
visitor_info['email'] += result['email'] + ','
visitor_info['mobile'] = result['mobile']
mapped_data[result['visitor_id']] = visitor_info
for visitor in self:
email = mapped_data.get(visitor.id, {}).get('email')
visitor.email = email[:-1] if email else False
visitor.mobile = mapped_data.get(visitor.id, {}).get('mobile')
def _prepare_visitor_send_mail_values(self):
visitor_mail_values = super(WebsiteVisitor, self)._prepare_visitor_send_mail_values()
if self.lead_ids:
lead = self.lead_ids._sort_by_confidence_level(reverse=True)[0]
partner_id = self.user_partner_id.id
if not self.user_partner_id:
partner_id = lead.handle_partner_assignation()[lead.id]
if not lead.partner_id:
lead.partner_id = partner_id
self.user_partner_id = partner_id
return {
'res_model': 'crm.lead',
'res_id': lead.id,
'partner_ids': [partner_id],
}
return visitor_mail_values
@@ -1,2 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_website_visitor_salesman,access_website_visitor_salesman,model_website_visitor,sales_team.group_sale_salesman,1,0,0,0
access_website_visitor_page_salesman,access_website_visitor_page_salesman,model_website_visitor_page,sales_team.group_sale_salesman,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_website_visitor_salesman access_website_visitor_salesman model_website_visitor sales_team.group_sale_salesman 1 0 0 0
3 access_website_visitor_page_salesman access_website_visitor_page_salesman model_website_visitor_page sales_team.group_sale_salesman 1 0 0 0
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+14
View File
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Send SMS to Visitor with leads',
'category': 'Website',
'sequence': 54,
'summary': 'Allows to send sms to website visitor that have lead',
'version': '1.0',
'description': """Allows to send sms to website visitor if the visitor is linked to a lead.""",
'depends': ['website_sms', 'crm'],
'data': [],
'installable': True,
'auto_install': True,
}
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import website_visitor
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class WebsiteVisitor(models.Model):
_inherit = 'website.visitor'
def _prepare_visitor_send_sms_values(self):
visitor_sms_values = super(WebsiteVisitor, self)._prepare_visitor_send_sms_values()
if not visitor_sms_values:
leads_with_number = self.lead_ids.filtered(lambda l: l.mobile == self.mobile or l.phone == self.mobile)._sort_by_confidence_level(reverse=True)
if leads_with_number:
lead = leads_with_number[0]
return {
'res_model': 'crm.lead',
'res_id': lead.id,
'partner_ids': [lead.id],
'number_field_name': 'mobile' if lead.mobile == self.mobile else 'phone',
}
return visitor_sms_values
+1 -1
View File
@@ -64,7 +64,7 @@ class WebsiteLivechat(http.Controller):
in conversation with an operator, it's not possible to send the visitor a chat request."""
mail_channel = request.env['mail.channel'].sudo().search([('uuid', '=', uuid)])
if mail_channel:
mail_channel.close_livechat_request_session(_('has left the conversation.'))
mail_channel.close_livechat_request_session()
@http.route('/im_livechat/close_empty_livechat', type='json', auth="public")
def close_empty_livechat(self, uuid):
@@ -20,6 +20,6 @@ class ImLivechatChannel(models.Model):
# As chat requested by the visitor, delete the chat requested by an operator if any to avoid conflicts between two flows
chat_request_channel = self.env['mail.channel'].sudo().search([('livechat_visitor_id', '=', visitor_sudo.id), ('livechat_active', '=', True)])
for mail_channel in chat_request_channel:
mail_channel.close_livechat_request_session(_('has started a conversation with %s. The chat request has been canceled.') % operator.name)
mail_channel.close_livechat_request_session(type='cancel', speaking_with=operator.name)
return mail_channel_vals
+12 -3
View File
@@ -58,12 +58,20 @@ class MailChannel(models.Model):
break
return ' → '.join(history)
def close_livechat_request_session(self, message):
def close_livechat_request_session(self, type='leave', **kwargs):
""" Set deactivate the livechat channel and notify (the operator) the reason of closing the session."""
self.ensure_one()
if self.livechat_active:
self.livechat_active = False
# avoid useless notification if the channel is empty
if not self.channel_message_ids:
return
# Notify that the visitor has left the conversation
name = _('The visitor') if not self.livechat_visitor_id else self.livechat_visitor_id.name
if type == 'cancel':
message = _('has started a conversation with %s. The chat request has been canceled.') % kwargs.get('speaking_with', 'an operator')
else:
message = _('has left the conversation.')
leave_message = '%s %s' % (name, message)
self.message_post(author_id=self.env.ref('base.user_root').sudo().partner_id.id,
body=leave_message, message_type='comment', subtype='mt_comment')
@@ -76,7 +84,8 @@ class MailChannel(models.Model):
message_author_id = message.author_id
visitor = self.livechat_visitor_id
if len(self) == 1 and visitor and message_author_id != self.livechat_operator_id:
visitor.sudo().write({
'last_connection_datetime': fields.datetime.now(),
self.env['website.visitor.lastconnection'].create({
'visitor_id': visitor.id,
'connection_datetime': fields.datetime.now()
})
return message
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_im_livechat_channel_public,im_livechat.channel.public,im_livechat.model_im_livechat_channel,,1,0,0,0
access_website_visitor_livechat_users,website.visitor.livechat.users,model_website_visitor,im_livechat.im_livechat_group_user,1,0,0,0
access_website_track_livechat_users,website.track.livechat.users,website.model_website_track,im_livechat.im_livechat_group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_im_livechat_channel_public im_livechat.channel.public im_livechat.model_im_livechat_channel 1 0 0 0
3 access_website_visitor_livechat_users website.visitor.livechat.users model_website_visitor im_livechat.im_livechat_group_user 1 0 0 0
4 access_website_track_livechat_users website.track.livechat.users website.model_website_track im_livechat.im_livechat_group_user 1 0 0 0
@@ -32,7 +32,7 @@
<div class="o_thread_message_sidebar align-self-center">
<div class="o_thread_message_sidebar_image">
<img alt=""
t-att-src="visitor.partner_id ? '/web/image/res.partner/' + visitor.partner_id + '/image_small' : '/mail/static/src/img/smiley/avatar.jpg'"
t-att-src="visitor.partner_id ? '/web/image/res.partner/' + visitor.partner_id + '/image_64' : '/mail/static/src/img/smiley/avatar.jpg'"
class="o_thread_message_avatar rounded-circle"/>
<span>
<i t-if="visitor.is_connected" aria-label="Visitor is connected" class="o_w_visitor_status o_w_visitor_connected fa fa-circle" role="img" title="Online"></i>
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+16
View File
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Send SMS to Visitor',
'category': 'Website',
'sequence': 54,
'summary': 'Allows to send sms to website visitor',
'version': '1.0',
'description': """Allows to send sms to website visitor if the visitor is linked to a partner.""",
'depends': ['website', 'sms'],
'data': [
'views/website_visitor_views.xml',
],
'installable': True,
'auto_install': True,
}
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import website_visitor
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, _
from odoo.exceptions import UserError
class WebsiteVisitor(models.Model):
_inherit = 'website.visitor'
def _prepare_visitor_send_sms_values(self):
if self.user_partner_id.mobile:
return {
'res_model': 'res.partner',
'res_id': self.user_partner_id.id,
'partner_ids': [self.user_partner_id.id],
'number_field_name': 'mobile',
}
return {}
def action_send_sms(self):
self.ensure_one()
visitor_sms_values = self._prepare_visitor_send_sms_values()
if not visitor_sms_values:
raise UserError(_("There is no mobile phone number linked to this visitor."))
context = dict(self.env.context)
context.update({
'default_res_model': visitor_sms_values['res_model'],
'default_res_id': visitor_sms_values['res_id'],
'default_composition_mode': 'comment',
'default_number_field_name': visitor_sms_values['number_field_name'],
})
return {
"type": "ir.actions.act_window",
"res_model": "sms.composer",
"view_mode": 'form',
"context": context,
"name": "Send SMS Text Message",
"target": "new",
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<record id="website_visitor_view_form" model="ir.ui.view">
<field name="name">website.visitor.view.form.inherit.website.mass.mailing.sms</field>
<field name="model">website.visitor</field>
<field name="inherit_id" ref="website.website_visitor_view_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="action_send_sms" type="object" class="btn btn-primary"
attrs="{'invisible': [('mobile', '=', False)]}" string="Send SMS"/>
</xpath>
</field>
</record>
</data></odoo>