[FIX] web: BasicModel: domain returned by onchanges

Onchange RPCs return an object that may contain a 'domain' key.
When it does, its value is an object whose keys are field names and
values are the new domain for the corresponding field.

Commit 8473bde8 added the support of the 'domain' key, but stored
it directly in the fieldsInfo (an object gathering information of
the fields in the view, and which is shared between all records).

This was wrong as the domain isn't necessarily the same for all
records. To reproduce, with the UOMs activated, go to Sales >
Quotation, in the form view, add an order line, set a product
(e.g. Graphic Card), the onchange sets the domain for the uom
field (matching uoms like 'unit', 'dozens'). Add a second line
with a service product, the onchange returns another domain for
the uom field (matching uoms like 'hours', days'). Click again on
the uom field of the first line, the domain is the one of the
second record, which is wrong.

This rev. directly stores the domains returned by the onchange on
the object representing the record, so that this information isn't
shared between records anymore.
This commit is contained in:
Aaron Bohy
2017-08-07 11:24:15 +02:00
parent 144d8e42fc
commit 8a386b9a08
2 changed files with 65 additions and 6 deletions
@@ -2435,6 +2435,12 @@ var BasicModel = AbstractModel.extend({
*/
_getDomain: function (element, options) {
if (options && options.fieldName) {
if (element._domains[options.fieldName]) {
return Domain.prototype.stringToArray(
element._domains[options.fieldName],
this._getEvalContext(element, true)
);
}
var viewType = options.viewType || element.viewType;
var fieldInfo = element.fieldsInfo[viewType][options.fieldName];
if (fieldInfo && fieldInfo.domain) {
@@ -2697,6 +2703,7 @@ var BasicModel = AbstractModel.extend({
var dataPoint = {
_cache: type === 'list' ? {} : undefined,
_changes: null,
_domains: {},
aggregateValues: params.aggregateValues || {},
context: params.context || {},
count: params.count || res_ids.length,
@@ -3021,12 +3028,7 @@ var BasicModel = AbstractModel.extend({
record._warning = true;
}
if (result.domain) {
var fieldsInfo = record.fieldsInfo[viewType || record.viewType];
for (var fieldName in result.domain) {
if (fieldsInfo[fieldName]) {
fieldsInfo[fieldName].domain = result.domain[fieldName];
}
}
record._domains = _.extend(record._domains, result.domain);
}
return self._applyOnChange(result.value, record).then(function () {
return result;
@@ -1418,6 +1418,63 @@ QUnit.module('relational_fields', {
form.destroy();
});
QUnit.test('many2one in one2many: domain updated by an onchange', function (assert) {
assert.expect(3);
this.data.partner.onchanges = {
trululu: function () {},
};
var domain = [];
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<field name="p">' +
'<tree editable="bttom">' +
'<field name="trululu"/>' +
'</tree>' +
'</field>' +
'</form>',
res_id: 1,
mockRPC: function (route, args) {
if (args.method === 'onchange') {
return $.when({
domain: {
trululu: domain,
},
});
}
if (args.method === 'name_search') {
assert.deepEqual(args.kwargs.args, domain,
"sent domain should be correct");
}
return this._super(route, args);
},
viewOptions: {
mode: 'edit',
},
});
// add a first row with a specific domain for the m2o
domain = [['id', 'in', [10]]]; // domain for subrecord 1
form.$('.o_field_x2many_list_row_add a').click(); // triggers the onchange
form.$('.o_field_widget[name=trululu] input').click(); // triggers the name_search
// add a second row with another domain for the m2o
domain = [['id', 'in', [5]]]; // domain for subrecord 2
form.$('.o_field_x2many_list_row_add a').click(); // triggers the onchange
form.$('.o_field_widget[name=trululu] input').click(); // triggers the name_search
// check again the first row to ensure that the domain hasn't change
domain = [['id', 'in', [10]]]; // domain for subrecord 1 should have been kept
form.$('.o_data_row:first .o_data_cell').click();
form.$('.o_field_widget[name=trululu] input').click(); // triggers the name_search
form.destroy();
});
QUnit.test('updating a many2one from a many2many', function (assert) {
assert.expect(4);