[FIX] spreadsheet: get ODOO.FILTER.VALUE with from/to filter

- create a from/to date filter with let's say "my filter"
  as its title.
- in the spreadsheet, `=ODOO.FILTER.VALUE("my filter")`
=> the function doesn't return anything

closes odoo/odoo#146213

Task: 3584650
Related: odoo/enterprise#52871
Signed-off-by: Pierre Rousseau (pro) <pro@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2024-02-12 20:23:58 +00:00
parent 931fc29227
commit 2b07d5f54b
5 changed files with 195 additions and 26 deletions
@@ -41,7 +41,8 @@ const MONTHS = {
december: { value: 12, granularity: "month" },
};
const { UuidGenerator, createEmptyExcelSheet, createEmptySheet } = spreadsheet.helpers;
const { UuidGenerator, createEmptyExcelSheet, createEmptySheet, toXC, toNumber } =
spreadsheet.helpers;
const uuidGenerator = new UuidGenerator();
export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
@@ -230,17 +231,29 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
const value = this.getGlobalFilterValue(filter.id);
switch (filter.type) {
case "text":
return value || "";
return [[{ value: value || "" }]];
case "date": {
if (filter.rangeType === "from_to") {
const locale = this.getters.getLocale();
const from = {
value: value.from && toNumber(value.from, locale),
format: locale.dateFormat,
};
const to = {
value: value.to && toNumber(value.to, locale),
format: locale.dateFormat,
};
return [[from], [to]];
}
if (value && typeof value === "string") {
const type = RELATIVE_DATE_RANGE_TYPES.find((type) => type.type === value);
if (!type) {
return "";
return [[{ value: "" }]];
}
return type.description.toString();
return [[{ value: type.description.toString() }]];
}
if (!value || value.yearOffset === undefined) {
return "";
return [[{ value: "" }]];
}
const periodOptions = getPeriodOptions(DateTime.local());
const year = String(DateTime.local().year + value.yearOffset);
@@ -251,11 +264,11 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
periodStr =
MONTHS[value.period] && String(MONTHS[value.period].value).padStart(2, "0");
}
return periodStr ? periodStr + "/" + year : year;
return [[{ value: periodStr ? periodStr + "/" + year : year }]];
}
case "relation":
if (!value?.length || !this.orm) {
return "";
return [[{ value: "" }]];
}
if (!this.recordsDisplayName[filter.id]) {
this.orm
@@ -267,9 +280,9 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
sheetId: this.getters.getActiveSheetId(),
});
});
return "";
return [[{ value: "" }]];
}
return this.recordsDisplayName[filter.id].join(", ");
return [[{ value: this.recordsDisplayName[filter.id].join(", ") }]];
}
}
@@ -531,20 +544,32 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
const styleId = getItemId({ bold: true }, data.styles);
const cells = {};
cells["A1"] = { content: _t("Filter"), style: styleId };
cells["B1"] = { content: _t("Value"), style: styleId };
let row = 2;
cells["A1"] = { content: "Filter", style: styleId };
cells["B1"] = { content: "Value", style: styleId };
let numberOfCols = 2; // at least 2 cols (filter title and filter value)
let filterRowIndex = 1; // first row is the column titles
for (const filter of this.getters.getGlobalFilters()) {
const content = this.getters.getFilterDisplayValue(filter.label);
cells[`A${row}`] = { content: filter.label };
cells[`B${row}`] = { content };
row++;
cells[`A${filterRowIndex + 1}`] = { content: filter.label };
const result = this.getFilterDisplayValue(filter.label);
for (const colIndex in result) {
numberOfCols = Math.max(numberOfCols, Number(colIndex) + 2);
for (const rowIndex in result[colIndex]) {
const cell = result[colIndex][rowIndex];
const xc = toXC(Number(colIndex) + 1, Number(rowIndex) + filterRowIndex);
cells[xc] = { content: cell.value.toString() };
if (cell.format) {
const formatId = getItemId(cell.format, data.formats);
cells[xc].format = formatId;
}
}
}
filterRowIndex += result[0].length;
}
const sheet = {
...createEmptySheet(uuidGenerator.uuidv4(), _t("Active Filters")),
cells,
colNumber: 2,
rowNumber: this.getters.getGlobalFilters().length + 1,
colNumber: numberOfCols,
rowNumber: filterRowIndex,
};
data.sheets.push(sheet);
}
@@ -5,7 +5,7 @@ import { migrate } from "@spreadsheet/o_spreadsheet/migration";
import { _t } from "@web/core/l10n/translation";
import { loadBundle } from "@web/core/assets";
const { toCartesian } = helpers;
const { formatValue, isDefined, toCartesian } = helpers;
export async function fetchSpreadsheetModel(env, resModel, resId) {
const { data, revisions } = await env.services.orm.call(resModel, "join_spreadsheet_session", [
@@ -82,9 +82,14 @@ export async function freezeOdooData(model) {
function exportGlobalFiltersToSheet(model, data) {
model.getters.exportSheetWithActiveFilters(data);
const locale = model.getters.getLocale();
for (const filter of data.globalFilters) {
const content = model.getters.getFilterDisplayValue(filter.label);
filter["value"] = content;
filter["value"] = content
.flat()
.filter(isDefined)
.map(({ value, format }) => formatValue(value, { format, locale }))
.join(", ");
}
}
@@ -46,8 +46,8 @@ const ODOO_FILTER_VALUE = {
description: _t("Return the current value of a spreadsheet filter."),
args: [arg("filter_name (string)", _t("The label of the filter whose value to return."))],
category: "Odoo",
compute: function (filterName) {
const unEscapedFilterName = toString(filterName).replaceAll('\\"', '"');
computeValueAndFormat: function (filterName) {
const unEscapedFilterName = toString(filterName.value).replaceAll('\\"', '"');
return this.getters.getFilterDisplayValue(unEscapedFilterName);
},
returns: ["STRING"],
@@ -48,6 +48,7 @@ import { GlobalFiltersUIPlugin } from "@spreadsheet/global_filters/plugins/globa
import { migrate } from "@spreadsheet/o_spreadsheet/migration";
import { toRangeData } from "../utils/zones";
import { PivotUIPlugin } from "@spreadsheet/pivot/index";
import { getEvaluatedCell } from "../utils/getters";
const { DateTime } = luxon;
const { toZone } = helpers;
@@ -891,6 +892,85 @@ QUnit.module("spreadsheet > Global filters model", {}, () => {
assert.equal(getCellValue(model, "A10"), ``);
});
QUnit.test("ODOO.FILTER.VALUE date from/to without values", async function (assert) {
const model = await createModelWithDataSource();
setCellContent(model, "A1", `=ODOO.FILTER.VALUE("Date Filter")`);
await addGlobalFilter(model, {
id: "42",
type: "date",
label: "Date Filter",
rangeType: "from_to",
});
assert.equal(getEvaluatedCell(model, "A1").value, "");
assert.equal(getEvaluatedCell(model, "A2").value, "");
});
QUnit.test("ODOO.FILTER.VALUE date from/to with only from defined", async function (assert) {
const model = await createModelWithDataSource();
setCellContent(model, "A1", `=ODOO.FILTER.VALUE("Date Filter")`);
await addGlobalFilter(model, {
id: "42",
type: "date",
label: "Date Filter",
rangeType: "from_to",
});
await setGlobalFilterValue(model, {
id: "42",
value: {
from: "2020-01-01",
},
});
assert.equal(getEvaluatedCell(model, "A1").value, 43831);
assert.equal(getEvaluatedCell(model, "A1").format, "m/d/yyyy");
assert.equal(getEvaluatedCell(model, "A1").formattedValue, "1/1/2020");
assert.equal(getEvaluatedCell(model, "B1").value, "");
});
QUnit.test("ODOO.FILTER.VALUE date from/to with only to defined", async function (assert) {
const model = await createModelWithDataSource();
setCellContent(model, "A1", `=ODOO.FILTER.VALUE("Date Filter")`);
await addGlobalFilter(model, {
id: "42",
type: "date",
label: "Date Filter",
rangeType: "from_to",
});
await setGlobalFilterValue(model, {
id: "42",
value: {
to: "2020-01-01",
},
});
assert.equal(getEvaluatedCell(model, "A1").value, "");
assert.equal(getEvaluatedCell(model, "B1").value, 43831);
assert.equal(getEvaluatedCell(model, "B1").format, "m/d/yyyy");
assert.equal(getEvaluatedCell(model, "B1").formattedValue, "1/1/2020");
});
QUnit.test("ODOO.FILTER.VALUE date from/to with from and to defined", async function (assert) {
const model = await createModelWithDataSource();
setCellContent(model, "A1", `=ODOO.FILTER.VALUE("Date Filter")`);
await addGlobalFilter(model, {
id: "42",
type: "date",
label: "Date Filter",
rangeType: "from_to",
});
await setGlobalFilterValue(model, {
id: "42",
value: {
from: "2020-01-01",
to: "2021-01-01",
},
});
assert.equal(getEvaluatedCell(model, "A1").value, 43831);
assert.equal(getEvaluatedCell(model, "A1").format, "m/d/yyyy");
assert.equal(getEvaluatedCell(model, "A1").formattedValue, "1/1/2020");
assert.equal(getEvaluatedCell(model, "B1").value, 44197);
assert.equal(getEvaluatedCell(model, "B1").format, "m/d/yyyy");
assert.equal(getEvaluatedCell(model, "B1").formattedValue, "1/1/2021");
});
QUnit.test("ODOO.FILTER.VALUE relation filter", async function (assert) {
const model = await createModelWithDataSource({
mockRPC: function (route, { method, args }) {
@@ -1362,7 +1442,7 @@ QUnit.module("spreadsheet > Global filters model", {}, () => {
await addGlobalFilter(model, THIS_YEAR_GLOBAL_FILTER);
const [filter] = model.getters.getGlobalFilters();
assert.strictEqual(
model.getters.getFilterDisplayValue(filter.label),
model.getters.getFilterDisplayValue(filter.label)[0][0].value,
String(new Date().getFullYear())
);
});
@@ -1383,11 +1463,43 @@ QUnit.module("spreadsheet > Global filters model", {}, () => {
assert.equal(filterSheet.cells["B1"].content, "Value");
assert.equal(
filterSheet.cells["B2"].content,
model.getters.getFilterDisplayValue(filter.label)
model.getters.getFilterDisplayValue(filter.label)[0][0].value
);
model.exportXLSX(); // should not crash
});
QUnit.test("Export from/to global filters for excel", async function (assert) {
const model = await createModelWithDataSource();
await addGlobalFilter(model, {
id: "42",
type: "date",
label: "Date Filter",
rangeType: "from_to",
});
await setGlobalFilterValue(model, {
id: "42",
value: {
from: "2020-01-01",
to: "2021-01-01",
},
});
const [filter] = model.getters.getGlobalFilters();
const filterPlugin = model["handlers"].find(
(handler) => handler instanceof GlobalFiltersUIPlugin
);
const exportData = { styles: {}, formats: {}, sheets: [] };
filterPlugin.exportForExcel(exportData);
const filterSheet = exportData.sheets[0];
assert.equal(filterSheet.cells["A1"].content, "Filter");
assert.equal(filterSheet.cells["A2"].content, filter.label);
assert.equal(filterSheet.cells["B1"].content, "Value");
assert.equal(filterSheet.cells["B2"].content, 43831);
assert.equal(filterSheet.cells["C2"].content, 44197);
assert.equal(filterSheet.cells["B2"].format, 1);
assert.equal(filterSheet.cells["C2"].format, 1);
assert.strictEqual(exportData.formats[1], "m/d/yyyy");
});
QUnit.test("Date filter automatic default value for years filter", async function (assert) {
const label = "This year";
const { model } = await createSpreadsheetWithPivot();
@@ -1498,7 +1610,7 @@ QUnit.module("spreadsheet > Global filters model", {}, () => {
rangeType: "relative",
});
assert.equal(
model.getters.getFilterDisplayValue(label),
model.getters.getFilterDisplayValue(label)[0][0].value,
RELATIVE_DATE_RANGE_TYPES[1].description
);
});
@@ -2,7 +2,7 @@
import { freezeOdooData } from "../../src/helpers/model";
import { createSpreadsheetWithChart } from "../utils/chart";
import { setCellContent } from "../utils/commands";
import { setCellContent, setGlobalFilterValue } from "../utils/commands";
import { getCell, getEvaluatedCell } from "../utils/getters";
import { createSpreadsheetWithPivot } from "../utils/pivot";
import { createModelWithDataSource } from "@spreadsheet/../tests/utils/model";
@@ -106,4 +106,31 @@ QUnit.module("freezing spreadsheet", {}, function () {
assert.strictEqual(data.globalFilters[0].label, "This Year");
assert.strictEqual(data.globalFilters[0].value, new Date().getFullYear().toString());
});
QUnit.test("from/to global filters are exported", async function (assert) {
const model = await createModelWithDataSource();
await addGlobalFilter(model, {
id: "42",
type: "date",
label: "Date Filter",
rangeType: "from_to",
});
await setGlobalFilterValue(model, {
id: "42",
value: {
from: "2020-01-01",
to: "2021-01-01",
},
});
const data = await freezeOdooData(model);
const filterSheet = data.sheets[1];
assert.strictEqual(filterSheet.cells.B2.content, "43831");
assert.strictEqual(filterSheet.cells.C2.content, "44197");
assert.strictEqual(filterSheet.cells.B2.format, 1);
assert.strictEqual(filterSheet.cells.C2.format, 1);
assert.strictEqual(data.formats[1], "m/d/yyyy");
assert.strictEqual(data.globalFilters.length, 1);
assert.strictEqual(data.globalFilters[0].label, "Date Filter");
assert.strictEqual(data.globalFilters[0].value, "1/1/2020, 1/1/2021");
});
});