[FIX] im_livechat, *: fix channel created before user interaction
*: website_livechat. Before this PR, a `mail.channel` record would have been created before any user interaction. Since the introduction of the welcome bot, this issue has gotten worse. Indeed, any user accessing a page with the bot enabled created a useless channel. Before 15.3, around 15k channels were created each month, after 15.3, 300-500k channels are created each month, most of them empty channels whom creation could have been avoided. This PR fixes the issue by waiting the first user interaction before creating the channel. closes odoo/odoo#108566 X-original-commit: b2eaf32ba92e537c7eefba6489533613be1b8c91 Signed-off-by: Stockbauer Matthieu (tsm) <tsm@odoo.com> Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -113,7 +113,7 @@ class LivechatController(http.Controller):
|
||||
).get_response()
|
||||
|
||||
@http.route('/im_livechat/get_session', type="json", auth='public', cors="*")
|
||||
def get_session(self, channel_id, anonymous_name, previous_operator_id=None, chatbot_script_id=None, **kwargs):
|
||||
def get_session(self, channel_id, anonymous_name, previous_operator_id=None, chatbot_script_id=None, persisted=True, **kwargs):
|
||||
user_id = None
|
||||
country_id = None
|
||||
# if the user is identifiy (eg: portal user on the frontend), don't use the anonymous name. The user will be added to session.
|
||||
@@ -141,7 +141,9 @@ class LivechatController(http.Controller):
|
||||
previous_operator_id=previous_operator_id,
|
||||
chatbot_script=chatbot_script,
|
||||
user_id=user_id,
|
||||
country_id=country_id)
|
||||
country_id=country_id,
|
||||
persisted=persisted
|
||||
)
|
||||
|
||||
@http.route('/im_livechat/feedback', type='json', auth='public', cors="*")
|
||||
def feedback(self, uuid, rate, reason=None, **kwargs):
|
||||
|
||||
@@ -1177,6 +1177,13 @@ msgstr ""
|
||||
msgid "No history found"
|
||||
msgstr ""
|
||||
|
||||
#. module: im_livechat
|
||||
#. odoo-javascript
|
||||
#: code:addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.xml:0
|
||||
#, python-format
|
||||
msgid "No operator available"
|
||||
msgstr ""
|
||||
|
||||
#. module: im_livechat
|
||||
#. openerp-web
|
||||
#: code:addons/im_livechat/static/src/legacy/widgets/public_livechat_view/public_livechat_view.xml:0
|
||||
|
||||
@@ -161,14 +161,15 @@ class ImLivechatChannel(models.Model):
|
||||
'name': name,
|
||||
}
|
||||
|
||||
def _open_livechat_mail_channel(self, anonymous_name, previous_operator_id=None, chatbot_script=None, user_id=None, country_id=None):
|
||||
""" Return a mail.channel given a livechat channel. It creates one with a connected operator or with Odoobot as
|
||||
def _open_livechat_mail_channel(self, anonymous_name, previous_operator_id=None, chatbot_script=None, user_id=None, country_id=None, persisted=True):
|
||||
""" Return a livechat session. If the session is persisted, creates a mail.channel record with a connected operator or with Odoobot as
|
||||
an operator if a chatbot has been configured, or return false otherwise
|
||||
:param anonymous_name : the name of the anonymous person of the channel
|
||||
:param anonymous_name : the name of the anonymous person of the session
|
||||
:param previous_operator_id : partner_id.id of the previous operator that this visitor had in the past
|
||||
:param chatbot_script : chatbot script if there is one configured
|
||||
:param user_id : the id of the logged in visitor, if any
|
||||
:param country_code : the country of the anonymous person of the channel
|
||||
:param country_code : the country of the anonymous person of the session
|
||||
:param persisted: whether or not the session should be persisted
|
||||
:type anonymous_name : str
|
||||
:return : channel header
|
||||
:rtype : dict
|
||||
@@ -192,13 +193,23 @@ class ImLivechatChannel(models.Model):
|
||||
if not user_operator and not chatbot_script:
|
||||
# no one available
|
||||
return False
|
||||
|
||||
# create the session, and add the link with the given channel
|
||||
mail_channel_vals = self._get_livechat_mail_channel_vals(anonymous_name, user_operator, chatbot_script, user_id=user_id, country_id=country_id)
|
||||
mail_channel = self.env["mail.channel"].with_context(mail_create_nosubscribe=False).sudo().create(mail_channel_vals)
|
||||
if user_operator:
|
||||
mail_channel._broadcast([user_operator.partner_id.id])
|
||||
return mail_channel.sudo().channel_info()[0]
|
||||
if persisted:
|
||||
# create the session, and add the link with the given channel
|
||||
mail_channel = self.env["mail.channel"].with_context(mail_create_nosubscribe=False).sudo().create(mail_channel_vals)
|
||||
if user_operator:
|
||||
mail_channel._broadcast([user_operator.partner_id.id])
|
||||
return mail_channel.sudo().channel_info()[0]
|
||||
else:
|
||||
operator_partner_id = user_operator.partner_id if user_operator else chatbot_script.operator_partner_id
|
||||
display_name = operator_partner_id.user_livechat_username or operator_partner_id.display_name
|
||||
return {
|
||||
'name': mail_channel_vals['name'],
|
||||
'chatbot_current_step_id': mail_channel_vals['chatbot_current_step_id'],
|
||||
'state': 'open',
|
||||
'operator_pid': (operator_partner_id.id, display_name.replace(',', '')),
|
||||
'chatbot_script_id': chatbot_script.id if chatbot_script else None
|
||||
}
|
||||
|
||||
def _get_random_operator(self):
|
||||
""" Return a random operator from the available users of the channel that have the lowest number of active livechats.
|
||||
|
||||
@@ -289,6 +289,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
*/
|
||||
toData() {
|
||||
return {
|
||||
chatbot_script_id: this.messaging.publicLivechatGlobal.publicLivechat.data.chatbot_script_id,
|
||||
folded: this.messaging.publicLivechatGlobal.publicLivechat.isFolded,
|
||||
id: this.messaging.publicLivechatGlobal.publicLivechat.id,
|
||||
message_unread_counter: this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter,
|
||||
@@ -338,6 +339,11 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
* otherwise
|
||||
*/
|
||||
_notifyMyselfTyping(params) {
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.isTemporary) {
|
||||
// channel is not created yet, it will be when first message is
|
||||
// sent. Until then, do not notify visitor is typing.
|
||||
return;
|
||||
}
|
||||
return session.rpc('/im_livechat/notify_typing', {
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
is_typing: params.typing,
|
||||
|
||||
@@ -27,7 +27,12 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
_prepareGetSessionParameters() {
|
||||
const parameters = this._super(...arguments);
|
||||
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
const { publicLivechat } = this.messaging.publicLivechatGlobal;
|
||||
if (publicLivechat && publicLivechat.isTemporary && !publicLivechat.data.chatbot_script_id) {
|
||||
return parameters;
|
||||
} else if (publicLivechat && publicLivechat.data.chatbot_script_id) {
|
||||
parameters.chatbot_script_id = publicLivechat.data.chatbot_script_id;
|
||||
} else if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
parameters.chatbot_script_id = this.messaging.publicLivechatGlobal.chatbot.scriptId;
|
||||
}
|
||||
|
||||
|
||||
+29
-5
@@ -52,10 +52,6 @@ const PublicLivechatWindow = Widget.extend({
|
||||
} else {
|
||||
this._focusInput();
|
||||
}
|
||||
if (!config.device.isMobile) {
|
||||
const margin_dir = _t.database.parameters.direction === "rtl" ? "margin-left" : "margin-right";
|
||||
this.$el.css(margin_dir, $.position.scrollbarWidth());
|
||||
}
|
||||
const def = this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.replace(this.$('.o_thread_window_content')).then(() => {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.$el.on('scroll', this, this._debouncedOnScroll);
|
||||
});
|
||||
@@ -115,6 +111,29 @@ const PublicLivechatWindow = Widget.extend({
|
||||
renderHeader() {
|
||||
this.$header.html(qweb.render('im_livechat.legacy.PublicLivechatWindow.HeaderContent', { widget: this }));
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the chat window itself.
|
||||
*/
|
||||
renderChatWindow() {
|
||||
this.renderElement();
|
||||
this.adjustPosition();
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute position of this chat window and apply corresponding styles to
|
||||
* the underlying widget.
|
||||
*/
|
||||
adjustPosition() {
|
||||
const cssProps = { bottom: 0 };
|
||||
cssProps[this.messaging.locale.textDirection === 'rtl' ? 'left' : 'right'] = 0;
|
||||
if (!config.device.isMobile) {
|
||||
const margin_dir = _t.database.parameters.direction === "rtl" ? "margin-left" : "margin-right";
|
||||
cssProps[margin_dir] = $.position.scrollbarWidth();
|
||||
}
|
||||
this.$el.css(cssProps);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace the thread content with provided new content
|
||||
*
|
||||
@@ -138,7 +157,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
folded = !this.messaging.publicLivechatGlobal.publicLivechat.isFolded;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: folded });
|
||||
setCookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60, 'required');
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.operator) {
|
||||
setCookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60, 'required');
|
||||
}
|
||||
this.updateVisualFoldState();
|
||||
},
|
||||
/**
|
||||
@@ -198,6 +219,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
} catch {
|
||||
await this.messaging.publicLivechatGlobal.livechatButtonView.sendMessage(messageData); // try again just in case
|
||||
}
|
||||
if (!this.messaging.publicLivechatGlobal.publicLivechat.operator) {
|
||||
return;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.postMessage(messageData)
|
||||
.then(() => {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
|
||||
+10
-5
@@ -16,14 +16,19 @@
|
||||
<t t-set="thread" t-value="widget.messaging.publicLivechatGlobal.publicLivechat.widget"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_thread_window_content">
|
||||
</div>
|
||||
<div class="o_thread_composer o_chat_mini_composer">
|
||||
<input class="o_composer_text_field o_PublicLivechatWindow_composer" t-att-placeholder="widget.messaging.publicLivechatGlobal.chatWindow.inputPlaceholder"/>
|
||||
<t t-if="widget.messaging.publicLivechatGlobal.publicLivechat.operator">
|
||||
<div class="o_thread_window_content">
|
||||
</div>
|
||||
<div class="o_thread_composer o_chat_mini_composer">
|
||||
<input class="o_composer_text_field o_PublicLivechatWindow_composer" t-att-placeholder="widget.messaging.publicLivechatGlobal.chatWindow.inputPlaceholder"/>
|
||||
</div>
|
||||
</t>
|
||||
<div t-else="" class="d-flex justify-content-center align-items-center flex-grow-1">
|
||||
<p class="text-500">No operator available</p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
<!--
|
||||
@param {im_livechat/legacy/widgets/public_livechat_window/public_livechat_window} widget
|
||||
-->
|
||||
|
||||
@@ -402,6 +402,13 @@ Model({
|
||||
* display that restart button.
|
||||
*/
|
||||
compute() {
|
||||
const { publicLivechat } = this.messaging.publicLivechatGlobal;
|
||||
if (publicLivechat && !publicLivechat.operator) {
|
||||
return false;
|
||||
}
|
||||
if (publicLivechat && !publicLivechat.data.chatbot_script_id) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(
|
||||
!this.currentStep ||
|
||||
(
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
import { attr, clear, one, Model } from '@mail/model';
|
||||
|
||||
import {unaccent} from 'web.utils';
|
||||
import {getCookie, setCookie, deleteCookie} from 'web.utils.cookies';
|
||||
import {getCookie, deleteCookie} from 'web.utils.cookies';
|
||||
|
||||
Model({
|
||||
name: 'LivechatButtonView',
|
||||
@@ -129,9 +128,7 @@ Model({
|
||||
async openChatWindow() {
|
||||
this.messaging.publicLivechatGlobal.update({ chatWindow: {} });
|
||||
await this.messaging.publicLivechatGlobal.chatWindow.widget.appendTo($('body'));
|
||||
const cssProps = { bottom: 0 };
|
||||
cssProps[this.messaging.locale.textDirection === 'rtl' ? 'left' : 'right'] = 0;
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$el.css(cssProps);
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.adjustPosition();
|
||||
this.widget.$el.hide();
|
||||
this._openChatWindowChatbot();
|
||||
},
|
||||
@@ -139,6 +136,12 @@ Model({
|
||||
* @param {Object} message
|
||||
*/
|
||||
async sendMessage(message) {
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.isTemporary) {
|
||||
await this.messaging.publicLivechatGlobal.publicLivechat.createLivechatChannel();
|
||||
if (!this.messaging.publicLivechatGlobal.publicLivechat.operator) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await this._sendMessageChatbotBefore();
|
||||
await this._sendMessage(message);
|
||||
this._sendMessageChatbotAfter();
|
||||
@@ -194,7 +197,10 @@ Model({
|
||||
this.messaging.publicLivechatGlobal.update({ messages: clear() });
|
||||
def = this.messaging.rpc({
|
||||
route: '/im_livechat/get_session',
|
||||
params: this.widget._prepareGetSessionParameters(),
|
||||
params: {
|
||||
...this.widget._prepareGetSessionParameters(),
|
||||
persisted: false,
|
||||
},
|
||||
}, { silent: true });
|
||||
}
|
||||
def.then((livechatData) => {
|
||||
@@ -223,15 +229,7 @@ Model({
|
||||
this.widget._sendWelcomeMessage();
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
this.messaging.publicLivechatGlobal.update({ notificationHandler: {} });
|
||||
|
||||
setCookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60, 'required');
|
||||
setCookie('im_livechat_auto_popup', JSON.stringify(false), 60 * 60, 'optional');
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.operator) {
|
||||
const operatorPidId = this.messaging.publicLivechatGlobal.publicLivechat.operator.id;
|
||||
const oneWeek = 7 * 24 * 60 * 60;
|
||||
setCookie('im_livechat_previous_operator_pid', operatorPidId, oneWeek, 'optional');
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.updateSessionCookie();
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
|
||||
@@ -4,6 +4,9 @@ import PublicLivechat from '@im_livechat/legacy/models/public_livechat';
|
||||
|
||||
import { attr, clear, one, Model } from '@mail/model';
|
||||
|
||||
import { unaccent } from 'web.utils';
|
||||
import { deleteCookie, setCookie } from 'web.utils.cookies';
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechat',
|
||||
lifecycleHooks: {
|
||||
@@ -19,6 +22,38 @@ Model({
|
||||
this.widget.destroy();
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
async createLivechatChannel() {
|
||||
const livechatData = await this.messaging.rpc({
|
||||
route: "/im_livechat/get_session",
|
||||
params: this.messaging.publicLivechatGlobal.livechatButtonView.widget._prepareGetSessionParameters(),
|
||||
});
|
||||
if (!livechatData || !livechatData.operator_pid) {
|
||||
this.update({ data: clear() });
|
||||
deleteCookie("im_livechat_session");
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.renderChatWindow();
|
||||
} else {
|
||||
this.update({ data: livechatData });
|
||||
this.widget.data = livechatData;
|
||||
this.updateSessionCookie();
|
||||
}
|
||||
},
|
||||
updateSessionCookie() {
|
||||
deleteCookie("im_livechat_session");
|
||||
setCookie(
|
||||
"im_livechat_session",
|
||||
unaccent(JSON.stringify(this.widget.toData()), true),
|
||||
60 * 60,
|
||||
"required"
|
||||
);
|
||||
setCookie("im_livechat_auto_popup", JSON.stringify(false), 60 * 60, "optional");
|
||||
if (this.operator) {
|
||||
const operatorPidId = this.operator.id;
|
||||
const oneWeek = 7 * 24 * 60 * 60;
|
||||
setCookie("im_livechat_previous_operator_pid", operatorPidId, oneWeek, "optional");
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
data: attr(),
|
||||
id: attr({
|
||||
@@ -32,13 +67,21 @@ Model({
|
||||
isFolded: attr({
|
||||
default: false,
|
||||
}),
|
||||
isTemporary: attr({
|
||||
compute() {
|
||||
if (!this.data || !this.data.id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
identifying: true,
|
||||
inverse: 'publicLivechat',
|
||||
}),
|
||||
name: attr({
|
||||
compute() {
|
||||
if (!this.data) {
|
||||
if (!this.data || !this.operator) {
|
||||
return clear();
|
||||
}
|
||||
return this.data.name;
|
||||
|
||||
@@ -42,12 +42,13 @@ Model({
|
||||
await this._willStartChatbot();
|
||||
},
|
||||
async _willStart() {
|
||||
const cookie = getCookie('im_livechat_session');
|
||||
if (cookie) {
|
||||
const channel = JSON.parse(cookie);
|
||||
const strCookie = getCookie('im_livechat_session');
|
||||
const isSessionCookieAvailable = Boolean(strCookie);
|
||||
const cookie = JSON.parse(strCookie || '{}');
|
||||
if (cookie.id) {
|
||||
const history = await this.messaging.rpc({
|
||||
route: '/mail/chat_history',
|
||||
params: { uuid: channel.uuid, limit: 100 },
|
||||
params: { uuid: cookie.uuid, limit: 100 },
|
||||
});
|
||||
history.reverse();
|
||||
this.update({ history });
|
||||
@@ -55,6 +56,8 @@ Model({
|
||||
message.body = Markup(message.body);
|
||||
}
|
||||
this.update({ isAvailableForMe: true });
|
||||
} else if (isSessionCookieAvailable) {
|
||||
this.update({ history: [], isAvailableForMe: true });
|
||||
} else {
|
||||
const result = await this.messaging.rpc({
|
||||
route: '/im_livechat/init',
|
||||
@@ -185,6 +188,9 @@ Model({
|
||||
if (!this.publicLivechat) {
|
||||
return clear();
|
||||
}
|
||||
if (!this.publicLivechat.operator) {
|
||||
return clear();
|
||||
}
|
||||
return this.lastMessage.authorId !== this.publicLivechat.operator.id;
|
||||
},
|
||||
default: false,
|
||||
@@ -219,6 +225,12 @@ Model({
|
||||
messages: many('PublicLivechatMessage'),
|
||||
notificationHandler: one('PublicLivechatGlobalNotificationHandler', {
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
compute() {
|
||||
if (this.publicLivechat && !this.publicLivechat.isTemporary) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
}
|
||||
}),
|
||||
options: attr({
|
||||
default: {},
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import tour from "web_tour.tour";
|
||||
|
||||
const requestChatSteps = [
|
||||
{
|
||||
trigger: ".o_livechat_button",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".o_thread_window",
|
||||
},
|
||||
];
|
||||
|
||||
tour.register("im_livechat_request_chat", { test: true }, requestChatSteps);
|
||||
|
||||
tour.register("im_livechat_request_chat_and_send_message", { test: true }, [
|
||||
...requestChatSteps,
|
||||
{
|
||||
trigger: ".o_composer_text_field",
|
||||
run: "text Hello, I need help please !",
|
||||
},
|
||||
{
|
||||
trigger: '.o_composer_text_field',
|
||||
run() {
|
||||
$(".o_composer_text_field").trigger($.Event("keydown", { which: 13 }));
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger: ".o_thread_message:contains('Hello, I need help')",
|
||||
},
|
||||
]);
|
||||
@@ -60,12 +60,12 @@ class WebsiteLivechat(LivechatController):
|
||||
return request.render("website_livechat.channel_page", values)
|
||||
|
||||
@http.route('/im_livechat/get_session', type="json", auth='public', cors="*")
|
||||
def get_session(self, channel_id, anonymous_name, previous_operator_id=None, chatbot_script_id=None, **kwargs):
|
||||
def get_session(self, channel_id, anonymous_name, previous_operator_id=None, chatbot_script_id=None, persisted=True, **kwargs):
|
||||
""" Override to use visitor name instead of 'Visitor' whenever a visitor start a livechat session. """
|
||||
visitor_sudo = request.env['website.visitor']._get_visitor_from_request()
|
||||
if visitor_sudo:
|
||||
anonymous_name = visitor_sudo.with_context(lang=visitor_sudo.lang_id.code).display_name
|
||||
return super(WebsiteLivechat, self).get_session(channel_id, anonymous_name, previous_operator_id=previous_operator_id, chatbot_script_id=chatbot_script_id, **kwargs)
|
||||
return super(WebsiteLivechat, self).get_session(channel_id, anonymous_name, previous_operator_id=previous_operator_id, chatbot_script_id=chatbot_script_id, persisted=persisted, **kwargs)
|
||||
|
||||
def _livechat_templates_get(self):
|
||||
return super(WebsiteLivechat, self)._livechat_templates_get() + [
|
||||
|
||||
@@ -9,6 +9,14 @@ from odoo.addons.website_livechat.tests.common import TestLivechatCommon
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestLivechatBasicFlowHttpCase(tests.HttpCase, TestLivechatCommon):
|
||||
def test_channel_created_on_user_interaction(self):
|
||||
self.start_tour('/', 'im_livechat_request_chat', login=None)
|
||||
channel = self.env['mail.channel'].search([['livechat_active', '=', True], ['livechat_visitor_id', '=', self.visitor.id]])
|
||||
self.assertFalse(channel, 'Channel should not be created until user sends a message')
|
||||
self.start_tour('/', 'im_livechat_request_chat_and_send_message', login=None)
|
||||
channel = self.env['mail.channel'].search([['livechat_active', '=', True], ['livechat_visitor_id', '=', self.visitor.id]])
|
||||
self.assertTrue(channel, 'Channel should be created after sending the first message')
|
||||
|
||||
def test_visitor_banner_history(self):
|
||||
# create visitor history
|
||||
self.env['website.track'].create([{
|
||||
|
||||
Reference in New Issue
Block a user