[FIX] web: read records up to list limit

Fine-tuning of commit e7fab23420
In a one2many with more than one page with a limit of k records, add a line l.
It increments the list limit to k+1. Remove a line above (of index <= k).
It will try to load a line l' to replace it, that it will find on page 2.
However when deciding if it should read missing fields on that record, it will
scan at most u records, skipping virtual ids.
Here u is the min between the current number of lines and the list limit
without the temporary increase due to the added record.
Since there are at least two pages, u = k.
However since l is in the list at index k, then l' won't be read, and since it
was on page 2 its data hasn't been loaded.
Therefore the js would traceback when trying to evaluate it.

opw 1913361

closes odoo/odoo#29338
This commit is contained in:
len-odoo
2018-12-19 13:03:46 +00:00
committed by Aaron Bohy
parent 3d119415a2
commit 9186f433f1
2 changed files with 62 additions and 5 deletions
@@ -3958,11 +3958,24 @@ var BasicModel = AbstractModel.extend({
}
return def.then(function () {
var resIDs = [];
// generate the current count and res_ids list by applying the changes
var currentCount = list.count;
var currentResIDs = list.res_ids;
var effectiveLimit = (list.limit || 0) - (list.tempLimitIncrement || 0);
var upperBound = effectiveLimit ? Math.min(list.offset + effectiveLimit, currentCount) : currentCount;
// if new records have been added to the list, their virtual ids have
// been pushed at the end of res_ids (or at the beginning, depending
// on the editable property), ignoring completely the current page
// where the records have actually been created ; for that reason,
// we use orderedResIDs which is a freezed order with the virtual ids
// at the correct position where they were actually inserted ; however,
// when we use orderedResIDs, we must filter out ids that are not in
// res_ids, which correspond to records that have been removed from
// the relation (this information being taken into account in res_ids
// but not in orderedResIDs)
if (list.orderedResIDs) {
currentResIDs = list.orderedResIDs.filter(function (resID) {
return list.res_ids.indexOf(resID) >= 0;
});
}
var currentCount = currentResIDs.length;
var upperBound = list.limit ? Math.min(list.offset + list.limit, currentCount) : currentCount;
var fieldNames = list.getFieldNames();
for (var i = list.offset; i < upperBound; i++) {
var resId = currentResIDs[i];
@@ -3999,6 +3999,50 @@ QUnit.module('relational_fields', {
form.destroy();
});
QUnit.test('delete a record while adding another one in a multipage', function (assert) {
// in a many2one with at least 2 pages, add a new line. Delete the line above it.
// (the onchange makes it so that the virtualID is inserted in the middle of the currentResIDs.)
// it should load the next line to display it on the page.
assert.expect(2);
this.data.partner.records[0].turtles = [2, 3];
this.data.partner.onchanges.turtles = function (obj) {
obj.turtles = [[5]].concat(obj.turtles);
};
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch:'<form string="Partners">' +
'<sheet>' +
'<group>' +
'<field name="turtles">' +
'<tree editable="bottom" limit="1" decoration-muted="turtle_bar == False">' +
'<field name="turtle_foo"/>' +
'<field name="turtle_bar"/>' +
'</tree>' +
'</field>' +
'</group>' +
'</sheet>' +
'</form>',
res_id: 1,
});
form.$buttons.find('.o_form_button_edit').click();
// add a line (virtual record)
form.$('.o_field_x2many_list_row_add a').click();
form.$('.o_input').val('pi').trigger('input');
// delete the line above it
form.$('.o_list_record_delete').first().click();
// the next line should be displayed below the newly added one
assert.strictEqual(form.$('.o_data_row').length, 2, "should have 2 records");
assert.strictEqual(form.$('.o_data_row').text(), 'pikawa',
"should display the correct records on page 1");
form.destroy();
});
QUnit.test('onchange returning a command 6 for an x2many', function (assert) {
assert.expect(2);
@@ -6231,7 +6275,7 @@ QUnit.module('relational_fields', {
'</form>',
res_id: 1,
mockRPC: function (route, args) {
assert.step(args.method + ' ' + args.model)
assert.step(args.method + ' ' + args.model);
return this._super(route, args);
},
viewOptions: {