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 00e2fc43f6d..e105b78a836 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -201,7 +201,7 @@ var BasicModel = AbstractModel.extend({ }) .then(function () { _.each(records, function (record) { - record.res_ids.splice(record.offset, 1)[0]; + record.res_ids.splice(record.offset, 1); record.res_id = record.res_ids[record.offset]; record.count--; }); @@ -215,11 +215,15 @@ var BasicModel = AbstractModel.extend({ * everything that was stored in a _changes key. * * @param {string} id local resource id + * @param {Object} [options] + * @param {boolean} [options.rollback=false] if true, the changes will + * be reset to the last _savePoint, otherwise, they are reset to null */ - discardChanges: function (id) { + discardChanges: function (id, options) { + options = options || {}; var element = this.localData[id]; this._visitChildren(element, function (elem) { - elem._changes = null; + elem._changes = options.rollback && elem._savePoint || null; }); }, /** @@ -550,9 +554,9 @@ var BasicModel = AbstractModel.extend({ * @param {string} record_id local resource * @param {Object} [options] * @param {boolean} [options.reload=true] if true, data will be reloaded - * @param {boolean} [options.localSave=false] if true, the record will only - * be 'locally' saved: its changes will move from the _changes key to the - * data key + * @param {boolean} [options.savePoint=false] if true, the record will only + * be 'locally' saved: its changes written in a _savePoint key that can + * be restored later by call discardChanges with option rollback to true * @returns {Deferred} */ save: function (record_id, options) { @@ -560,10 +564,9 @@ var BasicModel = AbstractModel.extend({ return this.mutex.exec(function () { options = options || {}; var record = self.localData[record_id]; - if (options.localSave) { + if (options.savePoint) { if (record._changes) { - _.extend(record.data, record._changes); - record._changes = null; + record._savePoint = _.extend(record._savePoint || {}, record._changes); } return $.when(); } @@ -735,7 +738,7 @@ var BasicModel = AbstractModel.extend({ * notifyChanges, it is not protected by a mutex. Every changes from the * user to the model go through this method. * - * @param {string} recordID + * @param {string} recordID * @param {Object} changes * @returns {Deferred} */ @@ -1647,25 +1650,25 @@ var BasicModel = AbstractModel.extend({ addedIds = _.difference(relIds, list.res_ids); keptIds = _.intersection(list.res_ids, relIds); for (i = 0; i < keptIds.length; i++) { - commands[fieldName].push(x2ManyCommands.link_to(keptIds[i])); - } - for (i = 0; i < removedIds.length; i++) { - commands[fieldName].push(x2ManyCommands.delete(removedIds[i])); + relRecord = _.findWhere(relData, {res_id: keptIds[i]}); + var command; + if (!_.isEmpty(relRecord._changes)) { + var r = this.get(relRecord.id, {raw: true}); + var changes = _.pick(r.data, Object.keys(relRecord._changes)); + command = x2ManyCommands.update(relRecord.res_id, changes); + } else { + command = x2ManyCommands.link_to(keptIds[i]); + } + commands[fieldName].push(command); } for (i = 0; i < addedIds.length; i++) { relRecord = _.findWhere(relData, {res_id: addedIds[i]}); relRecord = this.get(relRecord.id, {raw: true}); commands[fieldName].push(x2ManyCommands.create(_.omit(relRecord.data, 'id'))); } - // FIXME: update of records temporarily disabled as it doesn't work if we updated - // the record in a form view, as the _changes contains all the fields of the - // form view instead of the one that have changed - // _.each(relData, function (relRecord) { - // var new_values = relRecord._changes; - // if (!_.isEmpty(new_values)) { - // commands[fieldName].push(x2ManyCommands.update(relRecord.res_id, new_values)); - // } - // }); + for (i = 0; i < removedIds.length; i++) { + commands[fieldName].push(x2ManyCommands.delete(removedIds[i])); + } } } } diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index 9c69231937d..22f4688bc70 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -209,7 +209,7 @@ var FormController = BasicController.extend({ * readonly mode after saving the record * @param {boolean} [options.reload=true] if true, reload the record after * saving - * @param {boolean} [options.localSave=false] if true, the record will only + * @param {boolean} [options.savePoint=false] if true, the record will only * be 'locally' saved: its changes will move from the _changes key to the * data key * @returns {Deferred} @@ -228,7 +228,7 @@ var FormController = BasicController.extend({ if (this.checkInvalidFields()) { return $.Deferred().reject(); } else { - return this.model.save(this.handle, {reload: shouldReload, localSave: options.localSave}) + return this.model.save(this.handle, {reload: shouldReload, savePoint: options.savePoint}) .then(function () { if (!stayInEdit) { self._toReadOnlyMode(); diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index 828ecf58e90..fe7ddddaf10 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -109,7 +109,9 @@ var FormViewDialog = ViewDialog.extend({ close: true, click: function () { if (!readonly) { - self.form_view.model.discardChanges(self.form_view.handle); + self.form_view.model.discardChanges(self.form_view.handle, { + rollback: self.shouldSaveLocally, + }); } }, }]; @@ -180,7 +182,7 @@ var FormViewDialog = ViewDialog.extend({ self.form_view = formView; var fragment = document.createDocumentFragment(); if (self.recordID && self.shouldSaveLocally) { - self.model.save(self.recordID, {localSave: true}); + self.model.save(self.recordID, {savePoint: true}); } self.form_view.appendTo(fragment) .then(function () { @@ -213,7 +215,7 @@ var FormViewDialog = ViewDialog.extend({ def = this.form_view.saveRecord({ stayInEdit: true, reload: false, - localSave: this.shouldSaveLocally, + savePoint: this.shouldSaveLocally, }); } return $.when(def).then(function () { diff --git a/addons/web/static/test/fields/relational_fields_tests.js b/addons/web/static/test/fields/relational_fields_tests.js index 482c2f4e104..ac26d523b00 100644 --- a/addons/web/static/test/fields/relational_fields_tests.js +++ b/addons/web/static/test/fields/relational_fields_tests.js @@ -1053,8 +1053,9 @@ QUnit.module('relational_fields', { }); QUnit.test('one2many list (non editable): edition', function (assert) { - assert.expect(9); + assert.expect(12); + var nbWrite = 0; this.data.partner.records[0].p = [2, 4]; var form = createView({ View: FormView, @@ -1071,6 +1072,15 @@ QUnit.module('relational_fields', { '' + '', res_id: 1, + mockRPC: function (route, args) { + if (args.method === 'write') { + nbWrite++; + assert.deepEqual(args.args[1], { + p: [[1, 2, {display_name: 'new name'}], [2, 4, false]] + }, "should have sent the correct commands"); + } + return this._super.apply(this, arguments); + }, }); assert.ok(!form.$('.o_list_record_delete').length, @@ -1096,16 +1106,21 @@ QUnit.module('relational_fields', { $('.modal .modal-footer .btn-primary').click(); // save assert.strictEqual(form.$('.o_list_view tbody td:first()').text(), 'new name', 'value of subrecord should have been updated'); + assert.strictEqual(nbWrite, 0, "should not have write anything in DB"); // create new subrecords // TODO when 'Add an item' will be implemented // delete subrecords - form.$('.o_list_record_delete:first()').click(); + form.$('.o_list_record_delete:nth(1)').click(); assert.strictEqual(form.$('.o_list_view td.o_list_number').length, 1, 'should contain 1 subrecord'); - assert.strictEqual(form.$('.o_list_view tbody td:first()').text(), 'aaa', - 'the remaining subrecord should be "aaa"'); + assert.strictEqual(form.$('.o_list_view tbody td:first()').text(), 'new name', + 'the remaining subrecord should be "new name"'); + + form.$buttons.find('.o_form_button_save').click(); // save the record + assert.strictEqual(nbWrite, 1, "should have write the changes in DB"); + form.destroy(); });