diff --git a/addons/spreadsheet/static/src/global_filters/components/filter_date_from_to_value/filter_date_from_to_value.js b/addons/spreadsheet/static/src/global_filters/components/filter_date_from_to_value/filter_date_from_to_value.js new file mode 100644 index 00000000000..b701b543c0b --- /dev/null +++ b/addons/spreadsheet/static/src/global_filters/components/filter_date_from_to_value/filter_date_from_to_value.js @@ -0,0 +1,40 @@ +/** @odoo-module */ + +import { Component } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; +import { DateTimeInput } from "@web/core/datetime/datetime_input"; +import { serializeDate, deserializeDate } from "@web/core/l10n/dates"; + +export class DateFromToValue extends Component { + static template = "spreadsheet.DateFromToValue"; + static components = { DateTimeInput }; + static props = { + onFromToChanged: Function, + from: { type: String, optional: true }, + to: { type: String, optional: true }, + }; + fromPlaceholder = _t("Date from..."); + toPlaceholder = _t("Date to..."); + + onDateFromChanged(dateFrom) { + this.props.onFromToChanged({ + from: dateFrom && serializeDate(dateFrom.startOf("day")), + to: this.props.to, + }); + } + + onDateToChanged(dateTo) { + this.props.onFromToChanged({ + from: this.props.from, + to: dateTo && serializeDate(dateTo.endOf("day")), + }); + } + + get dateFrom() { + return this.props.from && deserializeDate(this.props.from); + } + + get dateTo() { + return this.props.to && deserializeDate(this.props.to); + } +} diff --git a/addons/spreadsheet/static/src/global_filters/components/filter_date_from_to_value/filter_date_from_to_value.xml b/addons/spreadsheet/static/src/global_filters/components/filter_date_from_to_value/filter_date_from_to_value.xml new file mode 100644 index 00000000000..5aad8c932b9 --- /dev/null +++ b/addons/spreadsheet/static/src/global_filters/components/filter_date_from_to_value/filter_date_from_to_value.xml @@ -0,0 +1,18 @@ + + +
+ + + +
+
diff --git a/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.js b/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.js index 097dc4c2e2b..828d777f8d5 100644 --- a/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.js +++ b/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.js @@ -3,6 +3,7 @@ import { MultiRecordSelector } from "@web/core/record_selectors/multi_record_selector"; import { RELATIVE_DATE_RANGE_TYPES } from "@spreadsheet/helpers/constants"; import { DateFilterValue } from "../filter_date_value/filter_date_value"; +import { DateFromToValue } from "../filter_date_from_to_value/filter_date_from_to_value"; import { Component } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; @@ -48,7 +49,7 @@ export class FilterValue extends Component { } } FilterValue.template = "spreadsheet_edition.FilterValue"; -FilterValue.components = { DateFilterValue, MultiRecordSelector }; +FilterValue.components = { DateFilterValue, DateFromToValue, MultiRecordSelector }; FilterValue.props = { filter: Object, model: Object, diff --git a/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.scss b/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.scss index d421e8203dd..d436da02e91 100644 --- a/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.scss +++ b/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.scss @@ -7,4 +7,9 @@ .o_datepicker_input { color: $o-main-text-color; } + .date_filter_values { + display: flex; + gap: map-get($spacers, 2); + align-items: baseline; + } } diff --git a/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.xml b/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.xml index dc457faf319..7a24eee5945 100644 --- a/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.xml +++ b/addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.xml @@ -2,9 +2,10 @@ -
- + +
@@ -22,10 +23,6 @@ update="(resIds) => this.onTagSelected(filter.id, resIds)" />
- + + +
=", value.from], [field, "<=", value.to]]); + } + if (value.from) { + return new Domain([[field, ">=", value.from]]); + } + if (value.to) { + return new Domain([[field, "<=", value.to]]); + } + return new Domain(); + } + if (filter.rangeType === "relative") { return getRelativeDateDomain(now, offset, value, field, type); } 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 2bdd8af296a..64d46ee0e81 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 @@ -1173,6 +1173,24 @@ QUnit.module("spreadsheet > Global filters model", {}, () => { }); }); + QUnit.test( + "Date filter automatic undefined values for from_to filter", + async function (assert) { + const label = "From to"; + const { model } = await createSpreadsheetWithPivot(); + await addGlobalFilter(model, { + id: "1", + type: "date", + label, + rangeType: "from_to", + }); + assert.deepEqual(model.getters.getGlobalFilterValue("1"), { + from: undefined, + to: undefined, + }); + } + ); + QUnit.test("Date filter automatic default value at model loading", async function (assert) { const label = "This year"; const model = new Model({ @@ -1317,6 +1335,122 @@ QUnit.module("spreadsheet > Global filters model", {}, () => { assertDateDomainEqual(assert, "date", "2016-05-18", "2019-05-17", computedDomain); }); + QUnit.test("from_to date filter at model loading", async function (assert) { + const model = new Model({ + globalFilters: [ + { type: "date", rangeType: "from_to", label: "From To", fields: {}, id: "1" }, + ], + }); + assert.deepEqual(model.getters.getGlobalFilterValue("1"), { + from: undefined, + to: undefined, + }); + }); + + QUnit.test("from_to date filter domain value", async function (assert) { + const { model } = await createSpreadsheetWithPivot(); + /**@type GlobalFilter */ + const filter = { + id: "42", + type: "date", + label: "From To", + rangeType: "from_to", + }; + const value = { + from: "2022-01-01", + to: "2022-05-16", + }; + await addGlobalFilter(model, filter, { pivot: { 1: { chain: "date", type: "date" } } }); + await setGlobalFilterValue(model, { id: "42", value }); + const computedDomain = model.getters.getPivotComputedDomain("1"); + assertDateDomainEqual(assert, "date", "2022-01-01", "2022-05-16", computedDomain); + }); + + QUnit.test( + "set 'from_to' date filter domain value from specific date --> to specific date", + async function (assert) { + const { model } = await createSpreadsheetWithPivot(); + /**@type GlobalFilter */ + const filter = { + id: "42", + type: "date", + label: "From To", + rangeType: "from_to", + }; + const value = { + from: "2022-01-01", + to: "2022-05-16", + }; + await addGlobalFilter(model, filter, { pivot: { 1: { chain: "date", type: "date" } } }); + await setGlobalFilterValue(model, { id: "42", value }); + const computedDomain = model.getters.getPivotComputedDomain("1"); + assertDateDomainEqual(assert, "date", "2022-01-01", "2022-05-16", computedDomain); + } + ); + + QUnit.test( + "set 'from_to' date filter domain value from specific date", + async function (assert) { + const { model } = await createSpreadsheetWithPivot(); + /**@type GlobalFilter */ + const filter = { + id: "42", + type: "date", + label: "From To", + rangeType: "from_to", + }; + const value = { + from: "2022-01-01", + to: undefined, + }; + await addGlobalFilter(model, filter, { pivot: { 1: { chain: "date", type: "date" } } }); + await setGlobalFilterValue(model, { id: "42", value }); + const computedDomain = model.getters.getPivotComputedDomain("1"); + assert.deepEqual(computedDomain, [["date", ">=", "2022-01-01"]]); + } + ); + + QUnit.test("set 'from_to' date filter domain value to specific date", async function (assert) { + const { model } = await createSpreadsheetWithPivot(); + /**@type GlobalFilter */ + const filter = { + id: "42", + type: "date", + label: "From To", + rangeType: "from_to", + }; + const value = { + from: undefined, + to: "2022-05-16", + }; + await addGlobalFilter(model, filter, { pivot: { 1: { chain: "date", type: "date" } } }); + await setGlobalFilterValue(model, { id: "42", value }); + const computedDomain = model.getters.getPivotComputedDomain("1"); + assert.deepEqual(computedDomain, [["date", "<=", "2022-05-16"]]); + }); + + QUnit.test("can clear 'from_to' date filter values", async function (assert) { + const { model } = await createSpreadsheetWithPivot(); + await addGlobalFilter(model, { + id: "42", + type: "date", + label: "From To", + rangeType: "from_to", + }); + const [filter] = model.getters.getGlobalFilters(); + const value = { + from: "2022-01-01", + to: "2022-05-16", + }; + await setGlobalFilterValue(model, { id: "42", value }); + model.dispatch("CLEAR_GLOBAL_FILTER_VALUE", { id: filter.id }); + assert.deepEqual( + model.getters.getGlobalFilterValue(filter.id), + { preventAutomaticValue: true }, + "can clear 'from_to' date filter values" + ); + }); + QUnit.test( "Can set a value to a relation filter from the SET_MANY_GLOBAL_FILTER_VALUE command", async function (assert) { diff --git a/addons/spreadsheet_dashboard/static/src/bundle/dashboard_action/dashboard_action.scss b/addons/spreadsheet_dashboard/static/src/bundle/dashboard_action/dashboard_action.scss index e8799dcdb60..520ef4dfd95 100644 --- a/addons/spreadsheet_dashboard/static/src/bundle/dashboard_action/dashboard_action.scss +++ b/addons/spreadsheet_dashboard/static/src/bundle/dashboard_action/dashboard_action.scss @@ -67,16 +67,16 @@ .o-filter-value { min-height: 25px; min-width: 100px; + flex-basis: 100px; + flex-grow: 1; .o_multi_record_selector { width: 100%; } + } - .date_filter_values { - display: flex; - gap: map-get($spacers, 2); - align-items: baseline; - } + .o-filter-value-double-size { + flex-basis: 200px; } } }