From 2b07d5f54b9ab397bf91a34a75da5bdf803b4b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Lef=C3=A8vre=20=28lul=29?= Date: Wed, 13 Dec 2023 14:51:09 +0100 Subject: [PATCH] [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) --- .../plugins/global_filters_ui_plugin.js | 61 ++++++--- .../spreadsheet/static/src/helpers/model.js | 9 +- .../static/src/pivot/pivot_functions.js | 4 +- .../global_filters_model_test.js | 118 +++++++++++++++++- .../tests/public_spreadsheet/freeze_test.js | 29 ++++- 5 files changed, 195 insertions(+), 26 deletions(-) diff --git a/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js b/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js index 3055f1ff280..a7f658f10dd 100644 --- a/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js +++ b/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js @@ -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); } diff --git a/addons/spreadsheet/static/src/helpers/model.js b/addons/spreadsheet/static/src/helpers/model.js index ff267df6aaf..383b77defc4 100644 --- a/addons/spreadsheet/static/src/helpers/model.js +++ b/addons/spreadsheet/static/src/helpers/model.js @@ -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(", "); } } diff --git a/addons/spreadsheet/static/src/pivot/pivot_functions.js b/addons/spreadsheet/static/src/pivot/pivot_functions.js index 789e710ac7e..0161f57a95a 100644 --- a/addons/spreadsheet/static/src/pivot/pivot_functions.js +++ b/addons/spreadsheet/static/src/pivot/pivot_functions.js @@ -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"], diff --git a/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js b/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js index 5614733ace0..6403af08c57 100644 --- a/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js +++ b/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js @@ -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 ); }); diff --git a/addons/spreadsheet/static/tests/public_spreadsheet/freeze_test.js b/addons/spreadsheet/static/tests/public_spreadsheet/freeze_test.js index f3bb27c6ecf..94848343bec 100644 --- a/addons/spreadsheet/static/tests/public_spreadsheet/freeze_test.js +++ b/addons/spreadsheet/static/tests/public_spreadsheet/freeze_test.js @@ -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"); + }); });