19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:39 +01:00
parent 5df8c07b59
commit daa394e8b0
2114 changed files with 564841 additions and 299642 deletions

View file

@ -0,0 +1,265 @@
import {
mailDataHelpers,
parseRequestParams,
registerRoute,
} from "@mail/../tests/mock_server/mail_mock_server";
import { Command, makeKwArgs, serverState } from "@web/../tests/web_test_helpers";
import { loadBundle } from "@web/core/assets";
import { patch } from "@web/core/utils/patch";
/**
* @template [T={}]
* @typedef {import("@web/../tests/web_test_helpers").RouteCallback<T>} RouteCallback
*/
registerRoute("/im_livechat/get_session", get_session);
/** @type {RouteCallback} */
async function get_session(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
/** @type {import("mock_models").LivechatChannel} */
const LivechatChannel = this.env["im_livechat.channel"];
/** @type {import("mock_models").ResCountry} */
const ResCountry = this.env["res.country"];
/** @type {import("mock_models").ResPartner} */
const ResPartner = this.env["res.partner"];
/** @type {import("mock_models").ResUsers} */
const ResUsers = this.env["res.users"];
let {
channel_id,
previous_operator_id,
persisted,
context = {},
} = await parseRequestParams(request);
previous_operator_id = parseInt(previous_operator_id);
const agent = LivechatChannel._get_operator(channel_id, previous_operator_id);
if (!agent) {
return false;
}
let country_id;
if (this.env.user && !ResUsers._is_public(this.env.uid)) {
country_id = this.env.user.country_id;
} else if (context.mockedCountryCode) {
// simulate geoip
const country = ResCountry._filter([["code", "=", context.mockedCountryCode]])[0];
if (country) {
country_id = country.id;
}
}
if (!persisted) {
const store = new mailDataHelpers.Store();
ResUsers._init_store_data(store);
store.add("discuss.channel", {
channel_type: "livechat",
fetchChannelInfoState: "fetched",
id: -1,
isLoaded: true,
livechat_operator_id: mailDataHelpers.Store.one(
ResPartner.browse(agent.partner_id),
makeKwArgs({ fields: ["avatar_128", "user_livechat_username"] })
),
scrollUnread: false,
});
return { store_data: store.get_result(), channel_id: -1 };
}
const channelVals = LivechatChannel._get_livechat_discuss_channel_vals(channel_id, {
agent: agent,
});
channelVals.country_id = country_id;
const channelId = DiscussChannel.create(channelVals);
const store = new mailDataHelpers.Store();
ResUsers._init_store_data(store);
store.add(DiscussChannel.browse(channelId));
store.add(DiscussChannel.browse(channelId), {
isLoaded: true,
scrollUnread: false,
});
return { store_data: store.get_result(), channel_id: channelId };
}
registerRoute("/im_livechat/visitor_leave_session", visitor_leave_session);
/** @type {RouteCallback} */
async function visitor_leave_session(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
const { channel_id } = await parseRequestParams(request);
const [channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
if (!channel) {
return;
}
DiscussChannel._close_livechat_session(channel_id);
}
registerRoute("/im_livechat/feedback", feedback);
/** @type {RouteCallback} */
async function feedback(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
/** @type {import("mock_models").RatingRating} */
const RatingRating = this.env["rating.rating"];
const { channel_id, rate, reason } = await parseRequestParams(request);
let [channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
if (!channel) {
return false;
}
const values = {
rating: rate,
consumed: true,
feedback: reason,
is_internal: false,
res_id: channel.id,
res_model: "discuss.channel",
rated_partner_id: channel.channel_partner_ids[0],
};
if (channel.rating_ids.length === 0) {
RatingRating.create(values);
} else {
RatingRating.write([channel.rating_ids[0]], values);
}
[channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
return channel.rating_ids[0];
}
registerRoute("/im_livechat/init", livechat_init);
/** @type {RouteCallback} */
async function livechat_init(request) {
return {
available_for_me: true,
rule: {},
};
}
registerRoute("/im_livechat/email_livechat_transcript", email_livechat_transcript);
/** @type {RouteCallback} */
async function email_livechat_transcript(request) {
const DiscussChannel = this.env["discuss.channel"];
const { channel_id, email } = await parseRequestParams(request);
const [channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
if (!channel) {
return;
}
DiscussChannel._email_livechat_transcript(channel_id, email);
}
registerRoute("/im_livechat/emoji_bundle", get_emoji_bundle);
/** @type {RouteCallback} */
async function get_emoji_bundle(request) {
await loadBundle("web.assets_emoji");
return new Response();
}
registerRoute("/im_livechat/session/update_status", session_update_status);
/** @type {RouteCallback} */
async function session_update_status(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
const { channel_id, livechat_status } = await parseRequestParams(request);
if (this.env.user.share) {
return false;
}
const [channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
if (!channel) {
return false;
}
DiscussChannel.write([channel_id], {
livechat_status: livechat_status,
});
return true;
}
registerRoute("/im_livechat/session/update_note", session_update_note);
/** @type {RouteCallback} */
async function session_update_note(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
const { channel_id, note } = await parseRequestParams(request);
if (this.env.user.share) {
return false;
}
const [channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
if (!channel) {
return false;
}
DiscussChannel.write([channel_id], {
livechat_note: note,
});
return true;
}
registerRoute("/im_livechat/conversation/write_expertises", livechat_conversation_write_expertises);
/** @type {RouteCallback} */
async function livechat_conversation_write_expertises(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
const { channel_id, orm_commands } = await parseRequestParams(request);
const [channel] = DiscussChannel.search_read([["id", "=", channel_id]]);
if (!channel) {
return false;
}
DiscussChannel.write(channel_id, { livechat_expertise_ids: orm_commands });
}
registerRoute(
"/im_livechat/conversation/create_and_link_expertise",
livechat_conversation_create_and_link_expertise
);
/** @type {RouteCallback} */
async function livechat_conversation_create_and_link_expertise(request) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
/** @type {import("mock_models").ImLivechatExpertise} */
const ImLivechatExpertise = this.env["im_livechat.expertise"];
const { channel_id, expertise_name } = await parseRequestParams(request);
const [channel] = DiscussChannel.search([["id", "=", channel_id]]);
if (!channel) {
return false;
}
const [expertise] = ImLivechatExpertise.search([["name", "=", expertise_name]]);
let expertiseId = expertise?.id;
if (!expertise) {
expertiseId = ImLivechatExpertise.create({ name: expertise_name });
}
DiscussChannel.write(channel_id, { livechat_expertise_ids: [Command.link(expertiseId)] });
}
patch(mailDataHelpers, {
_process_request_for_all(store, name, params) {
const ResPartner = this.env["res.partner"];
const ResUsers = this.env["res.users"];
super._process_request_for_all(...arguments);
store.add({ livechat_available: true });
if (name === "init_livechat") {
if (this.env.user && !ResUsers._is_public(this.env.uid)) {
store.add(
ResPartner.browse(this.env.user.partner_id),
makeKwArgs({ fields: ["email"] })
);
}
}
},
_process_request_for_internal_user(store, name, params) {
super._process_request_for_internal_user(...arguments);
if (name === "im_livechat.channel") {
const LivechatChannel = this.env["im_livechat.channel"];
store.add(
LivechatChannel.browse(LivechatChannel.search([])),
makeKwArgs({ fields: ["are_you_inside", "name"] })
);
return;
}
if (name === "/im_livechat/looking_for_help") {
const DiscussChannel = this.env["discuss.channel"];
store.add(
DiscussChannel.browse(
DiscussChannel.search([["livechat_status", "=", "need_help"]])
)
);
}
if (name === "/im_livechat/fetch_self_expertise") {
const ResUsers = this.env["res.users"];
store.add(ResUsers.browse(serverState.userId), ["livechat_expertise_ids"]);
}
},
});

View file

@ -0,0 +1,11 @@
declare module "mock_models" {
import { LivechatChannel as LivechatChannel2 } from "../im_livechat_channel";
import { RatingRating as RatingRating2 } from "../rating_rating";
export interface LivechatChannel extends LivechatChannel2 {}
export interface RatingRating extends RatingRating2 {}
export interface Models {
"im_livechat.channel": LivechatChannel,
}
}

View file

@ -0,0 +1,221 @@
import { mailModels } from "@mail/../tests/mail_test_helpers";
import { mailDataHelpers } from "@mail/../tests/mock_server/mail_mock_server";
import { fields, getKwArgs, makeKwArgs, serverState } from "@web/../tests/web_test_helpers";
import { serializeDate } from "@web/core/l10n/dates";
import { ensureArray } from "@web/core/utils/arrays";
export class DiscussChannel extends mailModels.DiscussChannel {
livechat_channel_id = fields.Many2one({ relation: "im_livechat.channel", string: "Channel" }); // FIXME: somehow not fetched properly
livechat_lang_id = fields.Many2one({ relation: "res.lang", string: "Language" });
livechat_note = fields.Html({ sanitize: true });
livechat_status = fields.Selection({
selection: [
("in_progress", "In progress"),
("waiting", "Waiting for customer"),
("need_help", "Looking for help"),
],
});
livechat_expertise_ids = fields.Many2many({
relation: "im_livechat.expertise",
});
action_unfollow(idOrIds) {
/** @type {import("mock_models").BusBus} */
const BusBus = this.env["bus.bus"];
const ids = ensureArray(idOrIds);
for (const channel_id of ids) {
const [channel] = this.browse(channel_id);
if (channel.channel_type == "livechat" && channel.channel_member_ids.length <= 2) {
this.write([channel.id], { livechat_end_dt: serializeDate(luxon.DateTime.now()) });
BusBus._sendone(
channel,
"mail.record/insert",
new mailDataHelpers.Store()
.add(this.browse(channel_id), makeKwArgs({ fields: ["livechat_end_dt"] }))
.get_result()
);
}
}
return super.action_unfollow(...arguments);
}
/**
* @override
* @param {number[]} ids
* @param {number[]} partner_ids
* @param {boolean} [invite_to_rtc_call=undefined]
*/
add_members(ids, partner_ids, invite_to_rtc_call) {
const kwargs = getKwArgs(arguments, "ids", "partner_ids", "invite_to_rtc_call");
ids = kwargs.ids;
delete kwargs.ids;
partner_ids = kwargs.partner_ids || [];
const channels = this.browse(
Array.from(super.add_members(ids, partner_ids, invite_to_rtc_call)).map(
({ channel_id }) => channel_id
)
);
for (const channel of channels) {
if (channel.livechat_status == "need_help") {
this.write([channel.id], { livechat_status: "in_progress" });
}
}
}
_channel_basic_info_fields() {
return [
...super._channel_basic_info_fields(),
"livechat_lang_id",
"livechat_note",
"livechat_status",
"livechat_expertise_ids",
];
}
/**
* @override
* @type {typeof mailModels.DiscussChannel["prototype"]["_to_store"]}
*/
_to_store(store) {
/** @type {import("mock_models").ResCountry} */
const ResCountry = this.env["res.country"];
/** @type {import("mock_models").ResLang} */
const ResLang = this.env["res.lang"];
/** @type {import("mock_models").ResPartner} */
const ResPartner = this.env["res.partner"];
super._to_store(...arguments);
for (const channel of this) {
const channelInfo = {};
const [country] = ResCountry.browse(channel.country_id);
channelInfo["country_id"] = country
? {
code: country.code,
id: country.id,
name: country.name,
}
: false;
// add the last message date
if (channel.channel_type === "livechat") {
// add the operator id
if (channel.livechat_operator_id) {
// livechat_username ignored for simplicity
channelInfo.livechat_operator_id = mailDataHelpers.Store.one(
ResPartner.browse(channel.livechat_operator_id),
makeKwArgs({ fields: ["avatar_128", "user_livechat_username"] })
);
} else {
channelInfo.livechat_operator_id = false;
}
channelInfo.livechat_lang_id = channel.livechat_lang_id
? mailDataHelpers.Store.one(
ResLang.browse(channel.livechat_lang_id),
makeKwArgs({ fields: ["name"] })
)
: false;
channelInfo["livechat_end_dt"] = channel.livechat_end_dt;
channelInfo["livechat_note"] = ["markup", channel.livechat_note];
channelInfo["livechat_status"] = channel.livechat_status;
channelInfo["livechat_expertise_ids"] = mailDataHelpers.Store.many(
this.env["im_livechat.expertise"].browse(channel.livechat_expertise_ids),
makeKwArgs({ fields: ["name"] })
);
channelInfo.livechat_channel_id = mailDataHelpers.Store.one(
this.env["im_livechat.channel"].browse(channel.livechat_channel_id),
makeKwArgs({ fields: ["name"] })
);
}
store._add_record_fields(this.browse(channel.id), channelInfo);
}
}
_close_livechat_session(channel_id) {
/** @type {import("mock_models").BusBus} */
const BusBus = this.env["bus.bus"];
if (this.browse(channel_id)[0].livechat_end_dt) {
return;
}
this.write([channel_id], { livechat_end_dt: serializeDate(luxon.DateTime.now()) });
const [channel] = this.browse(channel_id);
BusBus._sendone(
channel,
"mail.record/insert",
new mailDataHelpers.Store()
.add(this.browse(channel_id), makeKwArgs({ fields: ["livechat_end_dt"] }))
.get_result()
);
if (channel.message_ids.length === 0) {
return;
}
this.message_post(
channel.id,
makeKwArgs({
body: this._get_visitor_leave_message(),
message_type: "comment",
subtype_xmlid: "mail.mt_comment",
})
);
}
_get_visitor_leave_message() {
return "Visitor left the conversation.";
}
_email_livechat_transcript(channel_id, email) {
const [channel] = this.browse(channel_id);
this.message_post(
channel.id,
makeKwArgs({
body: `<div class="o_mail_notification o_hide_author">${this.env.user.name} sent the conversation to ${email}</div>`,
message_type: "notification",
subtype_xmlid: "mail.mt_comment",
})
);
}
/**
* @override
* @type {typeof mailModels.DiscussChannel["prototype"]["_types_allowing_seen_infos"]}
*/
_types_allowing_seen_infos() {
return super._types_allowing_seen_infos(...arguments).concat(["livechat"]);
}
livechat_join_channel_needing_help(idOrIds) {
const channel = this.browse(idOrIds)[0];
if (channel.livechat_status !== "need_help") {
return false;
}
this.add_members([channel.id], [this.env.user.partner_id]);
return true;
}
/** @type {typeof models.Model["prototype"]["write"]} */
write(idOrIds, values) {
const kwargs = getKwArgs(arguments, "ids", "vals");
({ ids: idOrIds, vals: values } = kwargs);
const needHelpBefore = [];
for (const channel of this._filter([["livechat_status", "=", "need_help"]])) {
needHelpBefore.push(channel.id);
}
const result = super.write(...arguments);
const needHelpAfter = [];
for (const channel of this._filter([["livechat_status", "=", "need_help"]])) {
needHelpAfter.push(channel.id);
}
const updatedChannelIds = [
...needHelpBefore.filter((id) => !needHelpAfter.includes(id)),
...needHelpAfter.filter((id) => !needHelpBefore.includes(id)),
];
if (updatedChannelIds.length) {
this.env["bus.bus"]._sendone(
[this.env["res.groups"].browse(serverState.groupLivechatId), "LOOKING_FOR_HELP"],
"mail.record/insert",
new mailDataHelpers.Store().add(this.browse(updatedChannelIds)).get_result()
);
}
return result;
}
}

View file

@ -0,0 +1,55 @@
import { mailModels } from "@mail/../tests/mail_test_helpers";
import { fields } from "@web/../tests/web_test_helpers";
export class DiscussChannelMember extends mailModels.DiscussChannelMember {
livechat_member_type = fields.Selection({
selection: [
["agent", "Agent"],
["visitor", "Visitor"],
["bot", "Chatbot"],
],
compute: false,
});
/**
* @override
* @type {typeof mailModels.DiscussChannelMember["prototype"]["_get_store_partner_fields"]}
*/
_get_store_partner_fields(fields) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
const member = this[0];
const [channel] = DiscussChannel.browse(member.channel_id);
if (channel.channel_type === "livechat") {
if (!fields) {
fields = [
"active",
"avatar_128",
"country_id",
"im_status",
"is_public",
"user_livechat_username",
];
if (member.livechat_member_type == "visitor") {
fields.push("offline_since", "email");
}
}
}
return super._get_store_partner_fields(fields);
}
/**
* @override
* @type {typeof mailModels.DiscussChannelMember["prototype"]["_to_store"]}
*/
_to_store(store, fields, extra_fields) {
super._to_store(...arguments);
for (const member of this) {
store._add_record_fields(this.browse(member.id), {
livechat_member_type: member.livechat_member_type,
});
}
}
get _to_store_defaults() {
return super._to_store_defaults.concat(["livechat_member_type"]);
}
}

View file

@ -0,0 +1,124 @@
import { mailDataHelpers } from "@mail/../tests/mock_server/mail_mock_server";
import { Command, fields, getKwArgs, makeKwArgs, models } from "@web/../tests/web_test_helpers";
export class LivechatChannel extends models.ServerModel {
_name = "im_livechat.channel";
available_operator_ids = fields.Many2many({ relation: "res.users" }); // FIXME: somehow not fetched properly
user_ids = fields.Many2many({ relation: "res.users" }); // FIXME: somehow not fetched properly
/** @param {integer} id */
action_join(id) {
this.write([id], { user_ids: [Command.link(this.env.user.id)] });
const [partner] = this.env["res.partner"].read(this.env.user.partner_id);
this.env["bus.bus"]._sendone(
partner,
"mail.record/insert",
new mailDataHelpers.Store(
this.browse(id),
makeKwArgs({ fields: ["are_you_inside", "name"] })
).get_result()
);
}
/** @param {integer} id */
action_quit(id) {
this.write(id, { user_ids: [Command.unlink(this.env.user.id)] });
const [partner] = this.env["res.partner"].read(this.env.user.partner_id);
this.env["bus.bus"]._sendone(
partner,
"mail.record/insert",
new mailDataHelpers.Store(
this.browse(id),
makeKwArgs({ fields: ["are_you_inside", "name"] })
).get_result()
);
}
/** @param {integer} id */
_compute_available_operator_ids(id) {
/** @type {import("mock_models").ResUsers} */
const ResUsers = this.env["res.users"];
const [livechatChannel] = this.browse(id);
const users = ResUsers.browse(livechatChannel.user_ids);
return users.filter((user) => user.im_status === "online");
}
/** @param {integer} id */
_get_livechat_discuss_channel_vals(id, operator_info) {
/** @type {import("mock_models").MailGuest} */
const MailGuest = this.env["mail.guest"];
/** @type {import("mock_models").ResUsers} */
const ResUsers = this.env["res.users"];
const agent = operator_info["agent"];
const membersToAdd = [
Command.create({
last_interest_dt: "2021-01-01 10:00:00",
livechat_member_type: "agent",
partner_id: agent.partner_id,
unpin_dt: "2021-01-01 12:00:00",
}),
];
const guest = ResUsers._is_public(this.env.uid) && MailGuest._get_guest_from_context();
if (guest) {
membersToAdd.push(
Command.create({ guest_id: guest.id, livechat_member_type: "visitor" })
);
}
let visitorUser;
if (this.env.user && !ResUsers._is_public(this.env.uid) && this.env.user !== agent) {
visitorUser = this.env.user;
membersToAdd.push(
Command.create({
livechat_member_type: "visitor",
partner_id: visitorUser.partner_id,
})
);
}
const membersName = [
visitorUser ? visitorUser.display_name : guest.name,
agent.livechat_username ? agent.livechat_username : agent.name,
];
return {
channel_partner_ids: [agent.partner_id],
channel_member_ids: membersToAdd,
livechat_operator_id: agent.partner_id,
livechat_channel_id: id,
livechat_status: "in_progress",
channel_type: "livechat",
name: membersName.join(" "),
};
}
/**
* Simplified mock implementation: returns
* the previous operator if he is still available
* or the first available operator.
*
* @param {integer} id
*/
_get_operator(id, previous_operator_id) {
const availableUsers = this._compute_available_operator_ids(id);
return (
availableUsers.find((operator) => operator.partner_id === previous_operator_id) ??
availableUsers[0]
);
}
_to_store(store, fields) {
const kwargs = getKwArgs(arguments, "store", "fields");
fields = kwargs.fields;
store._add_record_fields(
this,
fields.filter((field) => field !== "are_you_inside")
);
for (const livechatChannel of this) {
if (fields.includes("are_you_inside")) {
store._add_record_fields(this.browse(livechatChannel.id), {
are_you_inside: livechatChannel.user_ids.includes(this.env.user.id),
});
}
}
}
}

View file

@ -0,0 +1,5 @@
import { models } from "@web/../tests/web_test_helpers";
export class Im_LivechatExpertise extends models.ServerModel {
_name = "im_livechat.expertise";
}

View file

@ -0,0 +1,23 @@
import { mailModels } from "@mail/../tests/mail_test_helpers";
import { serverState } from "@web/../tests/web_test_helpers";
export class IrWebSocket extends mailModels.IrWebSocket {
/**
* @override
* @type {typeof busModels.IrWebSocket["prototype"]["_build_bus_channel_list"]}
*/
_build_bus_channel_list(channels) {
channels = [...super._build_bus_channel_list(channels)];
const result = channels;
for (const channel of channels) {
if (channel === "im_livechat.looking_for_help") {
result.push([
this.env["res.groups"].browse(serverState.groupLivechatId)[0],
"LOOKING_FOR_HELP",
]);
}
}
return result.filter((channel) => channel !== "im_livechat.looking_for_help");
}
}

View file

@ -0,0 +1,5 @@
import { models } from "@web/../tests/web_test_helpers";
export class LivechatChannelRule extends models.ServerModel {
_name = "im_livechat.channel.rule";
}

View file

@ -0,0 +1,39 @@
import { mailModels } from "@mail/../tests/mail_test_helpers";
import { mailDataHelpers } from "@mail/../tests/mock_server/mail_mock_server";
import { makeKwArgs } from "@web/../tests/web_test_helpers";
export class MailMessage extends mailModels.MailMessage {
_author_to_store(ids, store) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
/** @type {import("mock_models").MailMessage} */
const MailMessage = this.env["mail.message"];
/** @type {import("mock_models").ResPartner} */
const ResPartner = this.env["res.partner"];
const messages_w_author_livechat = MailMessage.browse(ids).filter((message) => {
if (!message.author_id || message.model !== "discuss.channel" || !message.res_id) {
return false;
}
const channel = DiscussChannel.browse(message.res_id);
return channel.channel_type === "livechat";
});
super._author_to_store(
ids.filter(
(id) => !messages_w_author_livechat.map((message) => message.id).includes(id)
),
store
);
for (const message of messages_w_author_livechat) {
store.add(this.browse(message.id), {
author_id: mailDataHelpers.Store.one(
ResPartner.browse(message.author_id),
makeKwArgs({
fields: ["avatar_128", "is_company", "user_livechat_username", "user"],
})
),
});
}
}
}

View file

@ -0,0 +1,18 @@
import { serverState } from "@web/../tests/web_test_helpers";
import { mailModels } from "@mail/../tests/mail_test_helpers";
export class ResGroups extends mailModels.ResGroups {
_records = [
...this._records,
{
id: serverState.groupLivechatId,
name: "Livechat User",
privilege_id: false,
},
{
id: serverState.groupLivechatManagerId,
name: "Livechat Manager",
privilege_id: false,
},
];
}

View file

@ -0,0 +1,3 @@
import { webModels } from "@web/../tests/web_test_helpers";
export class ResGroupsPrivilege extends webModels.ResGroupsPrivilege {}

View file

@ -0,0 +1,79 @@
import { mailModels } from "@mail/../tests/mail_test_helpers";
import { getKwArgs, makeKwArgs, serverState } from "@web/../tests/web_test_helpers";
export class ResPartner extends mailModels.ResPartner {
/**
* @override
* @type {typeof mailModels.ResPartner["prototype"]["_search_for_channel_invite_to_store"]}
*/
_search_for_channel_invite_to_store(ids, store, channel_id) {
/** @type {import("mock_models").DiscussChannel} */
const DiscussChannel = this.env["discuss.channel"];
/** @type {import("mock_models").DiscussChannelMember} */
const DiscussChannelMember = this.env["discuss.channel.member"];
/** @type {import("mock_models").LivechatChannel} */
const LivechatChannel = this.env["im_livechat.channel"];
/** @type {import("mock_models").Im_LivechatExpertise} */
const Im_LivechatExpertise = this.env["im_livechat.expertise"];
/** @type {import("mock_models").ResLang} */
const ResLang = this.env["res.lang"];
/** @type {import("mock_models").ResPartner} */
const ResPartner = this.env["res.partner"];
/** @type {import("mock_models").ResUsers} */
const ResUsers = this.env["res.users"];
super._search_for_channel_invite_to_store(ids, store, channel_id);
const [channel] = DiscussChannel.browse(channel_id);
if (channel?.channel_type !== "livechat") {
return;
}
const activeLivechatPartners = LivechatChannel._filter([])
.map(({ available_operator_ids }) => available_operator_ids)
.flat()
.map((userId) => ResUsers.browse(userId)[0].partner_id);
for (const partner of ResPartner.browse(ids)) {
const data = {
invite_by_self_count: DiscussChannelMember.search_count([
["partner_id", "=", partner.id],
["create_uid", "=", serverState.userId],
]),
is_available: activeLivechatPartners.includes(partner.id),
};
if (partner.lang) {
data.lang_name = ResLang.search_read([["code", "=", partner.lang]])[0].name;
}
if (partner.user_ids.length) {
const [user] = ResUsers.browse(partner.user_ids[0]);
if (user) {
const userLangs = user.livechat_lang_ids
.map((langId) => ResLang.browse(langId)[0])
.filter((lang) => lang.name !== data.lang_name);
data.livechat_languages = userLangs.map((lang) => lang.name);
data.livechat_expertise = user.livechat_expertise_ids.map(
(expId) => Im_LivechatExpertise.browse(expId)[0].name
);
}
}
store.add(this.browse(partner.id), makeKwArgs({ fields: ["user_livechat_username"] }));
store.add(this.browse(partner.id), data);
store.add(this.browse(partner.id), makeKwArgs({ extra_fields: ["is_in_call"] }));
}
}
/**
* @override
* @type {typeof mailModels.ResPartner["prototype"]["_to_store"]}
*/
_to_store(store, fields) {
const kwargs = getKwArgs(arguments, "store", "fields");
fields = kwargs.fields;
super._to_store(...arguments);
if (fields && fields.includes("user_livechat_username")) {
store._add_record_fields(
this.filter((partner) => !partner.user_livechat_username),
["name"]
);
}
}
}

View file

@ -0,0 +1,19 @@
import { mailModels } from "@mail/../tests/mail_test_helpers";
import { serverState } from "@web/../tests/web_test_helpers";
export class ResUsers extends mailModels.ResUsers {
/**
* @override
*/
_init_store_data(store) {
super._init_store_data(...arguments);
store.add({
has_access_livechat: this.env.user?.group_ids.includes(serverState.groupLivechatId),
});
store.add(this.browse(this.env.uid), {
is_livechat_manager: this.env.user?.group_ids.includes(
serverState.groupLivechatManagerId
),
});
}
}