[IMP] web: enhance kanban progressbars filtering

When a progressbar filter is activated, it will only show records
matching the filter and nothing else.

"Load more" link will update its counter accordingly.

closes odoo/odoo#55466

Taskid: 2196661
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Bruno Boi
2020-11-18 07:52:19 +00:00
parent 99fb043668
commit 43d312d1ee
6 changed files with 458 additions and 108 deletions
@@ -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
@@ -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 = {};
// <progressbar/> 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;
@@ -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 });
});
@@ -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<string>} 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<Object>}
*/
_readProgressBarGroup: function (list, options) {
var self = this;
var groupsDef = this._readGroup(list, options);
var progressBarDef = this._rpc({
async _readProgressBarGroup(list, options) {
const groupsDef = this._readGroup(list, options);
const progressBarDef = this._readProgressBar(list);
const [groups, progressBar] = await Promise.all([groupsDef, progressBarDef]);
list.data.forEach(groupId => {
const group = this.localData[groupId];
const valuesCount = progressBar[group.value] || {};
const valuesCountTotal = Object.keys(valuesCount).reduce((sum, key) => {
return sum + valuesCount[key];
}, 0);
// Compute records count for progressbar field values
// not specified in the progressbar attributes
const counts = Object.assign({
__false: group.count - valuesCountTotal
}, valuesCount);
group.progressBarValues = Object.assign({
counts,
}, list.progressBar);
});
return list;
},
/**
* @param {Object} list valid resource object
* @returns {Promise}
*/
_readProgressBar: function (list) {
return this._rpc({
model: list.model,
method: 'read_progress_bar',
kwargs: {
@@ -347,16 +424,6 @@ var KanbanModel = BasicModel.extend({
context: list.context,
},
});
return Promise.all([groupsDef, progressBarDef]).then(function (results) {
var data = results[1];
_.each(list.data, function (groupID) {
var group = self.localData[groupID];
group.progressBarValues = _.extend({
counts: data[group.value] || {},
}, list.progressBar);
});
return list;
});
},
/**
* Fetches tooltip specific fields on the group by relation and stores it in
@@ -407,34 +474,37 @@ var KanbanModel = BasicModel.extend({
},
/**
* Reloads all progressbar data. This is done after given promise and
* insures that the given promise's result is not lost.
* ensures that the given promise's result is not lost.
*
* @private
* @param {string} recordID
* @param {Promise} def
* @returns {Promise}
*/
_reloadProgressBarGroupFromRecord: function (recordID, def) {
var element = this.localData[recordID];
if (element.type === 'list' && !element.parentID) {
// we are reloading the whole view, so there is no need to manually
// reload the progressbars
return def;
}
async _reloadProgressBarGroupFromRecord(recordID, def) {
let element = this.localData[recordID];
if (element.type === 'list') {
if (!element.parentID) {
// we are reloading the whole view, so there is no need to manually
// reload the progressbars
return def;
}
if (element.activeFilter) {
// We must not read_group when an active filter (and thus
// a domain extension) is applied to the list datapoint.
return def;
}
}
// If we updated a record, then we must potentially update columns'
// progressbars, so we need to load groups info again
var self = this;
while (element) {
if (element.progressBar) {
return def.then(function (data) {
return self._load(element, {
keepEmptyGroups: true,
onlyGroups: true,
}).then(function () {
return data;
});
const data = await def;
await this._load(element, {
keepEmptyGroups: true,
onlyGroups: true,
});
return data;
}
element = this.localData[element.parentID];
}
+2 -2
View File
@@ -32,14 +32,14 @@
<span t-if="widget.quick_create" class="o_kanban_quick_add"><i class="fa fa-plus" role="img" aria-label="Quick add" title="Quick add"/></span>
</div>
</div>
<div t-if="widget.remaining > 0" class="o_kanban_load_more">
<div t-if="widget.loadMoreCount > 0" class="o_kanban_load_more">
<t t-call="KanbanView.LoadMore"/>
</div>
</div>
</t>
<t t-name="KanbanView.LoadMore">
<a href="#">Load more... (<t t-esc="widget.remaining"/> remaining)</a>
<a href="#">Load more... (<t t-esc="widget.loadMoreCount"/> remaining)</a>
</t>
<t t-name="KanbanView.ColumnQuickCreate">
+280 -15
View File
@@ -5937,6 +5937,50 @@ QUnit.module('Views', {
kanban.destroy();
});
QUnit.test('column progressbars with an active filter are working with load more', async function (assert) {
assert.expect(2);
this.data.partner.records.push(
{ id: 5, bar: true, foo: "blork" },
{ id: 6, bar: true, foo: "blork" },
{ id: 7, bar: true, foo: "blork" }
);
const kanban = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
domain: [['bar', '=', true]],
arch:
`<kanban limit="1">
<progressbar field="foo" colors='{"blork": "success"}'/>
<field name="foo"/>
<templates><t t-name="kanban-box">
<div><field name="id"/></div>
</t></templates>
</kanban>`,
groupBy: ['bar'],
});
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_counter_progress .progress-bar[data-filter="blork"]'));
// we should have 1 record shown
assert.deepEqual(
[...kanban.el.querySelectorAll('.o_kanban_record')].map(el => parseInt(el.innerText)),
[5]
);
// load 2 more and check it worked
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group .o_kanban_load_more'));
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group .o_kanban_load_more'));
assert.deepEqual(
[...kanban.el.querySelectorAll('.o_kanban_record')].map(el => parseInt(el.innerText)),
[5, 6, 7]
);
kanban.destroy();
});
QUnit.test('column progressbars on archiving records update counter', async function (assert) {
assert.expect(4);
@@ -6088,6 +6132,54 @@ QUnit.module('Views', {
kanban.destroy();
});
QUnit.test('RPCs when (de)activating kanban view progressbar filters', async function (assert) {
assert.expect(8);
const kanban = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch: `
<kanban>
<field name="bar"/>
<field name="int_field"/>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}' sum_field="int_field"/>
<templates><t t-name="kanban-box">
<div><field name="name"/></div>
</t></templates>
</kanban>
`,
groupBy: ['bar'],
mockRPC(route, args) {
assert.step(args.method || route);
return this._super.apply(this, arguments);
},
});
// Activate "yop" on second column
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group:nth-child(2) .progress-bar[data-filter="yop"]'));
// Activate "gnap" on second column
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group:nth-child(2) .progress-bar[data-filter="gnap"]'));
// Deactivate "gnap" on second column
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group:nth-child(2) .progress-bar[data-filter="gnap"]'));
assert.verifySteps([
// initial load
'web_read_group',
'read_progress_bar',
'/web/dataset/search_read',
'/web/dataset/search_read',
// activate filter
'/web/dataset/search_read',
// activate another filter (switching)
'/web/dataset/search_read',
// deactivate active filter
'/web/dataset/search_read',
]);
kanban.destroy();
});
QUnit.test('drag & drop records grouped by m2o with progressbar', async function (assert) {
assert.expect(4);
@@ -6139,34 +6231,152 @@ QUnit.module('Views', {
QUnit.test('progress bar subgroup count recompute', async function (assert) {
assert.expect(2);
var kanban = await createView({
const kanban = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch:
'<kanban>' +
'<progressbar field="foo" colors=\'{"yop": "success", "gnap": "warning", "blip": "danger"}\'/>' +
'<templates><t t-name="kanban-box">' +
'<div>' +
'<field name="foo"/>' +
'</div>' +
'</t></templates>' +
'</kanban>',
`<kanban>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}'/>
<templates><t t-name="kanban-box">
<div>
<field name="foo"/>
</div>
</t></templates>
</kanban>`,
groupBy: ['bar'],
});
var $secondGroup = kanban.$('.o_kanban_group:eq(1)');
var initialCount = parseInt($secondGroup.find('.o_kanban_counter_side').text());
assert.strictEqual(initialCount, 3,
let secondCounter = kanban.el.querySelector('.o_kanban_group:nth-child(2) .o_kanban_counter_side');
assert.strictEqual(parseInt(secondCounter.innerText), 3,
"Initial count should be Three");
await testUtils.dom.click($secondGroup.find('.bg-success-full'));
var lastCount = parseInt($secondGroup.find('.o_kanban_counter_side').text());
assert.strictEqual(lastCount, 1,
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group:nth-child(2) .bg-success-full'));
secondCounter = kanban.el.querySelector('.o_kanban_group:nth-child(2) .o_kanban_counter_side');
assert.strictEqual(parseInt(secondCounter.innerText), 1,
"kanban counters should vary according to what subgroup is selected");
kanban.destroy();
});
QUnit.test('progress bar recompute after drag&drop to and from other column', async function (assert) {
assert.expect(4);
const view = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch:
`<kanban>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}'/>
<templates><t t-name="kanban-box">
<div>
<field name="foo"/>
</div>
</t></templates>
</kanban>`,
groupBy: ['bar'],
});
assert.deepEqual(
[...view.el.querySelectorAll('.progress-bar')].map(el => el.getAttribute('data-original-title')),
['0 yop', '0 gnap', '1 blip', '0 __false', '1 yop', '1 gnap', '1 blip', '0 __false']
);
assert.deepEqual(
[...view.el.querySelectorAll('.o_kanban_counter_side')].map(el => parseInt(el.innerText)),
[1, 3]
);
// Drag the last kanban record to the first column
await testUtils.dom.dragAndDrop(
[...view.el.querySelectorAll('.o_kanban_record')].pop(),
[...view.el.querySelectorAll('.o_kanban_group')].shift()
);
assert.deepEqual(
[...view.el.querySelectorAll('.progress-bar')].map(el => el.getAttribute('data-original-title')),
['0 yop', '1 gnap', '1 blip', '0 __false', '1 yop', '0 gnap', '1 blip', '0 __false']
);
assert.deepEqual(
[...view.el.querySelectorAll('.o_kanban_counter_side')].map(el => parseInt(el.innerText)),
[2, 2]
);
view.destroy();
});
QUnit.test('load more should load correct records after drag&drop event', async function (assert) {
assert.expect(3);
// Add a sequence number and initialize
this.data.partner.fields = Object.assign(this.data.partner.fields, {
sequence: { type: 'integer' }
});
this.data.partner.records.forEach((el, i) => {
el.sequence = i;
});
const view = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch:
`<kanban limit="1">
<field name="id"/>
<field name="foo"/>
<field name="sequence"/>
<templates><t t-name="kanban-box">
<div>
<field name="id"/>
</div>
</t></templates>
</kanban>`,
groupBy: ['bar'],
favoriteFilters: [
{
domain: '[]',
is_default: true,
sort: '["sequence asc"]',
},
],
});
assert.deepEqual(
[...view.el.querySelectorAll('.o_kanban_group:nth-child(1) .o_kanban_record span')]
.map(el => parseInt(el.innerText))[0],
4,
"first column's first record must be id 4"
);
assert.deepEqual(
[...view.el.querySelectorAll('.o_kanban_group:nth-child(2) .o_kanban_record span')]
.map(el => parseInt(el.innerText)),
[1],
"second column's records should be only the id 1"
);
// Drag the first kanban record on top of the last
await testUtils.dom.dragAndDrop(
[...view.el.querySelectorAll('.o_kanban_record')].shift(),
[...view.el.querySelectorAll('.o_kanban_record')].pop(),
{ position: 'top' }
);
// load more twice to load all records of second column
await testUtils.dom.click(view.el.querySelector('.o_kanban_group:nth-child(2) .o_kanban_load_more'));
await testUtils.dom.click(view.el.querySelector('.o_kanban_group:nth-child(2) .o_kanban_load_more'));
// Check records of the second column
assert.deepEqual(
[...view.el.querySelectorAll('.o_kanban_group:nth-child(2) .o_kanban_record span')].map(el => parseInt(el.innerText)),
[4, 1, 2, 3]
);
view.destroy();
});
QUnit.test('column progressbars on quick create with quick_create_view are updated', async function (assert) {
assert.expect(1);
@@ -6206,6 +6416,61 @@ QUnit.module('Views', {
kanban.destroy();
});
QUnit.test('column progressbars and active filter on quick create with quick_create_view are updated', async function (assert) {
assert.expect(2);
const kanban = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch: `
<kanban on_create="quick_create" quick_create_view="some_view_ref">
<field name="int_field"/>
<field name="foo"/>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}' sum_field="int_field"/>
<templates><t t-name="kanban-box">
<div><field name="name"/></div>
</t></templates>
</kanban>
`,
archs: {
'partner,some_view_ref,form': `
<form>
<field name="int_field"/>
<field name="foo"/>
</form>
`,
},
groupBy: ['bar'],
});
await testUtils.dom.click(kanban.el.querySelector('.o_kanban_group:nth-child(1) .progress-bar[data-filter="blip"]'));
const initialCount = parseInt(kanban.el.querySelector('.o_kanban_group:nth-child(1) .o_kanban_counter_side').innerText);
assert.strictEqual(initialCount, -4, "Initial count should be -4");
// open the quick create
await testUtils.kanban.clickCreate(kanban);
// fill it with a record that satisfies the activated filter
let quickCreate = kanban.el.querySelector('.o_kanban_group:nth-child(1) .o_kanban_quick_create');
await testUtils.fields.editInput(quickCreate.querySelector('.o_field_widget[name="int_field"]'), '44');
await testUtils.fields.editInput(quickCreate.querySelector('.o_field_widget[name="foo"]'), 'blip');
await testUtils.dom.click(quickCreate.querySelector('button.o_kanban_add'));
// fill it again with another record that DOES NOT satisfies the activated filter
quickCreate = kanban.el.querySelector('.o_kanban_group:nth-child(1) .o_kanban_quick_create');
await testUtils.fields.editInput(quickCreate.querySelector('.o_field_widget[name="int_field"]'), '1000');
await testUtils.fields.editInput(quickCreate.querySelector('.o_field_widget[name="foo"]'), 'yop');
await testUtils.dom.click(quickCreate.querySelector('button.o_kanban_add'));
// check counter
const lastCount = parseInt(kanban.el.querySelector('.o_kanban_group:nth-child(1) .o_kanban_counter_side').innerText);
assert.strictEqual(lastCount, initialCount + 44,
"kanban counters should have been updated on quick create, respecting the activated filter");
kanban.destroy();
});
QUnit.test('keep adding quickcreate in first column after a record from this column was moved', async function (assert) {
assert.expect(2);