From 95f40530f344df3f38d67e5c092b0862e866c5e9 Mon Sep 17 00:00:00 2001 From: Mathieu Duckerts-Antoine Date: Tue, 28 Jan 2020 11:42:59 +0000 Subject: [PATCH] [FIX] web: fix date filters Issue If month - 1 or month - 2 are in the previous year, the filter is incorrect. ex: Date = Jan. 2020, Filter = Dec. 2019 => last_year__last_month = 2018-12 instead 2019-12 Cause If date is 2020-01-20 Selecting December => adding param "last_month" Selecting 2019 => adding param "last_year" Applying "last_month" => date become 2019-12-01 Applying "last_year" => date become 2018-12-01 Solution Detect the right year to activate by default when a month is selected and no year is selected. With that solution it is not possible to get records in Jan. 2020 or in Dec. 2019 only for instance but the global functioning of date filters stays the same as before. OPW-2169528 closes odoo/odoo#44208 X-original-commit: 096fdc7d3fe0799aa761c6ad3597569a002c698e Signed-off-by: Nicolas Lempereur (nle) Co-authored-by: Jason Van Malder --- .../control_panel/control_panel_model.js | 14 ++++--- .../views/control_panel/control_panel_view.js | 40 ++++++++++-------- .../static/tests/views/search_view_tests.js | 41 ++++++++++++++++++- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/addons/web/static/src/js/views/control_panel/control_panel_model.js b/addons/web/static/src/js/views/control_panel/control_panel_model.js index dc7ab303abd..80a6a6a441d 100644 --- a/addons/web/static/src/js/views/control_panel/control_panel_model.js +++ b/addons/web/static/src/js/views/control_panel/control_panel_model.js @@ -420,7 +420,7 @@ var ControlPanelModel = mvc.Model.extend({ } if (filter.currentOptionIds) { filter.currentOptionIds.clear(); - } + } }) group.activeFilterIds = []; }); @@ -459,9 +459,13 @@ var ControlPanelModel = mvc.Model.extend({ acc.push(y.optionId); } return acc; - }, + }, [] ); + const defaultYearId = (optionId) => { + const year = filter.options.find(o => o.optionId === optionId).defaultYear; + return filter.options.find(o => o.setParam.year === year).optionId; + }; if (filter.type === 'filter') { const alreadyActive = group.activeFilterIds.some(isEqualTo([filterId])); @@ -486,7 +490,7 @@ var ControlPanelModel = mvc.Model.extend({ filter.currentOptionIds.add(optionId); if (!selectedYears().length) { // Here we add 'this_year' as options if no option of type year is already selected. - filter.currentOptionIds.add('this_year'); + filter.currentOptionIds.add(defaultYearId(optionId)); } } } else if (filter.type === 'groupBy') { @@ -726,8 +730,8 @@ var ControlPanelModel = mvc.Model.extend({ return pyUtils.assembleDomains(domains, 'AND'); }, /** - * Return an array containing 'facets' used to create the content of the search bar. - * + * Return an array containing 'facets' used to create the content of the search bar. + * * @returns {Object} */ _getFacets: function () { diff --git a/addons/web/static/src/js/views/control_panel/control_panel_view.js b/addons/web/static/src/js/views/control_panel/control_panel_view.js index 0c862c7cc72..c2d4e3ebf33 100644 --- a/addons/web/static/src/js/views/control_panel/control_panel_view.js +++ b/addons/web/static/src/js/views/control_panel/control_panel_view.js @@ -13,7 +13,6 @@ var Domain = require('web.Domain'); var DEFAULT_INTERVAL = controlPanelViewParameters.DEFAULT_INTERVAL; var DEFAULT_PERIOD = controlPanelViewParameters.DEFAULT_PERIOD; var INTERVAL_OPTIONS = controlPanelViewParameters.INTERVAL_OPTIONS; -var PERIOD_OPTIONS = controlPanelViewParameters.PERIOD_OPTIONS; const OPTION_GENERATORS = controlPanelViewParameters.OPTION_GENERATORS; var Factory = mvc.Factory; @@ -74,18 +73,28 @@ var ControlPanelView = Factory.extend({ this.fields = viewInfo.fields; this.referenceMoment = moment(); - this.optionGenerators = OPTION_GENERATORS.map(o => { - const description = o.description ? + + const setDescriptions = (options) => { + return options.map(o => { + const oClone = JSON.parse(JSON.stringify(o)); + const description = o.description ? o.description.toString () : - this.referenceMoment.clone().set(o.setParam).add(o.addParam).format(o.format); - return _.extend({}, o, {description: description}); - }); - PERIOD_OPTIONS = PERIOD_OPTIONS.map(option => - _.extend({}, option, {description: option.description.toString()}) - ); - INTERVAL_OPTIONS = INTERVAL_OPTIONS.map(option => - _.extend({}, option, {description: option.description.toString()}) - ); + this.referenceMoment.clone().add(o.addParam).format(o.format); + return _.extend(oClone, {description: 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.controllerParams.modelName = params.modelName; @@ -196,7 +205,7 @@ var ControlPanelView = Factory.extend({ filter.fieldType = this.fields[attrs.fieldName].type; if (_.contains(['date', 'datetime'], filter.fieldType)) { filter.hasOptions = true; - filter.options = INTERVAL_OPTIONS; + filter.options = this.intervalOptions; filter.defaultOptionId = attrs.defaultInterval || DEFAULT_INTERVAL; filter.currentOptionIds = new Set(); @@ -227,10 +236,9 @@ var ControlPanelView = Factory.extend({ */ _getDateFilterBasicDomains: function (filter) { const _constructBasicDomain = (y, o) => { - const addParam = _.extend({}, y.addParam, o ? o.addParam : {}); - const setParam = _.extend({}, y.setParam, o ? o.setParam : {}); + const setParam = Object.assign({}, y.setParam, o ? o.setParam : {}); const granularity = o ? o.granularity : y.granularity; - const date = this.referenceMoment.clone().set(setParam).add(addParam); + const date = this.referenceMoment.clone().set(setParam); let leftBound = date.clone().startOf(granularity).locale('en'); let rightBound = date.clone().endOf(granularity).locale('en'); diff --git a/addons/web/static/tests/views/search_view_tests.js b/addons/web/static/tests/views/search_view_tests.js index 5d680488981..96e8d0b552b 100644 --- a/addons/web/static/tests/views/search_view_tests.js +++ b/addons/web/static/tests/views/search_view_tests.js @@ -753,7 +753,7 @@ QUnit.module('Search View', { var self = this; this.archs['partner,4,search'] = ''+ - '' + + '' + ''; var unpatchDate = patchDate(2017,2,22,1,0,0); @@ -803,6 +803,45 @@ QUnit.module('Search View', { unpatchDate(); }); + QUnit.test('filter by a date field using period works even in January', async function (assert) { + assert.expect(3); + + this.archs['partner,4,search'] = ''+ + '' + + ''; + + var unpatchDate = patchDate(2017,0,7,3,0,0); + + var actionManager = await createActionManager({ + actions: this.actions, + archs: this.archs, + data: this.data, + mockRPC: function(route, args) { + if (route === '/web/dataset/search_read' && args.domain.length) { + assert.deepEqual(args.domain, + ['&', ["date_field", ">=", "2016-12-01"], ["date_field", "<=", "2016-12-31"]] + ); + } + return this._super.apply(this, arguments); + }, + }); + + this.actions[4].context = {search_default_some_filter: true}; + + await actionManager.doAction(5); + + // open menu 'Filter' + await testUtils.dom.click($('.o_search_options .fa-filter')); + // open menu options + await testUtils.dom.click($('.o_menu_item')); + + assert.hasClass($('.o_item_option[data-option_id="last_month"] a'), 'selected'); + assert.hasClass($('.o_item_option[data-option_id="last_year"] a'), 'selected'); + + actionManager.destroy(); + unpatchDate(); + }); + QUnit.test('`context` key in is used', async function (assert) { assert.expect(3);