diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 9af32adda93..11961e6fecf 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -908,7 +908,7 @@ var BasicModel = AbstractModel.extend({ for (var fieldName in changes) { field = record.fields[fieldName]; if (field.type === 'one2many' || field.type === 'many2many') { - defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName])); + defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName], viewType)); } else if (field.type === 'many2one') { defs.push(this._applyMany2OneChange(record, fieldName, changes[fieldName])); } else { @@ -1112,13 +1112,15 @@ var BasicModel = AbstractModel.extend({ * @param {string} fieldName * @param {Object} command A command object. It should have a 'operation' * key. For example, it looks like {operation: ADD, id: 'partner_1'} + * @param {string} [viewType] current viewType. If not set, we will assume + * main viewType from the record * @returns {Deferred} */ - _applyX2ManyChange: function (record, fieldName, command) { + _applyX2ManyChange: function (record, fieldName, command, viewType) { var self = this; var list = this.localData[record._changes[fieldName] || record.data[fieldName]]; var field = record.fields[fieldName]; - var fieldInfo = record.fieldsInfo[record.viewType][fieldName]; + var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName]; var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode]; var rec; var defs = []; @@ -1939,8 +1941,8 @@ var BasicModel = AbstractModel.extend({ // generate update commands for records that have been // updated (it may happen with editable lists) _.each(relData, function (relRecord) { - if (!_.isEmpty(relRecord._changes)) { - var changes = self._generateChanges(relRecord); + var changes = self._generateChanges(relRecord); + if (!_.isEmpty(changes)) { var command = x2ManyCommands.update(relRecord.res_id, changes); commands[fieldName].push(command); } @@ -1958,8 +1960,8 @@ var BasicModel = AbstractModel.extend({ if (_.contains(keptIds, relIds[i])) { // this is an id that already existed relRecord = _.findWhere(relData, {res_id: relIds[i]}); - if (!_.isEmpty(relRecord._changes)) { - changes = this._generateChanges(relRecord); + changes = this._generateChanges(relRecord); + if (!_.isEmpty(changes)) { command = x2ManyCommands.update(relRecord.res_id, changes); didChange = true; } else { diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 66c825cb3c7..6003cef3d0c 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -3103,6 +3103,63 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('one2many with x2many in form view (but not in list view)', function (assert) { + assert.expect(1); + + // avoid error when saving the edited related record (because the + // related x2m field is unknown in the inline list view) + // also ensure that the changes are correctly saved + + this.data.turtle.fields.o2m = {string: "o2m", type: "one2many", relation: 'user'}; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + archs: { + "turtle,false,form": '
' + + '' + + '', + }, + viewOptions: { + mode: 'edit', + }, + mockRPC: function (route, args) { + if (args.method === 'write') { + assert.deepEqual(args.args[1].turtles, [[1, 2, { + partner_ids: [[6, false, [2, 4, 1]]], + }]]); + } + return this._super.apply(this, arguments); + }, + }); + + form.$('.o_data_row:first').click(); // edit first record + + var $input = $('.modal .o_field_many2manytags input'); + $input.click(); // opens the dropdown + $input.autocomplete('widget').find('li').click(); // add 'first record' + + // add a many2many tag and save + $('.modal .o_field_x2many_list_row_add a').click(); + $('.modal .o_field_widget[name=name]').val('test').trigger('input'); + $('.modal .modal-footer .btn-primary').click(); // save + + form.$buttons.find('.o_form_button_save').click(); + + form.destroy(); + }); + QUnit.test('one2many (who contains display_name) with tree view and without form view', function (assert) { assert.expect(1);