Files
gzth/boolean_group_operator/static/src/js/relational_model.js
T
2024-12-10 11:12:40 +08:00

198 lines
7.9 KiB
JavaScript

/* @odoo-module */
import { patch } from "@web/core/utils/patch";
import { extractInfoFromGroupData, isRelational } from "@web/model/relational_model/utils";
import { RelationalModel, makeActiveField } from "@web/model/relational_model/relational_model";
const orignExtractInfoFromGroupData = extractInfoFromGroupData;
function getAggregatesFromGroupData(groupData, fields) {
const aggregates = {};
for (const [key, value] of Object.entries(groupData)) {
if (key in fields && fields[key].type === "boolean") {
aggregates[key] = value;
}
}
return aggregates;
}
const newExtractInfoFromGroupData = function (groupData, groupBy, fields) {
let group = orignExtractInfoFromGroupData(groupData, groupBy, fields);
Object.assign(group.aggregates, getAggregatesFromGroupData(groupData, fields));
return group;
};
patch(RelationalModel.prototype, {
async _loadGroupedList(config) {
config.offset = config.offset || 0;
config.limit = config.limit || this.initialGroupsLimit;
if (!config.limit) {
config.limit = config.openGroupsByDefault
? this.constructor.DEFAULT_OPEN_GROUP_LIMIT
: this.constructor.DEFAULT_GROUP_LIMIT;
}
config.groups = config.groups || {};
const firstGroupByName = config.groupBy[0].split(":")[0];
if (firstGroupByName.includes(".")) {
if (!config.fields[firstGroupByName]) {
await this._getPropertyDefinition(config, firstGroupByName);
}
const propertiesFieldName = firstGroupByName.split(".")[0];
if (!config.activeFields[propertiesFieldName]) {
// add the properties field so we load its data when reading the records
// so when we drag and drop we don't need to fetch the value of the record
config.activeFields[propertiesFieldName] = makeActiveField();
}
}
const orderBy = config.orderBy.filter(
(o) =>
o.name === firstGroupByName ||
(o.name in config.activeFields &&
config.fields[o.name].group_operator !== undefined)
);
const response = await this._webReadGroup(config, firstGroupByName, orderBy);
const { groups: groupsData, length } = response;
const groupBy = config.groupBy.slice(1);
const groupByField = config.fields[config.groupBy[0].split(":")[0]];
const commonConfig = {
resModel: config.resModel,
fields: config.fields,
activeFields: config.activeFields,
};
let groupRecordConfig;
const groupRecordResIds = [];
if (this.groupByInfo[firstGroupByName]) {
groupRecordConfig = {
...this.groupByInfo[firstGroupByName],
resModel: config.fields[firstGroupByName].relation,
context: {},
};
}
const proms = [];
let nbOpenGroups = 0;
const groups = [];
for (const groupData of groupsData) {
const group = newExtractInfoFromGroupData(groupData, config.groupBy, config.fields);
if (!config.groups[group.value]) {
config.groups[group.value] = {
...commonConfig,
groupByFieldName: groupByField.name,
isFolded:
"__fold" in groupData ? groupData.__fold : !config.openGroupsByDefault,
extraDomain: false,
value: group.value,
list: {
...commonConfig,
groupBy,
},
};
if (isRelational(config.fields[firstGroupByName]) && !group.value) {
// fold the "unset" group by default when grouped by many2one
config.groups[group.value].isFolded = true;
}
if (groupRecordConfig) {
config.groups[group.value].record = {
...groupRecordConfig,
resId: group.value ?? false,
};
}
}
if (groupRecordConfig) {
const resId = config.groups[group.value].record.resId;
if (resId) {
groupRecordResIds.push(resId);
}
}
const groupConfig = config.groups[group.value];
groupConfig.list.orderBy = config.orderBy;
groupConfig.initialDomain = group.domain;
if (groupConfig.extraDomain) {
groupConfig.list.domain = Domain.and([
group.domain,
groupConfig.extraDomain,
]).toList();
} else {
groupConfig.list.domain = group.domain;
}
const context = {
...config.context,
[`default_${firstGroupByName}`]: group.serverValue,
};
groupConfig.list.context = context;
groupConfig.context = context;
if (groupBy.length) {
group.groups = [];
} else {
group.records = [];
}
if (!groupConfig.isFolded) {
nbOpenGroups++;
if (nbOpenGroups > this.constructor.MAX_NUMBER_OPENED_GROUPS) {
groupConfig.isFolded = true;
}
}
if (!groupConfig.isFolded && group.count > 0) {
const prom = this._loadData(groupConfig.list).then((response) => {
if (groupBy.length) {
group.groups = response ? response.groups : [];
group.length = response ? response.length : 0;
} else {
group.records = response ? response.records : [];
}
});
proms.push(prom);
}
groups.push(group);
}
if (groupRecordConfig && Object.keys(groupRecordConfig.activeFields).length) {
const prom = this._loadRecords({
...groupRecordConfig,
resIds: groupRecordResIds,
}).then((records) => {
for (const group of groups) {
if (!group.value) {
group.values = { id: false };
continue;
}
group.values = records.find((r) => group.value && r.id === group.value);
}
});
proms.push(prom);
}
await Promise.all(proms);
// if a group becomes empty at some point (e.g. we dragged its last record out of it), and the view is reloaded
// with the same domain and groupbys, we want to keep the empty group in the UI
const params = JSON.stringify([
config.domain,
config.groupBy,
config.offset,
config.limit,
config.orderBy,
]);
if (config.currentGroups && config.currentGroups.params === params) {
const currentGroups = config.currentGroups.groups;
currentGroups.forEach((group, index) => {
if (
config.groups[group.value] &&
!groups.some((g) => JSON.stringify(g.value) === JSON.stringify(group.value))
) {
const aggregates = Object.assign({}, group.aggregates);
for (const key in aggregates) {
aggregates[key] = 0;
}
groups.splice(
index,
0,
Object.assign({}, group, { count: 0, length: 0, records: [], aggregates })
);
}
});
}
config.currentGroups = { params, groups };
return { groups, length };
}
})