[FIX] web: BasicModel: correctly apply x2many changes

Suppose that we have a form view with a one2many list (non
editable) containing a record. In edit mode, the user clicks on
that record to edit it in a form view (in a dialog). In this form
view, there is an x2many field that isn't in the list. Before this
rev., it crashed when the user clicked on 'Save' in the dialog
after editing the value of the x2many.

This was because the basic model didn't use the correct fieldsInfo
in that case (it use the fieldsInfo of the list view instead of the
one of the form view, and the x2many field isn't in the list).

Also ensures that the record is correctly saved even if editing the
x2many is the only change done, as this wasn't the case before.
This commit is contained in:
Aaron Bohy
2017-05-29 20:53:40 +02:00
parent 5788ee8a1c
commit 45bce324f3
2 changed files with 66 additions and 7 deletions
@@ -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 {
@@ -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: '<form string="Partners">' +
'<group>' +
'<field name="turtles">' +
'<tree>' +
'<field name="turtle_foo"/>' +
'</tree>' +
'</field>' +
'</group>' +
'</form>',
res_id: 1,
archs: {
"turtle,false,form": '<form string="Turtles">' +
'<field name="partner_ids" widget="many2many_tags"/>' +
'</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);