[FIX] web: BasicModel: ignore unknown fields after onchange

It may happen that an onchange returns values for unknown fields in
the view. In that case, the corresponding values need to be ignored
as the postprocessing base on the field type can't be done, which
may lead to errors later on (e.g. when saving).

For example: one2many list in a form view which doesn't display
a many2one field that exists on the related model. If an onchange
on the main form view updates the value of the many2one inside the
one2many, it will crash later on when the user will try to save
(because the value [id, display_name] will be sent to the server).
This commit is contained in:
Aaron Bohy
2017-04-11 19:44:38 +02:00
parent ab44cabc78
commit bc89bb79d2
2 changed files with 75 additions and 6 deletions
@@ -942,10 +942,16 @@ var BasicModel = AbstractModel.extend({
params.res_id = command[1];
}
rec = self._makeDataPoint(params);
var data = command[2];
_.each(Object.keys(data), function (name) {
var data = {};
_.each(Object.keys(command[2]), function (name) {
var field = rec.fields[name];
if (field && field.type === 'many2one') {
if (!field) {
// ignore if this field is unknown as we don't
// know if it requires a special handling
return;
}
data[name] = command[2][name];
if (field.type === 'many2one') {
var r = self._makeDataPoint({
modelName: field.relation,
data: {
@@ -957,9 +963,6 @@ var BasicModel = AbstractModel.extend({
}
});
rec._changes = data;
for (var f in rec._changes) {
rec.data[f] = null;
}
list._changes.push(rec.id);
}
if (command[0] === 5) {
@@ -2275,6 +2275,72 @@ QUnit.module('Views', {
form.destroy();
});
QUnit.test('onchanges on unknown fields of o2m are ignored', function (assert) {
// many2one fields need to be postprocessed (the onchange returns [id,
// display_name]), but if we don't know the field, we can't know it's a
// many2one, so it isn't ignored, its value is an array instead of a
// dataPoint id, which may cause errors later (e.g. when saving).
assert.expect(2);
this.data.partner.records[1].p = [4];
this.data.partner.onchanges = {
foo: function () {},
};
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<group>' +
'<field name="foo"/>' +
'<field name="int_field"/>' +
'<field name="p">' +
'<tree>' +
'<field name="foo"/>' +
'<field name="bar"/>' +
'</tree>' +
'<form>' +
'<field name="foo"/>' +
'<field name="product_id"/>' +
'</form>' +
'</field>' +
'</group>' +
'</form>',
res_id: 2,
mockRPC: function (route, args) {
if (args.method === 'onchange') {
return $.when({
value: {
p: [
[5],
[1, 4, {
foo: 'foo changed',
product_id: [37, "xphone"],
}]
],
},
});
}
if (args.method === 'write') {
assert.deepEqual(args.args[1].p, [[1, 4, {
foo: 'foo changed',
}]], "should only write value of known fields");
}
return this._super.apply(this, arguments);
},
});
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_form_input:first').val('trigger an onchange').trigger('input');
assert.strictEqual(form.$('.o_data_row td:first').text(), 'foo changed',
"onchange should have been correctly applied on field in o2m list");
form.$buttons.find('.o_form_button_save').click();
form.destroy();
});
QUnit.test('navigation with tab key in form view', function (assert) {
assert.expect(2);