Files
odoo_source/addons/web/static/src/views/utils.js
T
Mathieu Duckerts-Antoine dcb0498ab8 [REF] web: remove unused props in reporting views
Now that the module web_dashboard has been removed https://github.com/odoo/enterprise/pull/31641,
some props of the reporting views graph and pivot have become unused.
We remove the code related to those props.

X-original-commit: 7d231d1d37707dab4b0ecdd4e2e7cc4535ba6196
Part-of: odoo/odoo#102761
2022-10-19 11:11:53 +02:00

293 lines
8.5 KiB
JavaScript

/** @odoo-module */
import { Domain } from "@web/core/domain";
import { _t } from "@web/core/l10n/translation";
export const X2M_TYPES = ["one2many", "many2many"];
const RELATIONAL_TYPES = [...X2M_TYPES, "many2one"];
const NUMERIC_TYPES = ["integer", "float", "monetary"];
/**
* @typedef ViewActiveActions {
* @property {"view"} type
* @property {boolean} edit
* @property {boolean} create
* @property {boolean} delete
* @property {boolean} duplicate
*/
/**
* Add dependencies to activeFields
*
* @param {Object} activeFields
* @param {Object} [dependencies={}]
*/
export function addFieldDependencies(activeFields, fields, dependencies = {}) {
for (const [name, dependency] of Object.entries(dependencies)) {
if (!(name in activeFields)) {
activeFields[name] = Object.assign({ name, rawAttrs: {} }, dependency, {
modifiers: { invisible: true },
});
}
if (!(name in fields)) {
fields[name] = { ...dependency };
}
}
}
/**
* Parse the arch to check if is true or false
* If the string is empty, 0, False or false it's considered as false
* The rest is considered as true
*
* @param {string} str
* @param {boolean} [trueIfEmpty=false]
* @returns {boolean}
*/
export function archParseBoolean(str, trueIfEmpty = false) {
return str ? !/^false|0$/i.test(str) : trueIfEmpty;
}
/**
* TODO: doc
*
* @param {Object} fields
* @param {Object} fieldAttrs
* @param {string[]} activeMeasures
* @returns {Object}
*/
export const computeReportMeasures = (fields, fieldAttrs, activeMeasures) => {
const measures = {
__count: { name: "__count", string: _t("Count"), type: "integer" },
};
for (const [fieldName, field] of Object.entries(fields)) {
if (fieldName === "id" || !field.store) {
continue;
}
const { isInvisible } = fieldAttrs[fieldName] || {};
if (isInvisible) {
continue;
}
if (["integer", "float", "monetary"].includes(field.type)) {
measures[fieldName] = field;
}
}
// add active measures to the measure list. This is very rarely
// necessary, but it can be useful if one is working with a
// functional field non stored, but in a model with an overridden
// read_group method. In this case, the pivot view could work, and
// the measure should be allowed. However, be careful if you define
// a measure in your pivot view: non stored functional fields will
// probably not work (their aggregate will always be 0).
for (const measure of activeMeasures) {
if (!measures[measure]) {
measures[measure] = fields[measure];
}
}
for (const fieldName in fieldAttrs) {
if (fieldAttrs[fieldName].string && fieldName in measures) {
measures[fieldName].string = fieldAttrs[fieldName].string;
}
}
const sortedMeasures = Object.entries(measures).sort(([m1, f1], [m2, f2]) => {
if (m1 === "__count" || m2 === "__count") {
return m1 === "__count" ? 1 : -1; // Count is always last
}
return f1.string.toLowerCase().localeCompare(f2.string.toLowerCase());
});
return Object.fromEntries(sortedMeasures);
};
/**
* @param {Array[] | boolean} modifier
* @param {Object} evalContext
* @returns {boolean}
*/
export function evalDomain(modifier, evalContext) {
if (modifier && typeof modifier !== "boolean") {
modifier = new Domain(modifier).contains(evalContext);
}
return Boolean(modifier);
}
/**
* @param {Element} rootNode
* @returns {ViewActiveActions}
*/
export function getActiveActions(rootNode) {
return {
type: "view",
edit: archParseBoolean(rootNode.getAttribute("edit"), true),
create: archParseBoolean(rootNode.getAttribute("create"), true),
delete: archParseBoolean(rootNode.getAttribute("delete"), true),
duplicate: archParseBoolean(rootNode.getAttribute("duplicate"), true),
};
}
export function getClassNameFromDecoration(decoration) {
if (decoration === "bf") {
return "fw-bold";
} else if (decoration === "it") {
return "fst-italic";
}
return `text-${decoration}`;
}
export function getDecoration(rootNode) {
const decorations = [];
for (const name of rootNode.getAttributeNames()) {
if (name.startsWith("decoration-")) {
decorations.push({
class: getClassNameFromDecoration(name.replace("decoration-", "")),
condition: rootNode.getAttribute(name),
});
}
}
return decorations;
}
/**
* @param {number | number[]} idsList
* @returns {number[]}
*/
export function getIds(idsList) {
if (Array.isArray(idsList)) {
if (idsList.length === 2 && typeof idsList[1] === "string") {
return [idsList[0]];
} else {
return idsList;
}
} else if (idsList) {
return [idsList];
} else {
return [];
}
}
/**
* @param {any} field
* @returns {boolean}
*/
export function isRelational(field) {
return field && RELATIONAL_TYPES.includes(field.type);
}
/**
* @param {any} field
* @returns {boolean}
*/
export function isX2Many(field) {
return field && X2M_TYPES.includes(field.type);
}
/**
* @param {Object} field
* @returns {boolean} true iff the given field is a numeric field
*/
export function isNumeric(field) {
return NUMERIC_TYPES.includes(field.type);
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNull(value) {
return [null, undefined].includes(value);
}
export function processButton(node) {
return {
className: node.getAttribute("class") || "",
icon: node.getAttribute("icon") || false,
title: node.getAttribute("title") || undefined,
string: node.getAttribute("string") || undefined,
options: JSON.parse(node.getAttribute("options") || "{}"),
modifiers: JSON.parse(node.getAttribute("modifiers") || "{}"),
clickParams: {
close: archParseBoolean(node.getAttribute("close"), false),
context: node.getAttribute("context") || "{}",
name: node.getAttribute("name"),
type: node.getAttribute("type"),
},
};
}
/**
* In the preview implementation of reporting views, the virtual field used to
* display the number of records was named __count__, whereas __count is
* actually the one used in xml. So basically, activating a filter specifying
* __count as measures crashed. Unfortunately, as __count__ was used in the JS,
* all filters saved as favorite at that time were saved with __count__, and
* not __count. So in order the make them still work with the new
* implementation, we handle both __count__ and __count.
*
* This function replaces occurences of '__count__' by '__count' in the given
* element(s).
*
* @param {any | any[]} [measures]
* @returns {any}
*/
export function processMeasure(measure) {
if (Array.isArray(measure)) {
return measure.map(processMeasure);
}
return measure === "__count__" ? "__count" : measure;
}
/**
* @param {any} string
* @return {OrderTerm[]}
*/
export function stringToOrderBy(string) {
if (!string) {
return [];
}
return string.split(",").map((order) => {
const splitOrder = order.trim().split(" ");
if (splitOrder.length === 2) {
return {
name: splitOrder[0],
asc: splitOrder[1].toLowerCase() === "asc",
};
} else {
return {
name: splitOrder[0],
asc: true,
};
}
});
}
/**
* Transforms a string into a valid expression to be injected
* in a template as a props via setAttribute.
* Example: myString = `Some weird language quote (") `;
* should become in the template:
* <Component label="&quot;Some weird language quote (\\&quot;)&quot; " />
* which should be interpreted by owl as a JS expression being a string:
* `Some weird language quote (") `
*
* @param {string} str The initial value: a pure string to be interpreted as such
* @return {string} the valid string to be injected into a component's node props.
*/
export function toStringExpression(str) {
return `\`${str.replaceAll("`", "\\`")}\``;
}
/**
* Generate a unique identifier (64 bits) in hexadecimal.
*
* @returns {string}
*/
export function uuid() {
const array = new Uint8Array(8);
window.crypto.getRandomValues(array);
// Uint8Array to hex
return [...array].map((b) => b.toString(16).padStart(2, "0")).join("");
}