19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:30:53 +01:00
parent dc68f80d3f
commit 7221b9ac46
610 changed files with 135477 additions and 161677 deletions

View file

@ -0,0 +1,8 @@
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
registry.category("discuss.channel_commands").add("lead", {
condition: ({ store }) => store.has_access_livechat,
help: _t("Create a new lead (/lead lead title)"),
methodName: "execute_command_lead",
});

View file

@ -0,0 +1,16 @@
import { patch } from "@web/core/utils/patch";
import { ThreadAction } from "@mail/core/common/thread_actions";
patch(ThreadAction.prototype, {
_condition({ action, owner, store, thread }) {
if (
action.id === "create-lead" &&
thread?.channel_type === "livechat" &&
store.has_access_create_lead &&
!owner.isDiscussSidebarChannelActions
) {
return true;
}
return super._condition(...arguments);
},
});

View file

@ -0,0 +1,40 @@
import { LivechatCommandDialog } from "@im_livechat/core/common/livechat_command_dialog";
import { registerThreadAction } from "@mail/core/common/thread_actions";
import "@mail/discuss/call/common/thread_actions";
import { _t } from "@web/core/l10n/translation";
import { usePopover } from "@web/core/popover/popover_hook";
registerThreadAction("create-lead", {
actionPanelComponent: LivechatCommandDialog,
actionPanelComponentProps: ({ action }) => ({
close: () => action.close(),
commandName: "lead",
placeholderText: _t("e.g. Product pricing"),
title: _t("Create Lead"),
icon: "fa fa-handshake-o",
}),
close: ({ action }) => action.popover?.close(),
condition: false, // managed by ThreadAction patch
panelOuterClass: "bg-100",
icon: "fa fa-handshake-o",
name: _t("Create Lead"),
sequence: 10,
sequenceGroup: 25,
setup({ owner }) {
if (!owner.env.inChatWindow) {
this.popover = usePopover(LivechatCommandDialog, {
onClose: () => this.close(),
popoverClass: this.panelOuterClass,
});
}
},
toggle: true,
open({ owner, thread }) {
this.popover?.open(owner.root.el.querySelector(`[name="${this.id}"]`), {
thread,
...this.actionPanelComponentProps,
});
},
});

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="website_livechat.LivechatChannelInfoList" t-inherit="im_livechat.LivechatChannelInfoList" t-inherit-mode="extension">
<xpath expr="//t[@t-name='extra_infos']" position="inside">
<t t-if="props.thread.livechatVisitorMember?.partner_id?.opportunity_ids?.length" t-call="im_livechat.LivechatChannelInfoList.info_links">
<t t-set="title">Open leads</t>
<t t-set="templateParams"
t-value="{
title,
'info_records': props.thread.livechatVisitorMember?.partner_id?.opportunity_ids,
'model': 'crm.lead'
}"
/>
</t>
</xpath>
</t>
</templates>

View file

@ -1,23 +0,0 @@
/** @odoo-module **/
import { registerPatch } from '@mail/model/model_core';
import { insert } from '@mail/model/model_field_command';
registerPatch({
name: 'MessagingInitializer',
recordMethods: {
/**
* @override
*/
_initCommands() {
this._super();
this.messaging.update({
commands: insert({
help: this.env._t("Create a new lead (/lead lead title)"),
methodName: 'execute_command_lead',
name: "lead",
}),
});
},
},
});

View file

@ -0,0 +1,32 @@
import { defineCrmLivechatModels } from "@crm_livechat/../tests/crm_livechat_test_helpers";
import {
click,
insertText,
openDiscuss,
start,
startServer,
} from "@mail/../tests/mail_test_helpers";
import { describe, test } from "@odoo/hoot";
import { asyncStep, onRpc, serverState, waitForSteps } from "@web/../tests/web_test_helpers";
describe.current.tags("desktop");
defineCrmLivechatModels();
test("Can execute lead command", async () => {
const pyEnv = await startServer();
pyEnv["res.users"].write([serverState.userId], {
group_ids: pyEnv["res.groups"]
.search_read([["id", "=", serverState.groupLivechatId]])
.map(({ id }) => id),
});
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
await start();
onRpc("discuss.channel", "execute_command_lead", ({ args }) => {
asyncStep(args[0]);
return true;
});
await openDiscuss(channelId);
await insertText(".o-mail-Composer-input", "/lead great lead");
await click(".o-mail-Composer button[title='Send']:enabled");
await waitForSteps([[channelId]]);
});

View file

@ -0,0 +1,42 @@
import { defineCrmLivechatModels } from "@crm_livechat/../tests/crm_livechat_test_helpers";
import {
click,
contains,
insertText,
openDiscuss,
start,
startServer,
} from "@mail/../tests/mail_test_helpers";
import { describe, test } from "@odoo/hoot";
import { Command, serverState } from "@web/../tests/web_test_helpers";
describe.current.tags("desktop");
defineCrmLivechatModels();
test("can create a lead from the thread action after the conversation ends", async () => {
const pyEnv = await startServer();
const groupId = pyEnv["res.groups"].create({ name: "Sales Team" });
serverState.groupSalesTeamId = groupId;
pyEnv["res.users"].write([serverState.userId], {
group_ids: [Command.link(groupId)],
});
const guestId = pyEnv["mail.guest"].create({ name: "Visitor" });
const channel_id = pyEnv["discuss.channel"].create({
channel_type: "livechat",
channel_member_ids: [
Command.create({ partner_id: serverState.partnerId, livechat_member_type: "agent" }),
Command.create({ guest_id: guestId, livechat_member_type: "visitor" }),
],
livechat_operator_id: serverState.partnerId,
});
await start();
await openDiscuss(channel_id);
await contains(".o-livechat-ChannelInfoList"); // wait for auto-open of this panel
await click(".o-mail-DiscussContent-header button[title='Create Lead']");
await insertText(".o-livechat-LivechatCommandDialog-form input", "testlead");
await click(".o-mail-ActionPanel button", { text: "Create Lead" });
await contains(".o_mail_notification", { text: "Created a new lead: testlead" });
});

View file

@ -0,0 +1,11 @@
import { CrmLead } from "@crm/../tests/mock_server/mock_models/crm_lead";
import { ResUsers } from "@crm_livechat/../tests/mock_server/mock_models/res_users";
import { livechatModels } from "@im_livechat/../tests/livechat_test_helpers";
import { defineModels } from "@web/../tests/web_test_helpers";
export function defineCrmLivechatModels() {
return defineModels({ ...livechatModels, CrmLead, ResUsers });
}

View file

@ -0,0 +1,40 @@
import { defineCrmLivechatModels } from "@crm_livechat/../tests/crm_livechat_test_helpers";
import { describe, test } from "@odoo/hoot";
import {
click,
contains,
insertText,
openDiscuss,
start,
startServer,
} from "@mail/../tests/mail_test_helpers";
import { Command, serverState } from "@web/../tests/web_test_helpers";
describe.current.tags("desktop");
defineCrmLivechatModels();
test("Can open lead from internal link", async () => {
const pyEnv = await startServer();
pyEnv["res.users"].write([serverState.userId], {
group_ids: pyEnv["res.groups"]
.search_read([["id", "=", serverState.groupLivechatId]])
.map(({ id }) => id),
});
const guestId = pyEnv["mail.guest"].create({ name: "Visitor" });
const channelId = pyEnv["discuss.channel"].create({
channel_member_ids: [
Command.create({ partner_id: serverState.partnerId, livechat_member_type: "agent" }),
Command.create({ guest_id: guestId, livechat_member_type: "visitor" }),
],
channel_type: "livechat",
livechat_operator_id: serverState.partnerId,
});
await start();
await openDiscuss(channelId);
await insertText(".o-mail-Composer-input", "/lead My Lead");
await click(".o-mail-Composer button[title='Send']:enabled");
await contains(".o-mail-ChatWindow", { count: 0 });
await click('.o_mail_notification a[data-oe-model="crm.lead"]');
await contains(".o-mail-ChatWindow-header", { text: "Visitor" });
await contains(".o_form_view .o_last_breadcrumb_item span", { text: "My Lead" });
});

View file

@ -0,0 +1,25 @@
import { DiscussChannel } from "@mail/../tests/mock_server/mock_models/discuss_channel";
import { getKwArgs, serverState } from "@web/../tests/web_test_helpers";
import { patch } from "@web/core/utils/patch";
const discussChannelPatch = {
execute_command_lead() {
const kwargs = getKwArgs(arguments, "ids", "body");
const ids = kwargs.ids;
const body = kwargs.body;
const leadName = body.substring("/lead".length).trim();
const leadId = this.env["crm.lead"].create({ name: leadName });
this.env["bus.bus"]._sendone(serverState.partnerId, "discuss.channel/transient_message", {
body: `
<span class="o_mail_notification">
Created a new lead: <a href="#" data-oe-model="crm.lead" data-oe-id="${leadId}">${leadName}</a>
</span>`,
channel_id: ids[0],
});
return true;
},
};
patch(DiscussChannel.prototype, discussChannelPatch);

View file

@ -0,0 +1,12 @@
import { livechatModels } from "@im_livechat/../tests/livechat_test_helpers";
import { serverState } from "@web/../tests/web_test_helpers";
export class ResUsers extends livechatModels.ResUsers {
/** @override */
_init_store_data(store) {
super._init_store_data(...arguments);
store.add({
has_access_create_lead: this.env.user?.group_ids.includes(serverState.groupSalesTeamId),
});
}
}

View file

@ -0,0 +1,37 @@
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("crm_livechat.create_lead_from_chatbot", {
steps: () => [
{
trigger: ".o-livechat-root:shadow .o-livechat-LivechatButton",
run: "click",
},
{
trigger: ".o-livechat-root:shadow .o-mail-Message:contains(Hello, how can I help you?)",
},
{
trigger: ".o-livechat-root:shadow .o-mail-Composer-input",
run: "edit I'd like to know more about the CRM application.",
},
{
trigger: ".o-livechat-root:shadow .o-mail-Composer-input",
run: "press Enter",
},
{
trigger:
".o-livechat-root:shadow .o-mail-Message:contains(Would you mind leaving your email address so that we can reach you back?)",
},
{
trigger: ".o-livechat-root:shadow .o-mail-Composer-input",
run: "edit visitor@example.com",
},
{
trigger: ".o-livechat-root:shadow .o-mail-Composer-input",
run: "press Enter",
},
{
trigger:
".o-livechat-root:shadow .o-mail-Message:contains(Thank you, you should hear back from us very soon!)",
},
],
});