Skip to content

Commit

Permalink
[FIX] web: can update one2many with custom field widget
Browse files Browse the repository at this point in the history
One might design a custom field widget to display/interact with a
one2many field. Before this commit, if this field widget triggered
a field_changed event to update a related record, it crashed,
because the code assumed that there was a view associated with the
field.

Closes #68276
opw~2468238

Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
Co-authored-by: Aaron Bohy <aab@odoo.com>
  • Loading branch information
kmagusiak and aab-odoo committed Mar 24, 2021
1 parent 9c21989 commit ca2995e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 1 deletion.
4 changes: 3 additions & 1 deletion addons/web/static/src/js/views/basic/basic_model.js
Expand Up @@ -1948,7 +1948,9 @@ var BasicModel = AbstractModel.extend({
case 'UPDATE':
list._changes.push({operation: 'UPDATE', id: command.id});
if (command.data) {
defs.push(this._applyChange(command.id, command.data, { viewType: view.type }));
defs.push(this._applyChange(command.id, command.data, {
viewType: view && view.type,
}));
}
break;
case 'FORGET':
Expand Down
Expand Up @@ -3,6 +3,7 @@ odoo.define('web.field_one_to_many_tests', function (require) {

var AbstractField = require('web.AbstractField');
var AbstractStorageService = require('web.AbstractStorageService');
var fieldRegistry = require('web.field_registry');
var FormView = require('web.FormView');
var KanbanRecord = require('web.KanbanRecord');
var ListRenderer = require('web.ListRenderer');
Expand Down Expand Up @@ -9242,6 +9243,77 @@ QUnit.module('fields', {}, function () {

form.destroy();
});

QUnit.test('update a one2many from a custom field widget', async function (assert) {
// In this test, we define a custom field widget to render/update a one2many
// field. For the update part, we ensure that updating primitive fields of a sub
// record works. There is no guarantee that updating a relational field on the sub
// record would work. Deleting a sub record works as well. However, creating sub
// records isn't supported. There are obviously a lot of limitations, but the code
// hasn't been designed to support all this. This test simply encodes what can be
// done, and this comment explains what can't (and won't be implemented in stable
// versions).
assert.expect(3);

this.data.partner.records[0].p = [1, 2];
const MyRelationalField = AbstractField.extend({
events: {
'click .update': '_onUpdate',
'click .delete': '_onDelete',
},
async _render() {
const records = await this._rpc({
method: 'read',
model: 'partner',
args: [this.value.res_ids],
});
this.$el.text(records.map(r => `${r.display_name}/${r.int_field}`).join(', '));
this.$el.append($('<button class="update fa fa-edit">'));
this.$el.append($('<button class="delete fa fa-trash">'));
},
_onUpdate() {
this._setValue({
operation: 'UPDATE',
id: this.value.data[0].id,
data: {
display_name: 'new name',
int_field: 44,
},
});
},
_onDelete() {
this._setValue({
operation: 'DELETE',
ids: [this.value.data[0].id],
});
},
});
fieldRegistry.add('my_relational_field', MyRelationalField);

const form = await createView({
View: FormView,
model: 'partner',
data: this.data,
arch: `
<form>
<field name="p" widget="my_relational_field"/>
</form>`,
res_id: 1,
});

assert.strictEqual(form.$('.o_field_widget[name=p]').text(), 'first record/10, second record/9');

await testUtils.dom.click(form.$('button.update'));

assert.strictEqual(form.$('.o_field_widget[name=p]').text(), 'new name/44, second record/9');

await testUtils.dom.click(form.$('button.delete'));

assert.strictEqual(form.$('.o_field_widget[name=p]').text(), 'second record/9');

form.destroy();
delete fieldRegistry.map.my_relational_field;
});
});
});
});

0 comments on commit ca2995e

Please sign in to comment.