[FIX] web: add a stableSort method for sorting x2m
Issue: create 60 lines in ordered x2many with the same value (0) for the order field and make a change ; the displayed records will be switched. The problem appears because the sorting function is not stable and the records with the same values can change place (hence change page), which is quite disturbing for the user.
This commit is contained in:
committed by
Martin Geubelle
parent
b7ee1aa4b1
commit
73e2ba991c
@@ -348,6 +348,23 @@ var utils = {
|
||||
'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
|
||||
].join(';');
|
||||
},
|
||||
/**
|
||||
* Sort an array in place, keeping the initial order for identical values.
|
||||
*
|
||||
* @param {Array} array
|
||||
* @param {function} iteratee
|
||||
*/
|
||||
stableSort: function (array, iteratee) {
|
||||
var stable = array.slice();
|
||||
return array.sort(function stableCompare (a, b) {
|
||||
var order = iteratee(a, b);
|
||||
if (order !== 0) {
|
||||
return order;
|
||||
} else {
|
||||
return stable.indexOf(a) - stable.indexOf(b);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {any} array
|
||||
* @param {any} elem1
|
||||
|
||||
@@ -89,6 +89,7 @@ var Context = require('web.Context');
|
||||
var core = require('web.core');
|
||||
var Domain = require('web.Domain');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
@@ -3833,7 +3834,7 @@ var BasicModel = AbstractModel.extend({
|
||||
for (var k = 0; k < list.orderedResIDs.length; k++) {
|
||||
orderedResIDs[list.orderedResIDs[k]] = k;
|
||||
}
|
||||
list.res_ids.sort(function compareResIdIndexes (resId1, resId2) {
|
||||
utils.stableSort(list.res_ids, function compareResIdIndexes (resId1, resId2) {
|
||||
if (!(resId1 in orderedResIDs) && !(resId2 in orderedResIDs)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -3878,7 +3879,7 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
return compareRecords(record1ID, record2ID, level + 1);
|
||||
};
|
||||
list.res_ids.sort(compareRecords);
|
||||
utils.stableSort(list.res_ids, compareRecords);
|
||||
}
|
||||
this._setDataInRange(list);
|
||||
},
|
||||
|
||||
@@ -2518,6 +2518,119 @@ QUnit.module('relational_fields', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('onchange with modifiers for embedded one2many on the second page', function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
var data = this.data;
|
||||
var ids = [];
|
||||
for (var i=10; i<60; i++) {
|
||||
var id = 10 + i;
|
||||
ids.push(id);
|
||||
data.turtle.records.push({
|
||||
id: id,
|
||||
turtle_int: 0,
|
||||
turtle_foo: "#" + id,
|
||||
});
|
||||
}
|
||||
ids.push(1, 2, 3);
|
||||
data.partner.records[0].turtles = ids;
|
||||
data.partner.onchanges = {
|
||||
turtles: function (obj) {
|
||||
// TODO: make this test more 'difficult'
|
||||
// For now, the server only returns UPDATE commands (no LINK TO)
|
||||
// even though it should do it (for performance reasons)
|
||||
// var turtles = obj.turtles.splice(0, 20);
|
||||
|
||||
var turtles = [];
|
||||
turtles.unshift([5]);
|
||||
// create UPDATE commands for each records (this is the server
|
||||
// usual answer for onchange)
|
||||
for (var k in obj.turtles) {
|
||||
var change = obj.turtles[k];
|
||||
var record = _.findWhere(data.turtle.records, {id: change[1]});
|
||||
if (change[0] === 1) {
|
||||
_.extend(record, change[2]);
|
||||
}
|
||||
turtles.push([1, record.id, record]);
|
||||
}
|
||||
obj.turtles = turtles;
|
||||
},
|
||||
};
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<sheet>' +
|
||||
'<group>' +
|
||||
'<field name="turtles">' +
|
||||
'<tree editable="bottom" default_order="turtle_int" limit="10">' +
|
||||
'<field name="turtle_int" widget="handle"/>' +
|
||||
'<field name="turtle_foo"/>' +
|
||||
'<field name="turtle_qux" attrs="{\'readonly\': [(\'turtle_foo\', \'=\', False)]}"/>' +
|
||||
'</tree>' +
|
||||
'</field>' +
|
||||
'</group>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#21#22#23#24#25#26#27#28#29",
|
||||
"should display the records in order");
|
||||
|
||||
form.$('.o_field_one2many .o_list_view tbody tr:first td:first').click();
|
||||
form.$('.o_field_one2many .o_list_view tbody tr:first input:first').val('blurp').trigger('input');
|
||||
|
||||
// the domain fail if the widget does not use the allready loaded data.
|
||||
form.$buttons.find('.o_form_button_cancel').click();
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "blurp#21#22#23#24#25#26#27#28#29",
|
||||
"should display the records in order with the changes");
|
||||
|
||||
$('.modal .modal-footer button:first').click();
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#21#22#23#24#25#26#27#28#29",
|
||||
"should cancel changes and display the records in order");
|
||||
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
|
||||
// Drag and drop the third line in second position
|
||||
testUtils.dragAndDrop(
|
||||
form.$('.ui-sortable-handle').eq(2),
|
||||
form.$('.o_field_one2many tbody tr').eq(1),
|
||||
{position: 'top'}
|
||||
);
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#30#31#32#33#34#35#36#37#38",
|
||||
"should display the records in order after resequence (display record with turtle_int=0)");
|
||||
|
||||
// Drag and drop the third line in second position
|
||||
testUtils.dragAndDrop(
|
||||
form.$('.ui-sortable-handle').eq(2),
|
||||
form.$('.o_field_one2many tbody tr').eq(1),
|
||||
{position: 'top'}
|
||||
);
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#39#40#41#42#43#44#45#46#47",
|
||||
"should display the records in order after resequence (display record with turtle_int=0)");
|
||||
|
||||
form.$buttons.find('.o_form_button_cancel').click();
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#39#40#41#42#43#44#45#46#47",
|
||||
"should display the records in order after resequence");
|
||||
|
||||
$('.modal .modal-footer button:first').click();
|
||||
|
||||
assert.equal(form.$('.o_field_one2many td[class="o_data_cell"]').text(), "#20#21#22#23#24#25#26#27#28#29",
|
||||
"should cancel changes and display the records in order");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('onchange followed by edition on the second page', function (assert) {
|
||||
assert.expect(20);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user