diff --git a/addons/board/static/src/add_to_board/add_to_board.js b/addons/board/static/src/add_to_board/add_to_board.js
index 69c28cc4eda..72dc4a0e88f 100644
--- a/addons/board/static/src/add_to_board/add_to_board.js
+++ b/addons/board/static/src/add_to_board/add_to_board.js
@@ -38,11 +38,10 @@ export class AddToBoard extends Component {
action,
displayName,
domain,
- comparison,
context,
groupBy,
orderedBy,
- view
+ view,
} = this.env.searchModel;
// Retrieves view context
@@ -57,8 +56,9 @@ export class AddToBoard extends Component {
...viewContext,
};
- if (comparison) {
- contextToSave.comparison = comparison;
+ const detailedComparison = this.env.searchModel.getFullComparison();
+ if (detailedComparison) {
+ contextToSave.comparison = detailedComparison;
}
const result = await this.rpc("/board/add_to_dashboard", {
diff --git a/addons/web/static/src/search/search_model.js b/addons/web/static/src/search/search_model.js
index c6d69f4bb18..ab6fd443245 100644
--- a/addons/web/static/src/search/search_model.js
+++ b/addons/web/static/src/search/search_model.js
@@ -20,6 +20,28 @@ import { deepCopy } from "@web/core/utils/objects";
const { DateTime } = luxon;
const EventBus = owl.core.EventBus;
+/**
+ * @typedef {Object} ComparisonDomain
+ * @property {DomainListRepr} arrayRepr
+ * @property {string} description
+ */
+
+/**
+ * @typedef {Object} Comparison
+ * @property {ComparisonDomain[]} domains
+ * @property {string} [fieldName]
+ */
+
+/**
+ * @typedef {Object} SearchParams
+ * @property {Comparison | null} comparison
+ * @property {Context} context
+ * @property {DomainListRepr} domain
+ * @property {string[]} groupBy
+ * @property {string[]} orderBy
+ * @property {boolean} [useSampleModel] to remove?
+ */
+
/** @todo rework doc */
// interface SectionCommon { // check optional keys
// color: string;
@@ -170,10 +192,10 @@ export class SearchModel extends EventBus {
*
* @param {Object} [config.action={id:false,views:[]}]
* @param {boolean} [config.activateFavorite=true]
+ * @param {Object | null} [config.comparison]
* @param {Object} [config.context={}]
* @param {String} [config.displayName=""]
* @param {Array} [config.domain=[]]
- * @param {Array} [config.domains]
* @param {Array} [config.dynamicFilters=[]]
* @param {string[]} [config.groupBy=[]]
* @param {boolean} [config.loadIrFilters=false]
@@ -197,17 +219,17 @@ export class SearchModel extends EventBus {
this.view = view || { id: false };
// used to avoid useless recomputations
+ // this._comparison is left undefined (we have to distinguish it from null)
this._context = null;
this._domain = null;
- this._domains = null;
this._groupBy = null;
this._orderBy = null;
- const { context, domain, domains, groupBy, orderBy } = config;
+ const { comparison, context, domain, groupBy, orderBy } = config;
+ this.globalComparison = comparison;
this.globalContext = context || {};
this.globalDomain = domain || [];
- this.globalDomains = domains; // rework that part
this.globalGroupBy = groupBy || [];
this.globalOrderBy = orderBy || [];
@@ -362,25 +384,25 @@ export class SearchModel extends EventBus {
/**
* @param {Object} [config={}]
+ * @param {Object | null} [config.comparison]
* @param {Object} [config.context={}]
* @param {Array} [config.domain=[]]
- * @param {Object[]} [config.domains]
* @param {string[]} [config.groupBy=[]]
* @param {string[]} [config.orderBy=[]]
*/
async reload(config = {}) {
// used to avoid useless recomputations
+ delete this._comparison;
this._context = null;
this._domain = null;
- this._domains = null;
this._groupBy = null;
this._orderBy = null;
- const { context, domain, domains, groupBy, orderBy } = config;
+ const { comparison, context, domain, groupBy, orderBy } = config;
this.globalContext = context || {};
this.globalDomain = domain || [];
- this.globalDomains = domains; // how to use this?
+ this.globalComparison = comparison;
this.globalGroupBy = groupBy || [];
this.globalOrderBy = orderBy || [];
@@ -398,13 +420,6 @@ export class SearchModel extends EventBus {
return [...this.sections.values()].filter((s) => s.type === "category");
}
- /**
- * @returns {Object}
- */
- get comparison() {
- return this._getComparison();
- }
-
/**
* @returns {Context} should be imported from context.js?
*/
@@ -425,12 +440,18 @@ export class SearchModel extends EventBus {
return this._domain;
}
- get domains() {
- if (!this._domains) {
- if (this.globalDomains) {
- this._domains = this.globalDomains;
+ /**
+ * @returns {Comparison}
+ */
+ get comparison() {
+ if (!this.searchMenuTypes.has("comparison")) {
+ return null;
+ }
+ if (this._comparison === undefined) {
+ if (this.globalComparison) {
+ this._comparison = this.globalComparison;
} else {
- const comparison = this._getComparison();
+ const comparison = this.getFullComparison();
if (comparison) {
const {
fieldName,
@@ -439,7 +460,7 @@ export class SearchModel extends EventBus {
comparisonRange,
comparisonRangeDescription,
} = comparison;
- this._domains = [
+ const domains = [
{
arrayRepr: Domain.and([this.domain, range]).toList(),
description: rangeDescription,
@@ -449,13 +470,13 @@ export class SearchModel extends EventBus {
description: comparisonRangeDescription,
},
];
- this._domains.fieldName = fieldName; // bad but for now okay
+ this._comparison = { domains, fieldName };
} else {
- this._domains = [{ arrayRepr: this.domain, description: null }];
+ this._comparison = null;
}
}
}
- return this._domains;
+ return this._comparison;
}
get facets() {
@@ -688,6 +709,57 @@ export class SearchModel extends EventBus {
return sortBy(Object.values(copy), (part) => part.groupId);
}
+ getFullComparison() {
+ let searchItem = null;
+ for (const queryElem of this.query.slice().reverse()) {
+ const item = this.searchItems[queryElem.searchItemId];
+ if (item.type === "comparison") {
+ searchItem = item;
+ break;
+ } else if (item.type === "favorite" && item.comparison) {
+ searchItem = item;
+ break;
+ }
+ }
+ if (!searchItem) {
+ return null;
+ } else if (searchItem.type === "favorite") {
+ return searchItem.comparison;
+ }
+ const { dateFilterId, comparisonOptionId } = searchItem;
+ const { fieldName, fieldType, description: dateFilterDescription } = this.searchItems[
+ dateFilterId
+ ];
+ const selectedGeneratorIds = this._getSelectedGeneratorIds(dateFilterId);
+ // compute range and range description
+ const { domain: range, description: rangeDescription } = constructDateDomain(
+ this.referenceMoment,
+ fieldName,
+ fieldType,
+ selectedGeneratorIds
+ );
+ // compute comparisonRange and comparisonRange description
+ const {
+ domain: comparisonRange,
+ description: comparisonRangeDescription,
+ } = constructDateDomain(
+ this.referenceMoment,
+ fieldName,
+ fieldType,
+ selectedGeneratorIds,
+ comparisonOptionId
+ );
+ return {
+ comparisonId: comparisonOptionId,
+ fieldName,
+ fieldDescription: dateFilterDescription,
+ range: range.toList(),
+ rangeDescription,
+ comparisonRange: comparisonRange.toList(),
+ comparisonRangeDescription,
+ };
+ }
+
getIrFilterValues(params) {
const { irFilter } = this._getIrFilterDescription(params);
return irFilter;
@@ -1264,57 +1336,6 @@ export class SearchModel extends EventBus {
return domain;
}
- _getComparison() {
- let searchItem = null;
- for (const queryElem of this.query.slice().reverse()) {
- const item = this.searchItems[queryElem.searchItemId];
- if (item.type === "comparison") {
- searchItem = item;
- break;
- } else if (item.type === "favorite" && item.comparison) {
- searchItem = item;
- break;
- }
- }
- if (!searchItem) {
- return null;
- } else if (searchItem.type === "favorite") {
- return searchItem.comparison;
- }
- const { dateFilterId, comparisonOptionId } = searchItem;
- const { fieldName, fieldType, description: dateFilterDescription } = this.searchItems[
- dateFilterId
- ];
- const selectedGeneratorIds = this._getSelectedGeneratorIds(dateFilterId);
- // compute range and range description
- const { domain: range, description: rangeDescription } = constructDateDomain(
- this.referenceMoment,
- fieldName,
- fieldType,
- selectedGeneratorIds
- );
- // compute comparisonRange and comparisonRange description
- const {
- domain: comparisonRange,
- description: comparisonRangeDescription,
- } = constructDateDomain(
- this.referenceMoment,
- fieldName,
- fieldType,
- selectedGeneratorIds,
- comparisonOptionId
- );
- return {
- comparisonId: comparisonOptionId,
- fieldName,
- fieldDescription: dateFilterDescription,
- range: range.toList(),
- rangeDescription,
- comparisonRange: comparisonRange.toList(),
- comparisonRangeDescription,
- };
- }
-
/**
* Construct a single context from the contexts of
* filters of type 'filter', 'favorite', and 'field'.
@@ -1736,7 +1757,7 @@ export class SearchModel extends EventBus {
}
const rawDomain = this._getDomain({ raw: true, withGlobal: false });
const groupBys = this._getGroupBy();
- const comparison = this._getComparison();
+ const comparison = this.getFullComparison();
const orderBy = saveParams.orderBy ? saveParams.orderBy : this._getOrderBy() || [];
const userId = isShared ? false : this.userService.userId;
@@ -1996,9 +2017,9 @@ export class SearchModel extends EventBus {
if (this.blockNotification) {
return;
}
+ delete this._comparison;
this._context = null;
this._domain = null;
- this._domains = null;
this._groupBy = null;
this._orderBy = null;
diff --git a/addons/web/static/src/search/with_search/with_search.js b/addons/web/static/src/search/with_search/with_search.js
index f4342a50652..7c138a1f949 100644
--- a/addons/web/static/src/search/with_search/with_search.js
+++ b/addons/web/static/src/search/with_search/with_search.js
@@ -7,7 +7,7 @@ import { CallbackRecorder, useSetupAction } from "@web/webclient/actions/action_
const { Component, hooks } = owl;
const { useSubEnv } = hooks;
-export const SEARCH_KEYS = ["context", "domain", "domains", "groupBy", "orderBy"];
+export const SEARCH_KEYS = ["comparison", "context", "domain", "groupBy", "orderBy"];
const OTHER_SEARCH_KEYS = ["irFilters", "searchViewArch", "searchViewFields", "searchViewId"];
export class WithSearch extends Component {
@@ -63,7 +63,7 @@ export class WithSearch extends Component {
async willUpdateProps(nextProps) {
const config = {};
for (const key of SEARCH_KEYS) {
- if (nextProps[key]) {
+ if (nextProps[key] !== undefined) {
config[key] = nextProps[key];
}
}
@@ -122,9 +122,10 @@ WithSearch.props = {
display: { type: Object, optional: true },
// search query elements
+ comparison: { validate: () => true, optional: true }, // fix problem with validation with type: [Object, null]
+ // Issue OWL: https://github.com/odoo/owl/issues/910
context: { type: Object, optional: true },
domain: { type: Array, element: [String, Array], optional: true },
- domains: { type: Array, element: Object, optional: true },
groupBy: { type: Array, element: String, optional: true },
orderBy: { type: Array, element: String, optional: true },
diff --git a/addons/web/static/src/views/graph/graph_model.js b/addons/web/static/src/views/graph/graph_model.js
index 07fddc83516..6e75041e5c9 100644
--- a/addons/web/static/src/views/graph/graph_model.js
+++ b/addons/web/static/src/views/graph/graph_model.js
@@ -11,6 +11,10 @@ import { computeReportMeasures, processMeasure } from "@web/views/helpers/utils"
export const SEP = " / ";
+/**
+ * @typedef {import("@web/search/search_model").SearchParams} SearchParams
+ */
+
class DateClasses {
// We view the param "array" as a matrix of values and undefined.
// An equivalence class is formed of defined values of a column.
@@ -100,15 +104,17 @@ export class GraphModel extends Model {
//--------------------------------------------------------------------------
/**
- * @param {Object} [searchParams]
- * @param {Object} [searchParams.context]
- * @param {Object[]} [searchParams.domains]
- * @param {string[]} [searchParams.groupBy]
+ * @param {SearchParams} searchParams
*/
async load(searchParams) {
- const { context, domains, groupBy } = searchParams;
- const metaData = Object.assign({}, this.metaData, { context, domains });
-
+ const { comparison, domain, context, groupBy } = searchParams;
+ const metaData = Object.assign({}, this.metaData, { context });
+ if (comparison) {
+ metaData.domains = comparison.domains;
+ metaData.comparisonField = comparison.fieldName;
+ } else {
+ metaData.domains = [{ arrayRepr: domain, description: null }];
+ }
metaData.measure = context.graph_measure || metaData.measure;
metaData.mode = context.graph_mode || metaData.mode;
@@ -183,10 +189,10 @@ export class GraphModel extends Model {
* @returns {Object}
*/
_getData(dataPoints) {
- const { domains, groupBy, mode } = this.metaData;
+ const { comparisonField, groupBy, mode } = this.metaData;
let identify = false;
- if (domains.length && groupBy.length && groupBy[0].fieldName === domains.fieldName) {
+ if (comparisonField && groupBy.length && groupBy[0].fieldName === comparisonField) {
identify = true;
}
const dateClasses = identify ? this._getDateClasses(dataPoints) : null;
diff --git a/addons/web/static/src/views/graph/graph_view.xml b/addons/web/static/src/views/graph/graph_view.xml
index 4a72f77dc7c..7d4f36f6ae5 100644
--- a/addons/web/static/src/views/graph/graph_view.xml
+++ b/addons/web/static/src/views/graph/graph_view.xml
@@ -31,7 +31,7 @@
t-att-class="{ active: model.metaData.stacked }"
/>
-
+