Skip to content

Commit db29299

Browse files
hokolomopoLucasLefevre
authored andcommitted
[IMP] spreadsheet: select granularity for date filter
This commit adds the possibility to disable the month or quarter granularity (or both) for fixedPeriod date filters. closes odoo#169922 Task: 3887844 Related: odoo/enterprise#64964 Signed-off-by: Lucas Lefèvre (lul) <[email protected]>
1 parent 265c1be commit db29299

File tree

10 files changed

+191
-62
lines changed

10 files changed

+191
-62
lines changed

addons/spreadsheet/static/src/global_filters/components/filter_date_value/filter_date_value.js

+28-19
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,28 @@
22

33
import { Component, onWillUpdateProps } from "@odoo/owl";
44
import { DateTimeInput } from "@web/core/datetime/datetime_input";
5-
import { FILTER_DATE_OPTION, monthsOptions } from "@spreadsheet/assets_backend/constants";
6-
import { getPeriodOptions } from "@web/search/utils/dates";
5+
import { monthsOptions } from "@spreadsheet/assets_backend/constants";
6+
import { QUARTER_OPTIONS } from "@web/search/utils/dates";
77

88
const { DateTime } = luxon;
99

1010
export class DateFilterValue extends Component {
11+
static template = "spreadsheet_edition.DateFilterValue";
12+
static components = { DateTimeInput };
13+
static props = {
14+
// See @spreadsheet_edition/bundle/global_filters/filters_plugin.RangeType
15+
onTimeRangeChanged: Function,
16+
yearOffset: { type: Number, optional: true },
17+
period: { type: String, optional: true },
18+
disabledPeriods: { type: Array, optional: true },
19+
};
1120
setup() {
1221
this._setStateFromProps(this.props);
13-
onWillUpdateProps(this._setStateFromProps);
14-
this.dateOptions = this.getDateOptions();
22+
this.dateOptions = this.getDateOptions(this.props);
23+
onWillUpdateProps((nextProps) => {
24+
this._setStateFromProps(nextProps);
25+
this.dateOptions = this.getDateOptions(nextProps);
26+
});
1527
}
1628
_setStateFromProps(props) {
1729
this.period = props.period;
@@ -32,12 +44,18 @@ export class DateFilterValue extends Component {
3244
*
3345
* @returns {Array<Object>}
3446
*/
35-
getDateOptions() {
36-
const periodOptions = getPeriodOptions(DateTime.local());
37-
const quarters = FILTER_DATE_OPTION["quarter"].map((quarterId) =>
38-
periodOptions.find((option) => option.id === quarterId)
39-
);
40-
return quarters.concat(monthsOptions);
47+
getDateOptions(props) {
48+
const quarterOptions = Object.values(QUARTER_OPTIONS);
49+
const disabledPeriods = props.disabledPeriods || [];
50+
51+
const dateOptions = [];
52+
if (!disabledPeriods.includes("quarter")) {
53+
dateOptions.push(...quarterOptions);
54+
}
55+
if (!disabledPeriods.includes("month")) {
56+
dateOptions.push(...monthsOptions);
57+
}
58+
return dateOptions;
4159
}
4260

4361
isSelected(periodId) {
@@ -65,12 +83,3 @@ export class DateFilterValue extends Component {
6583
});
6684
}
6785
}
68-
DateFilterValue.template = "spreadsheet_edition.DateFilterValue";
69-
DateFilterValue.components = { DateTimeInput };
70-
71-
DateFilterValue.props = {
72-
// See @spreadsheet_edition/bundle/global_filters/filters_plugin.RangeType
73-
onTimeRangeChanged: Function,
74-
yearOffset: { type: Number, optional: true },
75-
period: { type: String, optional: true },
76-
};

addons/spreadsheet/static/src/global_filters/components/filter_date_value/filter_date_value.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<templates>
33
<div t-name="spreadsheet_edition.DateFilterValue" class="date_filter_values">
4-
<select class="o_input me-3" t-on-change="onPeriodChanged">
4+
<select t-if="dateOptions.length" class="o_input me-3" t-on-change="onPeriodChanged">
55
<option value="empty">Select period...</option>
66
<t t-foreach="dateOptions" t-as="periodOption" t-key="periodOption.id">
77
<option t-if="isSelected(periodOption.id)" selected="1" t-att-value="periodOption.id">

addons/spreadsheet/static/src/global_filters/components/filter_value/filter_value.xml

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<DateFilterValue t-else=""
4242
period="filterValue?.period"
4343
yearOffset="filterValue?.yearOffset"
44+
disabledPeriods="filter.disabledPeriods"
4445
onTimeRangeChanged="(value) => this.onDateInput(filter.id, value)"/>
4546
</div>
4647
<i t-if="getters.isGlobalFilterActive(filter.id)"

addons/spreadsheet/static/src/global_filters/helpers.js

+53-18
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,80 @@ import { Domain } from "@web/core/domain";
55

66
import { CommandResult } from "@spreadsheet/o_spreadsheet/cancelled_reason";
77
import { RELATIVE_DATE_RANGE_TYPES } from "@spreadsheet/helpers/constants";
8+
import { monthsOptions } from "@spreadsheet/assets_backend/constants";
9+
import { QUARTER_OPTIONS } from "@web/search/utils/dates";
810

911
/**
1012
* @typedef {import("@spreadsheet/global_filters/plugins/global_filters_core_plugin").FieldMatching} FieldMatching
1113
*/
1214

13-
export function checkFiltersTypeValueCombination(type, value) {
15+
const monthsOptionsIds = monthsOptions.map((option) => option.id);
16+
const quarterOptionsIds = Object.values(QUARTER_OPTIONS).map((option) => option.id);
17+
18+
/**
19+
* Check if the value is valid for given filter.
20+
* @param {GlobalFilter | CmdGlobalFilter} filter
21+
* @param {any} value
22+
* @returns {boolean}
23+
*/
24+
export function checkFilterValueIsValid(filter, value) {
25+
const { type } = filter;
1426
if (value !== undefined) {
1527
switch (type) {
1628
case "text":
1729
if (typeof value !== "string") {
18-
return CommandResult.InvalidValueTypeCombination;
30+
return false;
1931
}
2032
break;
2133
case "date": {
22-
if (value === "") {
23-
return CommandResult.Success;
24-
} else if (typeof value === "string") {
25-
const expectedValues = RELATIVE_DATE_RANGE_TYPES.map((val) => val.type);
26-
expectedValues.push("this_month", "this_quarter", "this_year");
27-
if (expectedValues.includes(value)) {
28-
return CommandResult.Success;
29-
}
30-
return CommandResult.InvalidValueTypeCombination;
31-
} else if (typeof value !== "object") {
32-
return CommandResult.InvalidValueTypeCombination;
33-
}
34-
break;
34+
return checkDateFilterValueIsValid(filter, value);
3535
}
3636
case "relation":
3737
if (value === "current_user") {
38-
return CommandResult.Success;
38+
return true;
3939
}
4040
if (!Array.isArray(value)) {
41-
return CommandResult.InvalidValueTypeCombination;
41+
return false;
4242
}
4343
break;
4444
}
4545
}
46-
return CommandResult.Success;
46+
return true;
47+
}
48+
49+
/**
50+
* Check if the value is valid for given filter.
51+
* @param {DateGlobalFilter} filter
52+
* @param {any} value
53+
* @returns {boolean}
54+
*/
55+
function checkDateFilterValueIsValid(filter, value) {
56+
if (!value) {
57+
return true;
58+
}
59+
switch (filter.rangeType) {
60+
case "fixedPeriod": {
61+
const period = value.period;
62+
if (!filter.disabledPeriods || !filter.disabledPeriods.length) {
63+
return true;
64+
}
65+
if (filter.disabledPeriods.includes("month")) {
66+
return value !== "this_month" && !monthsOptionsIds.includes(period);
67+
}
68+
if (filter.disabledPeriods.includes("quarter")) {
69+
return value !== "this_quarter" && !quarterOptionsIds.includes(period);
70+
}
71+
return true;
72+
}
73+
case "relative": {
74+
const expectedValues = RELATIVE_DATE_RANGE_TYPES.map((val) => val.type);
75+
expectedValues.push("this_month", "this_quarter", "this_year");
76+
return expectedValues.includes(value);
77+
}
78+
case "from_to":
79+
return typeof value === "object";
80+
}
81+
return true;
4782
}
4883

4984
/**

addons/spreadsheet/static/src/global_filters/plugins/global_filters_core_plugin.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @typedef {"fixedPeriod"|"relative"|"from_to"} RangeType
55
*
66
* @typedef {"last_month" | "last_week" | "last_year" | "last_three_years" | "this_month" | "this_quarter" | "this_year"} RelativePeriod
7+
* @typedef {"quarter" | "month"} FixedPeriods
78
*
89
* @typedef {Object} FieldMatching
910
* @property {string} chain name of the field
@@ -23,6 +24,8 @@
2324
* @property {string} label
2425
* @property {RangeType} rangeType
2526
* @property {RelativePeriod} [defaultValue]
27+
* @property {FixedPeriods[]} [disabledPeriods]
28+
*
2629
*
2730
* @typedef RelationalGlobalFilter
2831
* @property {"relation"} type
@@ -38,7 +41,7 @@ export const globalFiltersFieldMatchers = {};
3841

3942
import * as spreadsheet from "@odoo/o-spreadsheet";
4043
import { CommandResult } from "@spreadsheet/o_spreadsheet/cancelled_reason";
41-
import { checkFiltersTypeValueCombination } from "@spreadsheet/global_filters/helpers";
44+
import { checkFilterValueIsValid } from "@spreadsheet/global_filters/helpers";
4245
import { _t } from "@web/core/l10n/translation";
4346
import { escapeRegExp } from "@web/core/utils/strings";
4447

@@ -52,7 +55,7 @@ export class GlobalFiltersCorePlugin extends spreadsheet.CorePlugin {
5255
/**
5356
* Check if the given command can be dispatched
5457
*
55-
* @param {Object} cmd Command
58+
* @param {import("@spreadsheet").AllCoreCommand} cmd Command
5659
*/
5760
allowDispatch(cmd) {
5861
switch (cmd.type) {
@@ -62,7 +65,10 @@ export class GlobalFiltersCorePlugin extends spreadsheet.CorePlugin {
6265
} else if (this._isDuplicatedLabel(cmd.filter.id, cmd.filter.label)) {
6366
return CommandResult.DuplicatedFilterLabel;
6467
}
65-
return checkFiltersTypeValueCombination(cmd.filter.type, cmd.filter.defaultValue);
68+
if (!checkFilterValueIsValid(cmd.filter, cmd.filter.defaultValue)) {
69+
return CommandResult.InvalidValueTypeCombination;
70+
}
71+
break;
6672
case "REMOVE_GLOBAL_FILTER":
6773
if (!this.getGlobalFilter(cmd.id)) {
6874
return CommandResult.FilterNotFound;
@@ -72,7 +78,10 @@ export class GlobalFiltersCorePlugin extends spreadsheet.CorePlugin {
7278
if (this._isDuplicatedLabel(cmd.filter.id, cmd.filter.label)) {
7379
return CommandResult.DuplicatedFilterLabel;
7480
}
75-
return checkFiltersTypeValueCombination(cmd.filter.type, cmd.filter.defaultValue);
81+
if (!checkFilterValueIsValid(cmd.filter, cmd.filter.defaultValue)) {
82+
return CommandResult.InvalidValueTypeCombination;
83+
}
84+
break;
7685
case "MOVE_GLOBAL_FILTER": {
7786
const index = this.globalFilters.findIndex((filter) => filter.id === cmd.id);
7887
if (index === -1) {

addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js

+22-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { CommandResult } from "@spreadsheet/o_spreadsheet/cancelled_reason";
1818
import { isEmpty } from "@spreadsheet/helpers/helpers";
1919
import { FILTER_DATE_OPTION } from "@spreadsheet/assets_backend/constants";
2020
import {
21-
checkFiltersTypeValueCombination,
21+
checkFilterValueIsValid,
2222
getRelativeDateDomain,
2323
} from "@spreadsheet/global_filters/helpers";
2424
import { RELATIVE_DATE_RANGE_TYPES } from "@spreadsheet/helpers/constants";
@@ -65,7 +65,7 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
6565
/**
6666
* Check if the given command can be dispatched
6767
*
68-
* @param {Object} cmd Command
68+
* @param {import("@spreadsheet").AllCommand} cmd Command
6969
*/
7070
allowDispatch(cmd) {
7171
switch (cmd.type) {
@@ -74,7 +74,10 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
7474
if (!filter) {
7575
return CommandResult.FilterNotFound;
7676
}
77-
return checkFiltersTypeValueCombination(filter.type, cmd.value);
77+
if (!checkFilterValueIsValid(filter, cmd.value)) {
78+
return CommandResult.InvalidValueTypeCombination;
79+
}
80+
break;
7881
}
7982
}
8083
return CommandResult.Success;
@@ -83,19 +86,30 @@ export class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin {
8386
/**
8487
* Handle a spreadsheet command
8588
*
86-
* @param {Object} cmd Command
89+
* @param {import("@spreadsheet").AllCommand} cmd
8790
*/
8891
handle(cmd) {
8992
switch (cmd.type) {
9093
case "ADD_GLOBAL_FILTER":
91-
this.recordsDisplayName[cmd.filter.id] = cmd.filter.defaultValueDisplayNames;
94+
this.recordsDisplayName[cmd.filter.id] =
95+
cmd.filter.type === "relation"
96+
? cmd.filter.defaultValueDisplayNames
97+
: undefined;
9298
break;
9399
case "EDIT_GLOBAL_FILTER": {
94-
const id = cmd.filter.id;
95-
if (this.values[id] && this.values[id].rangeType !== cmd.filter.rangeType) {
100+
const filter = cmd.filter;
101+
const id = filter.id;
102+
if (
103+
filter.type === "date" &&
104+
this.values[id] &&
105+
this.values[id].rangeType !== filter.rangeType
106+
) {
107+
delete this.values[id];
108+
} else if (!checkFilterValueIsValid(filter, this.values[id]?.value)) {
96109
delete this.values[id];
97110
}
98-
this.recordsDisplayName[id] = cmd.filter.defaultValueDisplayNames;
111+
this.recordsDisplayName[id] =
112+
filter.type === "relation" ? filter.defaultValueDisplayNames : undefined;
99113
break;
100114
}
101115
case "SET_GLOBAL_FILTER_VALUE":

0 commit comments

Comments
 (0)