summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/control_panel/control_panel_model.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/control_panel/control_panel_model.js')
-rw-r--r--addons/web/static/src/js/control_panel/control_panel_model.js555
1 files changed, 259 insertions, 296 deletions
diff --git a/addons/web/static/src/js/control_panel/control_panel_model.js b/addons/web/static/src/js/control_panel/control_panel_model.js
index 034c4cd22c7..e1eb9c8da1a 100644
--- a/addons/web/static/src/js/control_panel/control_panel_model.js
+++ b/addons/web/static/src/js/control_panel/control_panel_model.js
@@ -6,11 +6,9 @@ odoo.define('web.ControlPanelModel', function (require) {
const { parseArch } = require('web.viewUtils');
const pyUtils = require('web.py_utils');
- const { COMPARISON_TIME_RANGE_OPTIONS,
- DEFAULT_INTERVAL, DEFAULT_PERIOD, FACET_ICONS,
- INTERVAL_OPTIONS, OPTION_GENERATORS,
- TIME_RANGE_OPTIONS, YEAR_OPTIONS,
- rankPeriod, rankInterval } = require('web.searchUtils');
+ const { DEFAULT_INTERVAL, DEFAULT_PERIOD, FACET_ICONS,
+ getComparisonOptions, getIntervalOptions, getPeriodOptions,
+ constructDateDomain, rankInterval, yearSelected } = require('web.searchUtils');
const FAVORITE_PRIVATE_GROUP = 1;
const FAVORITE_SHARED_GROUP = 2;
@@ -41,10 +39,15 @@ odoo.define('web.ControlPanelModel', function (require) {
* @prop {number} groupId the id of some group, actually the group itself,
* the (active) 'groups' are reconstructed in _getGroups.
* @prop {string} description the description of the filter
- * @prop {string} type 'filter'|'groupBy'|'timeRange'|'field'|'favorite'
+ * @prop {string} type 'filter'|'groupBy'|'comparison'|'field'|'favorite'
*
* Other properties can be present according to the corresponding filter type:
*
+ * • type 'comparison':
+ * @prop {string} comparisonOptionId option identifier (@see COMPARISON_OPTIONS).
+ * @prop {string} dateFilterId the id of a date filter (filter of type 'filter'
+ * with isDateFilter=true)
+ *
* • type 'filter':
* @prop {number} groupNumber used to separate items in the 'Filters' menu
* @prop {string} [context] context
@@ -53,15 +56,15 @@ odoo.define('web.ControlPanelModel', function (require) {
* @if isDefault = true:
* > @prop {number} [defaultRank=-5] used to determine the order of
* > activation of default filters
- * @prop {boolean} [hasOptions] true if the filter comes from an arch node
+ * @prop {boolean} [isDateFilter] true if the filter comes from an arch node
* with a valid 'date' attribute.
- * @if hasOptions = true
+ * @if isDateFilter = true
+ * > @prop {boolean} [hasOptions=true]
* > @prop {string} defaultOptionId option identifier determined by
- * > default_period attribute (@see OPTION_GENERATORS).
+ * > default_period attribute (@see PERIOD_OPTIONS).
* > Default set to DEFAULT_PERIOD.
* > @prop {string} fieldName determined by the value of 'date' attribute
* > @prop {string} fieldType 'date' or 'datetime', type of the corresponding field
- * > @prop {Objecŧ[]} basicDomains of the form { description, domain }[]
* @else
* > @prop {string} domain
*
@@ -79,9 +82,6 @@ odoo.define('web.ControlPanelModel', function (require) {
* > @prop {string} defaultOptionId option identifier (see INTERVAL_OPTIONS)
* default set to DEFAULT_INTERVAL.
*
- * • type 'timeRange':
- * no extra key, a single filter has that type
- *
* • type 'field':
* @prop {string} fieldName
* @prop {string} fieldType
@@ -97,6 +97,8 @@ odoo.define('web.ControlPanelModel', function (require) {
* > @prop {Object} defaultAutocompleteValue of the form { value, label, operator }
*
* • type: 'favorite':
+ * @prop {Object} [comparison] of the form {comparisonId, fieldName, fieldDescription,
+ * range, rangeDescription, comparisonRange, comparisonRangeDescription, }
* @prop {Object} context
* @prop {string} domain
* @prop {string[]} groupBys
@@ -106,7 +108,6 @@ odoo.define('web.ControlPanelModel', function (require) {
* @prop {number} serverSideId
* @prop {number} userId
* @prop {boolean} [isDefault]
- * @prop {Object} [timeRanges] of the form { fieldName, rangeId[, comparisonRangeId] }
*
*-------------------------------------------------------------------------
* Query
@@ -120,17 +121,17 @@ odoo.define('web.ControlPanelModel', function (require) {
*
* Other properties must be defined according to the corresponding filter type.
*
+ * • type 'comparison':
+ * @prop {string} dateFilterId the id of a date filter (filter of type 'filter'
+ * with hasOptions=true)
+ * @prop {string} type 'comparison', help when searching if a comparison is active
+ *
* • type 'filter' with hasOptions=true:
- * @prop {string} optionId option identifier (@see OPTION_GENERATORS)
+ * @prop {string} optionId option identifier (@see PERIOD_OPTIONS)
*
* • type 'groupBy' with hasOptions=true:
* @prop {string} optionId option identifier (@see INTERVAL_OPTIONS)
*
- * • type 'timeRange':
- * @prop {string} fieldName name of a field
- * @prop {string} rangeId option identifier (see TIME_RANGE_OPTIONS)
- * @prop {string} [comparisonRangeId] option identifier (see COMPARISON_TIME_RANGE_OPTIONS)
- *
* • type 'field':
* @prop {string} label description put in the facet (can be temporarilly missing)
* @prop {(string|number)} value used as the value of the generated domain
@@ -173,6 +174,17 @@ odoo.define('web.ControlPanelModel', function (require) {
}
//---------------------------------------------------------------------
+ // Getters
+ //---------------------------------------------------------------------
+
+ /**
+ * @returns {(Object | undefined)}
+ */
+ get activeComparison() {
+ return this.state.query.find(queryElem => queryElem.type === 'comparison');
+ }
+
+ //---------------------------------------------------------------------
// Public
//---------------------------------------------------------------------
@@ -196,30 +208,6 @@ odoo.define('web.ControlPanelModel', function (require) {
}
/**
- * Activate the unique filter of type 'timeRange' with provided 'options' fieldName, rangeId,
- * and optional comparisonRangeId.
- * @param {string} fieldName
- * @param {string} rangeId
- * @param {string} [comparisonRangeId]
- */
- activateTimeRange(fieldName, rangeId, comparisonRangeId) {
- const filter = Object.values(this.state.filters).find(f => f.type === 'timeRange');
- const detail = { fieldName, rangeId };
- if (comparisonRangeId) {
- detail.comparisonRangeId = comparisonRangeId;
- }
- const queryElem = this.state.query.find(queryElem => queryElem.filterId === filter.id);
- if (queryElem) {
- Object.assign(queryElem, detail);
- if (!comparisonRangeId) {
- delete queryElem.comparisonRangeId;
- }
- } else {
- this.state.query.push(Object.assign({ groupId: filter.groupId, filterId: filter.id }, detail));
- }
- }
-
- /**
* Remove all the query elements from query.
*/
clearQuery() {
@@ -311,6 +299,7 @@ odoo.define('web.ControlPanelModel', function (require) {
this.state.query = this.state.query.filter(
queryElem => queryElem.groupId !== groupId
);
+ this._checkComparisonStatus();
}
/**
@@ -347,7 +336,7 @@ odoo.define('web.ControlPanelModel', function (require) {
*/
getFacets() {
const accept = type => {
- if (type === 'groupBy' && !this.searchMenuTypes.includes('groupBy')) {
+ if (['groupBy', 'comparison'].includes(type) && !this.searchMenuTypes.includes(type)) {
return false;
}
return true;
@@ -384,11 +373,13 @@ odoo.define('web.ControlPanelModel', function (require) {
const filters = Object.values(this.state.filters).reduce(
(filters, filter) => {
if (filter.type === type && !filter.invisible) {
- const activities = this.state.query.filter(
+ const filterQueryElements = this.state.query.filter(
queryElem => queryElem.filterId === filter.id
);
- const enrichedFilter = this._enrichFilterCopy(filter, activities);
- filters.push(enrichedFilter);
+ const enrichedFilter = this._enrichFilterCopy(filter, filterQueryElements);
+ if (enrichedFilter) {
+ filters.push(enrichedFilter);
+ }
}
return filters;
},
@@ -419,7 +410,7 @@ odoo.define('web.ControlPanelModel', function (require) {
} else {
query.groupBy = [];
}
- if (this.searchMenuTypes.includes('timeRange')) {
+ if (this.searchMenuTypes.includes('comparison')) {
const timeRanges = this._getTimeRanges(requireEvaluation);
query.timeRanges = timeRanges || {};
}
@@ -442,6 +433,24 @@ odoo.define('web.ControlPanelModel', function (require) {
search() { }
/**
+ * Activate/Deactivate a filter of type 'comparison' with provided id.
+ * At most one filter of type 'comparison' can be activated at every time.
+ * @param {string} filterId
+ */
+ toggleComparison(filterId) {
+ const { groupId, dateFilterId } = this.state.filters[filterId];
+ const queryElem = this.state.query.find(queryElem =>
+ queryElem.type === 'comparison' &&
+ queryElem.filterId === filterId
+ );
+ // make sure only one comparison can be active
+ this.state.query = this.state.query.filter(queryElem => queryElem.type !== 'comparison');
+ if (!queryElem) {
+ this.state.query.push({ groupId, filterId, dateFilterId, type: 'comparison', });
+ }
+ }
+
+ /**
* Activate or deactivate the simple filter with given filterId, i.e.
* add or remove a corresponding query element.
* @param {string} filterId
@@ -471,22 +480,15 @@ odoo.define('web.ControlPanelModel', function (require) {
toggleFilterWithOptions(filterId, optionId) {
const filter = this.state.filters[filterId];
optionId = optionId || filter.defaultOptionId;
-
- const noYearSelected = (filterId) => !this.state.query.some(queryElem => {
- return queryElem.filterId === filterId && YEAR_OPTIONS[queryElem.optionId];
- });
-
- const defaultYearId = (optionId) => {
- const year = this.optionGenerators.find(o => o.id === optionId).defaultYear;
- return this.optionGenerators.find(o => o.setParam.year === year).id;
- };
+ const option = this.optionGenerators.find(o => o.id === optionId);
const index = this.state.query.findIndex(
queryElem => queryElem.filterId === filterId && queryElem.optionId === optionId
);
+
if (index >= 0) {
this.state.query.splice(index, 1);
- if (filter.type === 'filter' && noYearSelected(filterId)) {
+ if (filter.type === 'filter' && !yearSelected(this._getSelectedOptionIds(filterId))) {
// This is the case where optionId was the last option
// of type 'year' to be there before being removed above.
// Since other options of type 'month' or 'quarter' do
@@ -497,16 +499,19 @@ odoo.define('web.ControlPanelModel', function (require) {
}
} else {
this.state.query.push({ groupId: filter.groupId, filterId, optionId });
- if (filter.type === 'filter' && noYearSelected(filterId)) {
+ if (filter.type === 'filter' && !yearSelected(this._getSelectedOptionIds(filterId))) {
// Here we add 'this_year' as options if no option of type
// year is already selected.
this.state.query.push({
groupId: filter.groupId,
filterId,
- optionId: defaultYearId(optionId),
+ optionId: option.defaultYearId,
});
}
}
+ if (filter.type === 'filter') {
+ this._checkComparisonStatus();
+ }
}
/**
@@ -535,16 +540,6 @@ odoo.define('web.ControlPanelModel', function (require) {
//---------------------------------------------------------------------
/**
- * Activate the filter of type timeRange with the options set in the key
- * time_ranges of actionContext (if any).
- * @private
- */
- _activateDefaultTimeRanges() {
- const { field, range, comparisonRange } = this.actionContext.time_ranges;
- this.activateTimeRange(field, range, comparisonRange);
- }
-
- /**
* Activate the default favorite (if any) or all default filters.
* @private
*/
@@ -583,25 +578,109 @@ odoo.define('web.ControlPanelModel', function (require) {
}
});
}
- if (this.actionContext.time_ranges) {
- this._activateDefaultTimeRanges();
- }
}
/**
* This function populates the 'filters' object at initialization.
* The filters come from:
- * - config.viewInfo.arch (types 'filter', 'groupBy', 'field'),
+ * - config.viewInfo.arch (types 'comparison', 'filter', 'groupBy', 'field'),
* - config.dynamicFilters (type 'filter'),
* - config.viewInfo.favoriteFilters (type 'favorite'),
- * - code itself (type 'timeRange')
* @private
*/
_addFilters() {
this._createGroupOfFiltersFromArch();
this._createGroupOfDynamicFilters();
this._createGroupOfFavorites();
- this._createGroupOfTimeRanges();
+ }
+
+ /**
+ * If a comparison is active, check if it should become inactive.
+ * The comparison should become inactive if the corresponding date filter has become
+ * inactive.
+ * @private
+ */
+ _checkComparisonStatus() {
+ const activeComparison = this.activeComparison;
+ if (!activeComparison) {
+ return;
+ }
+ const { dateFilterId } = activeComparison;
+ const dateFilterIsActive = this.state.query.some(
+ queryElem => queryElem.filterId === dateFilterId
+ );
+ if (!dateFilterIsActive) {
+ this.state.query = this.state.query.filter(
+ queryElem => queryElem.type !== 'comparison'
+ );
+ }
+ }
+
+ /**
+ * Returns the active comparison timeRanges object.
+ * @private
+ * @param {Object} comparisonFilter
+ * @returns {Object | undefined}
+ */
+ _computeTimeRanges(comparisonFilter) {
+ const { filterId } = this.activeComparison;
+ if (filterId !== comparisonFilter.id) {
+ return;
+ }
+ const { dateFilterId, comparisonOptionId } = comparisonFilter;
+ const {
+ fieldName,
+ fieldType,
+ description: dateFilterDescription,
+ } = this.state.filters[dateFilterId];
+
+ const selectedOptionIds = this._getSelectedOptionIds(dateFilterId);
+
+ // compute range and range description
+ const { domain: range, description: rangeDescription } = constructDateDomain(
+ this.referenceMoment, fieldName, fieldType, selectedOptionIds,
+ );
+
+ // compute comparisonRange and comparisonRange description
+ const {
+ domain: comparisonRange,
+ description: comparisonRangeDescription,
+ } = constructDateDomain(
+ this.referenceMoment, fieldName, fieldType, selectedOptionIds, comparisonOptionId
+ );
+
+ return {
+ comparisonId: comparisonOptionId,
+ fieldName,
+ fieldDescription: dateFilterDescription,
+ range,
+ rangeDescription,
+ comparisonRange,
+ comparisonRangeDescription,
+ };
+ }
+
+ /**
+ * Starting from the array of date filters, create the filters of type
+ * 'comparison'.
+ * @private
+ * @param {Object[]} dateFilters
+ */
+ _createGroupOfComparisons(dateFilters) {
+ const preFilters = [];
+ for (const dateFilter of dateFilters) {
+ for (const comparisonOption of this.comparisonOptions) {
+ const { id: dateFilterId, description } = dateFilter;
+ const preFilter = {
+ type: 'comparison',
+ comparisonOptionId: comparisonOption.id,
+ description: `${description}: ${comparisonOption.description}`,
+ dateFilterId,
+ };
+ preFilters.push(preFilter);
+ }
+ }
+ this._createGroupOfFilters(preFilters);
}
/**
@@ -717,15 +796,12 @@ odoo.define('web.ControlPanelModel', function (require) {
if (pregroupOfGroupBys.length) {
this._createGroupOfFilters(pregroupOfGroupBys);
}
- }
-
- /**
- * Add a single filter of type 'timeRange' to filters.
- * @private
- */
- _createGroupOfTimeRanges() {
- const pregroup = [{ type: 'timeRange' }];
- this._createGroupOfFilters(pregroup);
+ const dateFilters = Object.values(this.state.filters).filter(
+ (filter) => filter.isDateFilter
+ );
+ if (dateFilters.length) {
+ this._createGroupOfComparisons(dateFilters);
+ }
}
/**
@@ -734,19 +810,21 @@ odoo.define('web.ControlPanelModel', function (require) {
* mechanism different from __notifyComponents. Here we use the fact
* that the controlPanelModel is an (owl) EventBus to communicate with
* the parent.
+ * @private
*/
_dispatch() {
this.trigger('search', this.getQuery());
}
/**
- * Returns a copy of the provided filter with additional information
+ * Returns undefined or a copy of the provided filter with additional information
* used only outside of the control panel model, like in search bar or in the
- * various menus.
+ * various menus. The value undefined is returned if the filter should not appear
+ * for some reason.
* @private
* @param {Object} filter
* @param {Object[]} filterQueryElements
- * @returns {Object}
+ * @returns {Object|undefined}
*/
_enrichFilterCopy(filter, filterQueryElements) {
const isActive = Boolean(filterQueryElements.length);
@@ -761,6 +839,16 @@ odoo.define('web.ControlPanelModel', function (require) {
}
switch (f.type) {
+ case 'comparison': {
+ const { dateFilterId } = filter;
+ const dateFilterIsActive = this.state.query.some(
+ queryElem => queryElem.filterId === dateFilterId
+ );
+ if (!dateFilterIsActive) {
+ return;
+ }
+ break;
+ }
case 'filter':
if (f.hasOptions) {
f.options = _enrichOptions(this.optionGenerators);
@@ -776,12 +864,6 @@ odoo.define('web.ControlPanelModel', function (require) {
({ label, value, operator }) => ({ label, value, operator })
);
break;
- case 'timeRange':
- if (filterQueryElements.length) {
- const timeRange = this._extractTimeRange(filterQueryElements[0]);
- Object.assign(f, timeRange);
- }
- break;
}
return f;
}
@@ -842,11 +924,11 @@ odoo.define('web.ControlPanelModel', function (require) {
filter.context = attrs.context;
}
if (attrs.date) {
+ filter.isDateFilter = true;
filter.hasOptions = true;
filter.fieldName = attrs.date;
filter.fieldType = this.fields[attrs.date].type;
filter.defaultOptionId = attrs.default_period || DEFAULT_PERIOD;
- filter.basicDomains = this._getDateFilterBasicDomains(filter);
} else {
filter.domain = attrs.domain || '[]';
}
@@ -865,7 +947,7 @@ odoo.define('web.ControlPanelModel', function (require) {
filter.defaultRank = attrs.defaultRank;
}
break;
- case 'field':
+ case 'field': {
const field = this.fields[attrs.name];
filter.fieldName = attrs.name;
filter.fieldType = field.type;
@@ -885,6 +967,7 @@ odoo.define('web.ControlPanelModel', function (require) {
filter.defaultAutocompleteValue = attrs.defaultAutocompleteValue;
}
break;
+ }
}
if (filter.fieldName && !attrs.string) {
const { string } = this.fields[filter.fieldName];
@@ -893,76 +976,6 @@ odoo.define('web.ControlPanelModel', function (require) {
}
/**
- * For filter of type 'filter' with hasOptions=true,
- * returns an array of domains or descriptions according to key.
- * @param {Object} filter
- * @param {Object[]} filterQueryElements
- * @param {'domain'|'description'} key
- * @returns {string[]}
- */
- _extractInfoFromBasicDomains(filter, filterQueryElements, key) {
- const results = [];
- const yearIds = [];
- const otherOptionIds = [];
- filterQueryElements.forEach(({ optionId }) => {
- if (YEAR_OPTIONS[optionId]) {
- yearIds.push(optionId);
- } else {
- otherOptionIds.push(optionId);
- }
- });
-
- const sortOptionIds = (a, b) => rankPeriod(a) - rankPeriod(b);
- yearIds.sort(sortOptionIds);
- otherOptionIds.sort(sortOptionIds);
-
- // the following case corresponds to years selected only
- if (otherOptionIds.length === 0) {
- yearIds.forEach(yearId => {
- const d = filter.basicDomains[yearId];
- results.push(d[key]);
- });
- } else {
- otherOptionIds.forEach(optionId => {
- yearIds.forEach(yearId => {
- const d = filter.basicDomains[`${yearId}__${optionId}`];
- results.push(d[key]);
- });
- });
- }
- return results;
- }
-
- /**
- * Construct a timeRange object from the given fieldName, rangeId, comparisonRangeId
- * parameters.
- * @private
- * @param {string} fieldName
- * @param {string} rangeId
- * @param {string} comparisonRangeId
- * @returns {Object}
- */
- _extractTimeRange({ fieldName, rangeId, comparisonRangeId }) {
- const field = this.fields[fieldName];
- const timeRange = {
- fieldName,
- fieldDescription: field.string || fieldName,
- rangeId,
- range: Domain.prototype.constructDomain(fieldName, rangeId, field.type),
- rangeDescription: TIME_RANGE_OPTIONS[rangeId].description.toString(),
- };
- if (comparisonRangeId) {
- timeRange.comparisonRangeId = comparisonRangeId;
- timeRange.comparisonRange = Domain.prototype.constructDomain(
- fieldName, rangeId, field.type, comparisonRangeId
- );
- const { description } = COMPARISON_TIME_RANGE_OPTIONS[comparisonRangeId];
- timeRange.comparisonRangeDescription = description.toString();
- }
- return timeRange;
- }
-
- /**
* Returns an object irFilter serving to create an ir_filte in db
* starting from a filter of type 'favorite'.
* @private
@@ -1003,13 +1016,8 @@ odoo.define('web.ControlPanelModel', function (require) {
if ('groupBys' in favorite) {
context.group_by = favorite.groupBys;
}
- if ('timeRanges' in favorite) {
- const { fieldName, rangeId, comparisonRangeId } = favorite.timeRanges;
- context.time_ranges = {
- field: fieldName,
- range: rangeId,
- comparisonRange: comparisonRangeId,
- };
+ if ('comparison' in favorite) {
+ context.comparison = favorite.comparison;
}
if (Object.keys(context).length) {
irFilter.context = context;
@@ -1078,56 +1086,21 @@ odoo.define('web.ControlPanelModel', function (require) {
}
/**
- * Construct an object containing domains based on this.referenceMoment and
- * the field associated with the provided date filter.
- * @private
- * @param {Object} filter
- * @returns {Object}
- */
- _getDateFilterBasicDomains({ fieldName, fieldType }) {
- const _constructBasicDomain = (y, o) => {
- const setParam = Object.assign({}, y.setParam, o ? o.setParam : {});
- const granularity = o ? o.granularity : y.granularity;
- const date = this.referenceMoment.clone().set(setParam);
- let leftBound = date.clone().startOf(granularity).locale('en');
- let rightBound = date.clone().endOf(granularity).locale('en');
- if (fieldType === 'date') {
- leftBound = leftBound.format('YYYY-MM-DD');
- rightBound = rightBound.format('YYYY-MM-DD');
- } else {
- leftBound = leftBound.utc().format('YYYY-MM-DD HH:mm:ss');
- rightBound = rightBound.utc().format('YYYY-MM-DD HH:mm:ss');
- }
- const domain = Domain.prototype.arrayToString([
- '&',
- [fieldName, '>=', leftBound],
- [fieldName, '<=', rightBound]
- ]);
- const description = o ? o.description + " " + y.description : y.description;
- return { domain, description };
- };
-
- const domains = {};
- this.optionGenerators.filter(y => y.groupNumber === 2).forEach(y => {
- domains[y.id] = _constructBasicDomain(y);
- this.optionGenerators.filter(y => y.groupNumber === 1).forEach(o => {
- domains[y.id + '__' + o.id] = _constructBasicDomain(y, o);
- });
- });
- return domains;
- }
-
- /**
- * Compute the string representation of the current domain associated to a date filter
- * starting from its corresponding query elements.
+ * Compute the string representation or the description of the current domain associated
+ * with a date filter starting from its corresponding query elements.
* @private
* @param {Object} filter
- * @param {Objec[]} filterQueryElements
+ * @param {Object[]} filterQueryElements
+ * @param {'domain'|'description'} [key='domain']
* @returns {string}
*/
- _getDateFilterDomain(filter, filterQueryElements) {
- const domains = this._extractInfoFromBasicDomains(filter, filterQueryElements, 'domain');
- return pyUtils.assembleDomains(domains, 'OR');
+ _getDateFilterDomain(filter, filterQueryElements, key = 'domain') {
+ const { fieldName, fieldType } = filter;
+ const selectedOptionIds = filterQueryElements.map(queryElem => queryElem.optionId);
+ const dateFilterRange = constructDateDomain(
+ this.referenceMoment, fieldName, fieldType, selectedOptionIds,
+ );
+ return dateFilterRange[key];
}
/**
@@ -1205,6 +1178,10 @@ odoo.define('web.ControlPanelModel', function (require) {
*/
_getFilterDomain(filter, filterQueryElements) {
if (filter.type === 'filter' && filter.hasOptions) {
+ const { dateFilterId } = this.activeComparison || {};
+ if (this.searchMenuTypes.includes('comparison') && dateFilterId === filter.id) {
+ return "[]";
+ }
return this._getDateFilterDomain(filter, filterQueryElements);
} else if (filter.type === 'field') {
return this._getAutoCompletionFilterDomain(filter, filterQueryElements);
@@ -1307,7 +1284,7 @@ odoo.define('web.ControlPanelModel', function (require) {
/**
* Reconstruct the (active) groups from the query elements.
* @private
- * @returns {Objec[]}
+ * @returns {Object[]}
*/
_getGroups() {
const groups = this.state.query.reduce(
@@ -1356,37 +1333,20 @@ odoo.define('web.ControlPanelModel', function (require) {
}
/**
- * Returns the last timeRanges object found in the query.
- * TimeRanges objects can be associated with filters of type 'favorite'
- * or 'timeRange'.
+ * Starting from the id of a date filter, returns the array of option ids currently selected
+ * for the corresponding filter.
* @private
- * @param {boolean} [evaluation=false]
- * @returns {(Object|undefined)}
+ * @param {string} dateFilterId
+ * @returns {string[]}
*/
- _getTimeRanges(evaluation = false) {
- let timeRanges;
- for (const queryElem of this.state.query.slice().reverse()) {
- const filter = this.state.filters[queryElem.filterId];
- if (filter.type === 'timeRange') {
- timeRanges = this._extractTimeRange(queryElem);
- break;
- } else if (filter.type === 'favorite' && filter.timeRanges) {
- // we want to make sure that last is not observed! (it is change below in case of evaluation)
- timeRanges = this._extractTimeRange(filter.timeRanges);
- break;
- }
- }
- if (timeRanges) {
- if (evaluation) {
- timeRanges.range = Domain.prototype.stringToArray(timeRanges.range);
- if (timeRanges.comparisonRangeId) {
- timeRanges.comparisonRange = Domain.prototype.stringToArray(
- timeRanges.comparisonRange
- );
- }
+ _getSelectedOptionIds(dateFilterId) {
+ const selectedOptionIds = [];
+ for (const queryElem of this.state.query) {
+ if (queryElem.filterId === dateFilterId) {
+ selectedOptionIds.push(queryElem.optionId);
}
- return timeRanges;
}
+ return selectedOptionIds;
}
/**
@@ -1407,15 +1367,10 @@ odoo.define('web.ControlPanelModel', function (require) {
groupBys = context.group_by;
delete context.group_by;
}
- let timeRanges;
- if (context.time_ranges) {
- const { field, range, comparisonRange } = context.time_ranges;
- timeRanges = this._extractTimeRange({
- fieldName: field,
- rangeId: range,
- comparisonRangeId: comparisonRange,
- });
- delete context.time_ranges;
+ let comparison;
+ if (context.comparison) {
+ comparison = context.comparison;
+ delete context.comparison;
}
let sort;
try {
@@ -1460,8 +1415,8 @@ odoo.define('web.ControlPanelModel', function (require) {
if (irFilter.is_default) {
favorite.isDefault = irFilter.is_default;
}
- if (timeRanges) {
- favorite.timeRanges = timeRanges;
+ if (comparison) {
+ favorite.comparison = comparison;
}
return favorite;
}
@@ -1470,6 +1425,7 @@ odoo.define('web.ControlPanelModel', function (require) {
* Group the query elements in group.activities by qe -> qe.filterId
* and changes the form of group.activities to make it more suitable for further
* computations.
+ * @private
* @param {Object} group
*/
_mergeActivities(group) {
@@ -1477,7 +1433,7 @@ odoo.define('web.ControlPanelModel', function (require) {
let res = [];
switch (type) {
case 'filter':
- case 'groupBy':
+ case 'groupBy': {
for (const activity of activities) {
const { filterId } = activity;
let a = res.find(({ filter }) => filter.id === filterId);
@@ -1491,9 +1447,10 @@ odoo.define('web.ControlPanelModel', function (require) {
a.filterQueryElements.push(activity);
}
break;
+ }
case 'favorite':
case 'field':
- case 'timeRange':
+ case 'comparison': {
// all activities in the group have same filterId
const { filterId } = group.activities[0];
const filter = this.state.filters[filterId];
@@ -1502,6 +1459,7 @@ odoo.define('web.ControlPanelModel', function (require) {
filterQueryElements: group.activities
});
break;
+ }
}
if (type === 'groupBy') {
res.forEach(activity => {
@@ -1608,7 +1566,7 @@ odoo.define('web.ControlPanelModel', function (require) {
userId,
});
if (timeRanges) {
- preFilter.timeRanges = timeRanges;
+ preFilter.comparison = timeRanges;
}
const irFilter = this._favoriteToIrFilter(preFilter);
const serverSideId = await this.env.dataManager.create_filter(irFilter);
@@ -1648,20 +1606,13 @@ odoo.define('web.ControlPanelModel', function (require) {
} else {
let facetDescription;
for (const { filter, filterQueryElements } of activities) {
- if (type === 'timeRange') {
- const tr = this._extractTimeRange(filterQueryElements[0]);
- facetDescription = `${tr.fieldDescription}: ${tr.rangeDescription}`;
- if (tr.comparisonRangeDescription) {
- facetDescription += ` / ${tr.comparisonRangeDescription}`;
- }
- } else { // filter and favorite
- facetDescription = filter.description;
- if (filter.hasOptions) {
- const descriptions = this._extractInfoFromBasicDomains(
- filter, filterQueryElements, 'description'
- );
- facetDescription += `: ${descriptions.join(" / ")}`;
- }
+ // filter, favorite and comparison
+ facetDescription = filter.description;
+ if (filter.isDateFilter) {
+ const description = this._getDateFilterDomain(
+ filter, filterQueryElements, 'description'
+ );
+ facetDescription += `: ${description}`;
}
facetDescriptions.push(facetDescription);
}
@@ -1670,6 +1621,35 @@ odoo.define('web.ControlPanelModel', function (require) {
}
/**
+ * Returns the last timeRanges object found in the query.
+ * TimeRanges objects can be associated with filters of type 'favorite'
+ * or 'comparison'.
+ * @private
+ * @param {boolean} [evaluation=false]
+ * @returns {(Object|undefined)}
+ */
+ _getTimeRanges(evaluation) {
+ let timeRanges;
+ for (const queryElem of this.state.query.slice().reverse()) {
+ const filter = this.state.filters[queryElem.filterId];
+ if (filter.type === 'comparison') {
+ timeRanges = this._computeTimeRanges(filter);
+ break;
+ } else if (filter.type === 'favorite' && filter.comparison) {
+ timeRanges = filter.comparison;
+ break;
+ }
+ }
+ if (timeRanges) {
+ if (evaluation) {
+ timeRanges.range = Domain.prototype.stringToArray(timeRanges.range);
+ timeRanges.comparisonRange = Domain.prototype.stringToArray(timeRanges.comparisonRange);
+ }
+ return timeRanges;
+ }
+ }
+
+ /**
* Using the constructor parameter object config, set most of the properties
* of the control panel model.
* @private
@@ -1713,26 +1693,9 @@ odoo.define('web.ControlPanelModel', function (require) {
this.dynamicFilters = config.dynamicFilters || [];
this.referenceMoment = moment();
- const setDescriptions = options => {
- return Object.values(options).map(o => {
- const oClone = JSON.parse(JSON.stringify(o));
- const description = o.description ?
- o.description.toString() :
- this.referenceMoment.clone().add(o.addParam).format(o.format);
- return Object.assign(oClone, { description });
- });
- };
- const process = (options) => {
- return options.map(o => {
- const date = this.referenceMoment.clone().set(o.setParam).add(o.addParam);
- delete o.addParam;
- o.setParam[o.granularity] = date[o.granularity]();
- o.defaultYear = date.year();
- return o;
- });
- };
- this.optionGenerators = process(setDescriptions(OPTION_GENERATORS));
- this.intervalOptions = setDescriptions(INTERVAL_OPTIONS);
+ this.optionGenerators = getPeriodOptions(this.referenceMoment);
+ this.intervalOptions = getIntervalOptions();
+ this.comparisonOptions = getComparisonOptions();
}
}