diff options
| author | Mathieu Duckerts-Antoine <dam@odoo.com> | 2020-05-25 18:11:02 +0000 |
|---|---|---|
| committer | Mathieu Duckerts-Antoine <dam@odoo.com> | 2020-06-04 19:32:42 +0000 |
| commit | be5a51a345b8986da0e19627260f64f984e11ae9 (patch) | |
| tree | 4a0b1e5b0bfdad3124718ccf8e67164b5bbe07dd /addons/web/static/src/js/control_panel/control_panel_model.js | |
| parent | 884ef9f7ee345ab1e940dbf88f4432b7e10f36e3 (diff) | |
[IMP] web: Comparison menu
This commit replaces the "time range menu" in the control panel by a new
component: the "comparison menu".
Its purpose is roughly the same: compare a period of data in a reporting
view with the previous period or the previous year.
Its implementation is a bit simpler: it will only appear if at least one
date filter is selected in the search view, and will suggest to compare
each selected date option with the previous period or the previous year.
Task ID: 2245719
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Co-authored-by: Julien Mougenot <jum@odoo.com>
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.js | 555 |
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(); } } |
