mail/static/tests/helpers/mock_server/models/res_partner.js

406 lines
16 KiB
JavaScript
Raw Normal View History

2024-05-03 12:40:35 +03:00
/* @odoo-module */
import { patch } from "@web/core/utils/patch";
import { MockServer } from "@web/../tests/helpers/mock_server";
patch(MockServer.prototype, {
async _performRPC(route, args) {
if (args.model === "res.partner" && args.method === "im_search") {
const name = args.args[0] || args.kwargs.search;
const limit = args.args[1] || args.kwargs.limit;
const excluded_ids = args.args[2] || args.kwargs.excluded_ids;
return this._mockResPartnerImSearch(name, limit, excluded_ids);
}
if (args.model === "res.partner" && args.method === "search_for_channel_invite") {
const search_term = args.args[0] || args.kwargs.search_term;
const channel_id = args.args[1] || args.kwargs.channel_id;
const limit = args.args[2] || args.kwargs.limit;
return this._mockResPartnerSearchForChannelInvite(search_term, channel_id, limit);
}
if (args.model === "res.partner" && args.method === "get_mention_suggestions") {
return this._mockResPartnerGetMentionSuggestions(args);
}
if (
args.model === "res.partner" &&
args.method === "get_mention_suggestions_from_channel"
) {
return this._mockResPartnerGetMentionSuggestionsFromChannel(args);
}
return super._performRPC(route, args);
},
/**
* Simulates `get_mention_suggestions` on `res.partner`.
*
* @private
* @returns {Array[]}
*/
_mockResPartnerGetMentionSuggestions(args) {
const search = (args.args[0] || args.kwargs.search || "").toLowerCase();
const limit = args.args[1] || args.kwargs.limit || 8;
/**
* Returns the given list of partners after filtering it according to
* the logic of the Python method `get_mention_suggestions` for the
* given search term. The result is truncated to the given limit and
* formatted as expected by the original method.
*
* @param {Object[]} partners
* @param {string} search
* @param {integer} limit
* @returns {Object[]}
*/
const mentionSuggestionsFilter = (partners, search, limit) => {
const matchingPartners = [
...this._mockResPartnerMailPartnerFormat(
partners
.filter((partner) => {
// no search term is considered as return all
if (!search) {
return true;
}
// otherwise name or email must match search term
if (partner.name && partner.name.toLowerCase().includes(search)) {
return true;
}
if (partner.email && partner.email.toLowerCase().includes(search)) {
return true;
}
return false;
})
.map((partner) => partner.id)
).values(),
];
// reduce results to max limit
matchingPartners.length = Math.min(matchingPartners.length, limit);
return matchingPartners;
};
// add main suggestions based on users
const partnersFromUsers = this.getRecords("res.users", [])
.map((user) => this.getRecords("res.partner", [["id", "=", user.partner_id]])[0])
.filter((partner) => partner);
const mainMatchingPartners = mentionSuggestionsFilter(partnersFromUsers, search, limit);
let extraMatchingPartners = [];
// if not enough results add extra suggestions based on partners
const remainingLimit = limit - mainMatchingPartners.length;
if (mainMatchingPartners.length < limit) {
const partners = this.getRecords("res.partner", [
["id", "not in", mainMatchingPartners.map((partner) => partner.id)],
]);
extraMatchingPartners = mentionSuggestionsFilter(partners, search, remainingLimit);
}
return mainMatchingPartners.concat(extraMatchingPartners);
},
/**
* Simulates `get_channel_mention_suggestions` on `res.partner`.
*
* @private
* @returns {Array[]}
*/
_mockResPartnerGetMentionSuggestionsFromChannel(args) {
const search = (args.args[0] || args.kwargs.search || "").toLowerCase();
const limit = args.args[1] || args.kwargs.limit || 8;
const channel_id = args.args[2] || args.kwargs.channel_id;
/**
* Returns the given list of partners after filtering it according to
* the logic of the Python method `get_mention_suggestions` for the
* given search term. The result is truncated to the given limit and
* formatted as expected by the original method.
*
* @param {Object[]} partners
* @param {string} search
* @param {integer} limit
* @returns {Object[]}
*/
const mentionSuggestionsFilter = (partners, search, limit) => {
const matchingPartners = [
...this._mockResPartnerMailPartnerFormat(
partners
.filter((partner) => {
const [member] = this.getRecords("discuss.channel.member", [
["channel_id", "=", channel_id],
["partner_id", "=", partner.id],
]);
if (!member) {
return false;
}
// no search term is considered as return all
if (!search) {
return true;
}
// otherwise name or email must match search term
if (partner.name && partner.name.toLowerCase().includes(search)) {
return true;
}
if (partner.email && partner.email.toLowerCase().includes(search)) {
return true;
}
return false;
})
.map((partner) => partner.id)
).values(),
].map((partnerFormat) => {
const [member] = this.getRecords("discuss.channel.member", [
["channel_id", "=", channel_id],
["partner_id", "=", partnerFormat.id],
]);
partnerFormat["channelMembers"] = [
[
"ADD",
this._mockDiscussChannelMember_DiscussChannelMemberFormat([member.id])[0],
],
];
return partnerFormat;
});
// reduce results to max limit
matchingPartners.length = Math.min(matchingPartners.length, limit);
return matchingPartners;
};
// add main suggestions based on users
const partnersFromUsers = this.getRecords("res.users", [])
.map((user) => this.getRecords("res.partner", [["id", "=", user.partner_id]])[0])
.filter((partner) => partner);
const mainMatchingPartners = mentionSuggestionsFilter(partnersFromUsers, search, limit);
let extraMatchingPartners = [];
// if not enough results add extra suggestions based on partners
const remainingLimit = limit - mainMatchingPartners.length;
if (mainMatchingPartners.length < limit) {
const partners = this.getRecords("res.partner", [
["id", "not in", mainMatchingPartners.map((partner) => partner.id)],
]);
extraMatchingPartners = mentionSuggestionsFilter(partners, search, remainingLimit);
}
return mainMatchingPartners.concat(extraMatchingPartners);
},
/**
* Simulates `_get_needaction_count` on `res.partner`.
*
* @private
* @param {integer} id
* @returns {integer}
*/
_mockResPartner_GetNeedactionCount(id) {
const partner = this.getRecords("res.partner", [["id", "=", id]])[0];
return this.getRecords("mail.notification", [
["res_partner_id", "=", partner.id],
["is_read", "=", false],
]).length;
},
/**
* Simulates `im_search` on `res.partner`.
*
* @private
* @param {string} [name='']
* @param {integer} [limit=20]
* @returns {Object[]}
*/
_mockResPartnerImSearch(name = "", limit = 20, excluded_ids = []) {
name = name.toLowerCase(); // simulates ILIKE
// simulates domain with relational parts (not supported by mock server)
const matchingPartners = this.getRecords("res.users", [])
.filter((user) => {
const partner = this.getRecords("res.partner", [["id", "=", user.partner_id]])[0];
// user must have a partner
if (!partner) {
return false;
}
// not current partner
if (partner.id === this.pyEnv.currentPartnerId) {
return false;
}
// no name is considered as return all
if (!name) {
return true;
}
if (partner.name && partner.name.toLowerCase().includes(name)) {
return true;
}
return false;
})
.map((user) => {
const partner = this.getRecords("res.partner", [["id", "=", user.partner_id]])[0];
return {
id: partner.id,
name: partner.name,
};
})
.sort((a, b) => (a.name === b.name ? a.id - b.id : a.name > b.name ? 1 : -1));
matchingPartners.length = Math.min(matchingPartners.length, limit);
const resultPartners = matchingPartners.filter(
(partner) => !excluded_ids.includes(partner.id)
);
return [
...this._mockResPartnerMailPartnerFormat(
resultPartners.map((partner) => partner.id)
).values(),
];
},
/**
* Simulates `mail_partner_format` on `res.partner`.
*
* @private
* @returns {integer[]} ids
* @returns {Map}
*/
_mockResPartnerMailPartnerFormat(ids) {
const partners = this.getRecords("res.partner", [["id", "in", ids]], {
active_test: false,
});
// Servers is also returning `is_internal_user` but not
// done here for simplification.
return new Map(
partners.map((partner) => {
const users = this.getRecords("res.users", [["id", "in", partner.user_ids]]);
const internalUsers = users.filter((user) => !user.share);
let mainUser;
if (internalUsers.length > 0) {
mainUser = internalUsers[0];
} else if (users.length > 0) {
mainUser = users[0];
}
return [
partner.id,
{
active: partner.active,
email: partner.email,
id: partner.id,
im_status: partner.im_status,
is_company: partner.is_company,
name: partner.name,
type: "partner",
user: mainUser
? {
id: mainUser.id,
isInternalUser: !mainUser.share,
}
: false,
},
];
})
);
},
/**
* Simulates `search_for_channel_invite` on `res.partner`.
*
* @private
* @param {string} [search_term='']
* @param {integer} [channel_id]
* @param {integer} [limit=30]
* @returns {Object[]}
*/
_mockResPartnerSearchForChannelInvite(search_term, channel_id, limit = 30) {
if (search_term) {
search_term = search_term.toLowerCase(); // simulates ILIKE
}
const memberPartnerIds = new Set(
this.getRecords("discuss.channel.member", [["channel_id", "=", channel_id]]).map(
(member) => member.partner_id
)
);
// simulates domain with relational parts (not supported by mock server)
const matchingPartners = [
...this._mockResPartnerMailPartnerFormat(
this.getRecords("res.users", [])
.filter((user) => {
const partner = this.getRecords("res.partner", [
["id", "=", user.partner_id],
])[0];
// user must have a partner
if (!partner) {
return false;
}
// user should not already be a member of the channel
if (memberPartnerIds.has(partner.id)) {
return false;
}
// no name is considered as return all
if (!search_term) {
return true;
}
if (partner.name && partner.name.toLowerCase().includes(search_term)) {
return true;
}
return false;
})
.map((user) => user.partner_id)
).values(),
];
const count = matchingPartners.length;
matchingPartners.length = Math.min(count, limit);
return {
count,
partners: matchingPartners,
};
},
/**
* Simulates `_message_fetch_failed` on `res.partner`.
*
* @private
* @param {integer} id
* @returns {Object[]}
*/
_mockResPartner_MessageFetchFailed(id) {
const partner = this.getRecords("res.partner", [["id", "=", id]], {
active_test: false,
})[0];
const messages = this.getRecords("mail.message", [
["author_id", "=", partner.id],
["res_id", "!=", 0],
["model", "!=", false],
["message_type", "!=", "user_notification"],
]).filter((message) => {
// Purpose is to simulate the following domain on mail.message:
// ['notification_ids.notification_status', 'in', ['bounce', 'exception']],
// But it's not supported by getRecords domain to follow a relation.
const notifications = this.getRecords("mail.notification", [
["mail_message_id", "=", message.id],
["notification_status", "in", ["bounce", "exception"]],
]);
return notifications.length > 0;
});
return this._mockMailMessage_MessageNotificationFormat(
messages.map((message) => message.id)
);
},
/**
* Simulates `_get_channels_as_member` on `res.partner`.
*
* @private
* @param {integer[]} ids
* @returns {Object}
*/
_mockResPartner_GetChannelsAsMember(ids) {
const partner = this.getRecords("res.partner", [["id", "in", ids]])[0];
const channelMembers = this.getRecords("discuss.channel.member", [
["partner_id", "=", partner.id],
]);
const channels = this.getRecords("discuss.channel", [
["channel_type", "in", ["channel", "group"]],
["channel_member_ids", "in", channelMembers.map((member) => member.id)],
]);
const directMessagesMembers = this.getRecords("discuss.channel.member", [
["partner_id", "=", partner.id],
["is_pinned", "=", true],
]);
const directMessages = this.getRecords("discuss.channel", [
["channel_type", "=", "chat"],
["channel_member_ids", "in", directMessagesMembers.map((member) => member.id)],
]);
return [...channels, ...directMessages];
},
/**
* Simulates `_get_current_persona` on `res.partner`.
*
*/
_mockResPartner__getCurrentPersona() {
if (this.pyEnv.currentUser?._is_public()) {
return [null, this._mockMailGuest__getGuestFromContext()];
}
return [this.pyEnv.currentPartner, null];
},
});