diff --git a/addons/web/static/src/js/views/kanban/kanban_column.js b/addons/web/static/src/js/views/kanban/kanban_column.js
index 4aeb5404130..8cab25c31ab 100644
--- a/addons/web/static/src/js/views/kanban/kanban_column.js
+++ b/addons/web/static/src/js/views/kanban/kanban_column.js
@@ -64,7 +64,8 @@ var KanbanColumn = Widget.extend({
this.recordsDraggable = options.recordsDraggable;
this.relation = options.relation;
this.offset = 0;
- this.remaining = data.count - this.data_records.length;
+ this.loadMoreCount = data.loadMoreCount;
+ this.loadMoreOffset = data.loadMoreOffset;
this.canBeFolded = this.folded;
if (options.hasProgressBar) {
@@ -162,7 +163,7 @@ var KanbanColumn = Widget.extend({
if (this.tooltipInfo) {
this.$header.find('.o_kanban_header_title').tooltip({}).attr('data-original-title', this.tooltipInfo);
}
- if (!this.remaining) {
+ if (!this.loadMoreCount) {
this.$('.o_kanban_load_more').remove();
} else {
this.$('.o_kanban_load_more').html(QWeb.render('KanbanView.LoadMore', {widget: this}));
@@ -349,7 +350,7 @@ var KanbanColumn = Widget.extend({
*/
_onLoadMore: function (event) {
event.preventDefault();
- this.trigger_up('kanban_load_more');
+ this.trigger_up('kanban_load_column_records', { loadMoreOffset: this.loadMoreOffset });
},
/**
* @private
diff --git a/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js b/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
index 752d2b2dc6c..1639f7c34a3 100644
--- a/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
+++ b/addons/web/static/src/js/views/kanban/kanban_column_progressbar.js
@@ -11,7 +11,6 @@ const _t = core._t;
var KanbanColumnProgressBar = Widget.extend({
template: 'KanbanView.ColumnProgressBar',
events: {
- 'click .o_kanban_counter_progress': '_onProgressBarParentClick',
'click .progress-bar': '_onProgressBarClick',
},
/**
@@ -28,6 +27,7 @@ var KanbanColumnProgressBar = Widget.extend({
this.columnID = options.columnID;
this.columnState = columnState;
+ this.activeFilter = {};
// attributes
this.fieldName = columnState.progressBarValues.field;
@@ -90,16 +90,13 @@ var KanbanColumnProgressBar = Widget.extend({
*/
computeCounters() {
const subgroupCounts = {};
- let allSubgroupCount = 0;
for (const key of Object.keys(this.colors)) {
const subgroupCount = this.columnState.progressBarValues.counts[key] || 0;
- if (this.activeFilter === key && subgroupCount === 0) {
- this.activeFilter = false;
+ if (this.activeFilter.value === key && subgroupCount === 0) {
+ this.activeFilter = {};
}
subgroupCounts[key] = subgroupCount;
- allSubgroupCount += subgroupCount;
- };
- subgroupCounts.__false = this.columnState.count - allSubgroupCount;
+ }
this.groupCount = this.columnState.count;
this.subgroupCounts = subgroupCounts;
@@ -127,8 +124,8 @@ var KanbanColumnProgressBar = Widget.extend({
_.each(self.colors, function (val, key) {
$el.removeClass('o_kanban_group_show_' + val);
});
- if (self.activeFilter) {
- $el.addClass('o_kanban_group_show o_kanban_group_show_' + self.colors[self.activeFilter]);
+ if (self.activeFilter.value) {
+ $el.addClass('o_kanban_group_show o_kanban_group_show_' + self.colors[self.activeFilter.value]);
}
},
});
@@ -171,7 +168,7 @@ var KanbanColumnProgressBar = Widget.extend({
});
// Adapt active state
- $bar.toggleClass('progress-bar-animated progress-bar-striped', key === self.activeFilter);
+ $bar.toggleClass('progress-bar-animated progress-bar-striped', key === self.activeFilter.value);
// Adapt width
$bar.removeClass('o_bar_has_records transition-off');
@@ -198,24 +195,24 @@ var KanbanColumnProgressBar = Widget.extend({
var start = this.prevTotalCounterValue;
var end = this.totalCounterValue;
- if (this.activeFilter) {
+ if (this.activeFilter.value) {
if (this.sumField) {
end = 0;
_.each(self.columnState.data, function (record) {
var recordData = record.data;
- if (self.activeFilter === recordData[self.fieldName] ||
- (self.activeFilter === '__false' && !recordData[self.fieldName])) {
+ if (self.activeFilter.value === recordData[self.fieldName] ||
+ (self.activeFilter.value === '__false' && !recordData[self.fieldName])) {
end += parseFloat(recordData[self.sumField]);
}
});
} else {
- end = this.subgroupCounts[this.activeFilter];
+ end = this.subgroupCounts[this.activeFilter.value];
}
}
this.prevTotalCounterValue = end;
var animationClass = start > 999 ? 'o_kanban_grow' : 'o_kanban_grow_huge';
- if (start !== undefined && (end > start || this.activeFilter) && this.ANIMATE) {
+ if (start !== undefined && (end > start || this.activeFilter.value) && this.ANIMATE) {
$({currentValue: start}).animate({currentValue: end}, {
duration: 1000,
start: function () {
@@ -255,33 +252,47 @@ var KanbanColumnProgressBar = Widget.extend({
},
});
},
+ /**
+ * Toggles the active filter on this progressbar.
+ * It also computes the corresponding domain extension.
+ *
+ * @private
+ * @param {string} value
+ */
+ _toggleActiveFilter(value) {
+ const activeFilter = Object.assign({}, this.activeFilter);
+ if (activeFilter.value === value) {
+ // If the filter was active and we click again on the same one, deactivate.
+ activeFilter.domain = [];
+ activeFilter.value = false;
+ } else {
+ const field = this.fieldName;
+ if (value === '__false') {
+ const values = Object.keys(this.colors).filter(el => el !== value);
+ activeFilter.domain = ['!', [field, 'in', values]];
+ } else {
+ activeFilter.domain = [[field, '=', value]];
+ }
+ activeFilter.value = value;
+ }
+ this.activeFilter = activeFilter;
+ },
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
-
/**
* @private
* @param {Event} ev
*/
_onProgressBarClick: function (ev) {
this.$clickedBar = $(ev.currentTarget);
- var filter = this.$clickedBar.data('filter');
- this.activeFilter = (this.activeFilter === filter ? false : filter);
+ const filterValue = this.$clickedBar.data('filter');
+ this._toggleActiveFilter(filterValue);
this._notifyState();
- this._render();
- },
- /**
- * @private
- * @param {Event} ev
- */
- _onProgressBarParentClick: function (ev) {
- if (ev.target !== ev.currentTarget) {
- return;
- }
- this.activeFilter = false;
- this._notifyState();
- this._render();
+ this.trigger_up('kanban_load_column_records', {
+ activeFilter: this.activeFilter
+ });
},
});
return KanbanColumnProgressBar;
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index 1b6e6301906..fc43324a906 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -31,7 +31,7 @@ var KanbanController = BasicController.extend({
kanban_column_delete: '_onDeleteColumn',
kanban_column_add_record: '_onAddRecordToColumn',
kanban_column_resequence: '_onColumnResequence',
- kanban_load_more: '_onLoadMore',
+ kanban_load_column_records: '_onLoadColumnRecords',
column_toggle_fold: '_onToggleColumn',
kanban_column_records_toggle_active: '_onToggleActiveRecords',
}),
@@ -389,14 +389,15 @@ var KanbanController = BasicController.extend({
/**
* @private
* @param {OdooEvent} ev
+ * @param {Object} ev.data see model.reload options
*/
- _onLoadMore: function (ev) {
- var self = this;
- var column = ev.target;
- this.model.loadMore(column.db_id).then(function (db_id) {
- var data = self.model.get(db_id);
- self.renderer.updateColumn(db_id, data);
- });
+ async _onLoadColumnRecords(ev) {
+ const column = ev.target;
+ const id = column.columnID || column.db_id;
+ const options = ev.data;
+ const dbID = await this.model.reload(id, options);
+ const data = this.model.get(dbID);
+ return this.renderer.updateColumn(dbID, data);
},
/**
* @private
@@ -520,9 +521,11 @@ var KanbanController = BasicController.extend({
this.model.actionArchive(recordIds, column.db_id) :
this.model.actionUnarchive(recordIds, column.db_id);
prom.then(function (dbID) {
- var data = self.model.get(dbID);
+ let data = self.model.get(dbID);
if (data) { // Could be null if a wizard is returned for example
self.model.reload(self.handle).then(function () {
+ // Retrieve fresher data as the reload may have changed it.
+ data = self.model.get(dbID);
const state = self.model.get(self.handle);
self.renderer.updateColumn(dbID, data, { state });
});
diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js
index 7dcfe4081aa..52c420872f7 100644
--- a/addons/web/static/src/js/views/kanban/kanban_model.js
+++ b/addons/web/static/src/js/views/kanban/kanban_model.js
@@ -143,8 +143,10 @@ var KanbanModel = BasicModel.extend({
* Add the following (kanban specific) keys when performing a `get`:
*
* - tooltipData
- * - progressBarValues
* - isGroupedByM2ONoColumn
+ * - progressBarValues
+ * - loadMoreCount
+ * - loadMoreOffset
*
* @override
* @see _readTooltipFields
@@ -154,18 +156,47 @@ var KanbanModel = BasicModel.extend({
var result = this._super.apply(this, arguments);
var dp = result && this.localData[result.id];
if (dp) {
+ // Add tooltipData key
if (dp.tooltipData) {
result.tooltipData = $.extend(true, {}, dp.tooltipData);
}
- if (dp.progressBarValues) {
- result.progressBarValues = $.extend(true, {}, dp.progressBarValues);
- }
+ // Add isGroupedByM2ONoColumn key
if (dp.fields[dp.groupedBy[0]]) {
var groupedByM2O = dp.fields[dp.groupedBy[0]].type === 'many2one';
result.isGroupedByM2ONoColumn = !dp.data.length && groupedByM2O;
} else {
result.isGroupedByM2ONoColumn = false;
}
+ // Add progressBarValues, loadMoreCount and loadMoreOffset key
+ let loadMoreCount = result.count - result.data.length;
+ let loadMoreOffset = result.data.length;
+ if (dp.progressBarValues) {
+ result.progressBarValues = $.extend(true, {}, dp.progressBarValues);
+ if (dp.activeFilter && dp.activeFilter.value) {
+ // A progressbar filter is in an activated state?
+ // Then set loadMoreCount and loadMoreOffset accordingly.
+ // These computations are needed because there might be
+ // records that doesn't match the current filter, e.g.:
+ // - newly created records
+ // - records dragged in the column
+ const barValues = dp.progressBarValues;
+ const barField = barValues.field;
+ const definedBarFieldValues = Object.keys(barValues.colors);
+ const filterValue = dp.activeFilter.value;
+
+ const filteredRecordsTotal = barValues.counts[filterValue];
+ const filteredRecords = result.data.filter(element => {
+ const elementBarFieldValue = element.data[barField];
+ return (filterValue === elementBarFieldValue)
+ || (filterValue === '__false'
+ && !definedBarFieldValues.includes(elementBarFieldValue));
+ });
+ loadMoreCount = filteredRecordsTotal - filteredRecords.length;
+ loadMoreOffset = filteredRecords.length;
+ }
+ }
+ result.loadMoreCount = loadMoreCount;
+ result.loadMoreOffset = loadMoreOffset;
}
return result;
},
@@ -190,19 +221,6 @@ var KanbanModel = BasicModel.extend({
params.groupedBy = (params.groupedBy && params.groupedBy.length) ? params.groupedBy : this.defaultGroupedBy;
return this._super(params);
},
- /**
- * Load more records in a group.
- *
- * @param {string} groupID localID of the group
- * @returns {Promise} resolves to the localID of the group
- */
- loadMore: function (groupID) {
- var group = this.localData[groupID];
- var offset = group.loadMoreOffset + group.limit;
- return this.reload(group.id, {
- loadMoreOffset: offset,
- });
- },
/**
* Moves a record from a group to another.
*
@@ -262,8 +280,24 @@ var KanbanModel = BasicModel.extend({
},
/**
* @override
+ * @param {Object} options.activeFilter
*/
reload: function (id, options) {
+ const element = this.localData[id];
+ // Reset the limit right before reloading a group as a reload
+ // of a kanban view may update the limit of a group. @see BasicModel._readGroup()
+ // (e.g. if we are coming back to the kanban view from a form view)
+ // As this mechanism may only affect grouped kanban views, only reset
+ // the limit if the current element is a group (and thus, has a parent).
+ if (element.parentID) {
+ element.limit = this.loadParams.limit;
+ }
+ // Register the domain extension in the element.
+ if (options && options.activeFilter) {
+ element.activeFilter = options.activeFilter || element.activeFilter;
+ // Cleanup as this option is not used further
+ delete options.activeFilter;
+ }
// if the groupBy is given in the options and if it is an empty array,
// fallback on the default groupBy
if (options && options.groupBy && !options.groupBy.length) {
@@ -293,6 +327,23 @@ var KanbanModel = BasicModel.extend({
// Private
//--------------------------------------------------------------------------
+ /**
+ * @override
+ */
+ _getGroupedListPropsToKeep(list, options) {
+ const propsToKeep = this._super(...arguments);
+ if (list.activeFilter) {
+ propsToKeep.activeFilter = list.activeFilter;
+ }
+ return propsToKeep;
+ },
+ /**
+ * @override
+ */
+ _getUngroupedListDomain(list) {
+ const domainExtension = (list.activeFilter && list.activeFilter.domain) || [];
+ return [...this._super(...arguments), ...domainExtension];
+ },
/**
* @override
*/
@@ -334,10 +385,36 @@ var KanbanModel = BasicModel.extend({
* @param {Object} dataPoint
* @returns {Promise