[FIX] web: onchange: send correct nested x2manys values
Commit [1] recently fixed an issue with nested x2many fields and onchanges: in some cases, all field values weren't sent to the server as they should. However, there is a small issue with this fix. We didn't correctly apply the default value to option `changesOnly`: when not given, it was considered false, whereas in this particular function it should have been true. It caused an issue in the following scenario: Have a form view with an x2many field (say A) displayed as a list. In the list, there is another x2many field (say B), and (whatever its type) a field C with an onchange. When the user changes C, the onchange is performed, and we send to the server the value of all fields. In particular, in the row, we send the value for the many2one field pointing to the main record (the inverse field of the x2many relation). The value for that field is basically the whole record, containing itself field A. For field A, the value is a list of commands, and for the updated record, it is a command 1. Before this commit, in the values sent for this command, field B was the empty array, even if it wasn't empty. This issue was reproducible in account.move, on an existing record having already a line in invoice_line_ids (this is field A). In that line, tax_ids (this is field B) must have a tax which is included in the price. When changing the quantity of the product, the subtotal wasn't correctly computed. [1] https://github.com/odoo/odoo/commit/a8b43d02066b2299ac2f4b88056c37725e9ce6cd opw~2489755 closes odoo/odoo#69227 X-original-commit: 3f54ca3e29d1537f66c6a3b56494dc065d9230e9 Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com> Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -3241,14 +3241,15 @@ var BasicModel = AbstractModel.extend({
|
||||
options = options || {};
|
||||
var viewType = options.viewType || record.viewType;
|
||||
var changes;
|
||||
if ('changesOnly' in options && !options.changesOnly) {
|
||||
const changesOnly = 'changesOnly' in options ? !!options.changesOnly : true;
|
||||
if (!changesOnly) {
|
||||
changes = _.extend({}, record.data, record._changes);
|
||||
} else {
|
||||
changes = _.extend({}, record._changes);
|
||||
}
|
||||
var withReadonly = options.withReadonly || false;
|
||||
var commands = this._generateX2ManyCommands(record, {
|
||||
changesOnly: 'changesOnly' in options ? options.changesOnly : true,
|
||||
changesOnly: changesOnly,
|
||||
withReadonly: withReadonly,
|
||||
});
|
||||
for (var fieldName in record.fields) {
|
||||
@@ -3265,7 +3266,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var type = record.fields[fieldName].type;
|
||||
var value;
|
||||
if (type === 'one2many' || type === 'many2many') {
|
||||
if (!options.changesOnly || (commands[fieldName] && commands[fieldName].length)) { // replace localId by commands
|
||||
if (!changesOnly || (commands[fieldName] && commands[fieldName].length)) { // replace localId by commands
|
||||
changes[fieldName] = commands[fieldName];
|
||||
} else { // no command -> no change for that field
|
||||
delete changes[fieldName];
|
||||
|
||||
@@ -1633,7 +1633,6 @@ QUnit.module('fields', {}, function () {
|
||||
'</form>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
debug: 1,
|
||||
});
|
||||
|
||||
|
||||
@@ -4668,6 +4667,55 @@ QUnit.module('fields', {}, function () {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('parent data is properly sent on an onchange rpc (existing x2many record)', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
this.data.partner.onchanges = {
|
||||
display_name: function () {},
|
||||
};
|
||||
this.data.partner.records[0].p = [1];
|
||||
this.data.partner.records[0].turtles = [2];
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="foo"/>
|
||||
<field name="p">
|
||||
<tree editable="top">
|
||||
<field name="display_name"/>
|
||||
<field name="turtles" widget="many2many_tags"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
res_id: 1,
|
||||
mockRPC(route, args) {
|
||||
if (args.method === 'onchange') {
|
||||
const fieldValues = args.args[1];
|
||||
assert.strictEqual(fieldValues.trululu.foo, "yop");
|
||||
// we only send fields that changed inside the reverse many2one
|
||||
assert.deepEqual(fieldValues.trululu.p, [
|
||||
[1, 1, { display_name: 'new val' }],
|
||||
]);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsOnce(form, '.o_data_row');
|
||||
|
||||
await testUtils.dom.click(form.$('.o_data_row .o_data_cell:first'));
|
||||
|
||||
assert.containsOnce(form, '.o_data_row.o_selected_row');
|
||||
await testUtils.fields.editInput(form.$('.o_selected_row .o_field_widget[name=display_name]'), "new val");
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('parent data is properly sent on an onchange rpc, new record', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user