From ac0e4528904064cc85fe47ef3a2d211edd634f0c Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 19 Jan 2018 08:39:26 +0100 Subject: [PATCH] [FIX] web: Kanban: create several records on slow network Before this rev., it crashed when the user used the quick create to create several records (very quickly, or on a slow network), e.g. on crm.lead (there must be a date field on the kanban record to reproduce the crash). The crash occurred when the 'read' RPCs for the created records return misordered. For instance: 1) 'name_create' record 1 (called and returned) 2) call 'read' for record 1 3) 'name_create' record 2 (called and returned) 4) call 'read' for record 2 5) 'read' for record 2 returns 6) 'read' for record 1 returns At step 5, the kanban column was updated with the new state, but this state already contained record 1, which wasn't fetched yet. This rev. ensures that the created records are added to the state once they are fetched and ready to be displayed, not before. --- .../src/js/views/kanban/kanban_model.js | 16 ++-- addons/web/static/tests/views/kanban_tests.js | 74 +++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js index aba59555e93..df6172f0d8b 100644 --- a/addons/web/static/src/js/views/kanban/kanban_model.js +++ b/addons/web/static/src/js/views/kanban/kanban_model.js @@ -22,6 +22,7 @@ var KanbanModel = BasicModel.extend({ * @returns {Deferred} resolves to the local id of the new record */ addRecordToGroup: function (groupID, resId) { + var self = this; var group = this.localData[groupID]; var new_record = this._makeDataPoint({ res_id: resId, @@ -31,15 +32,16 @@ var KanbanModel = BasicModel.extend({ viewType: group.viewType, parentID: groupID, }); - group.data.unshift(new_record.id); - group.res_ids.unshift(resId); - group.count++; - - // update the res_ids and count of the parent - this.localData[group.parentID].count++; - this._updateParentResIDs(group); return this._fetchRecord(new_record).then(function (result) { + group.data.unshift(new_record.id); + group.res_ids.unshift(resId); + group.count++; + + // update the res_ids and count of the parent + self.localData[group.parentID].count++; + self._updateParentResIDs(group); + return result.id; }); }, diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 8d39a5ec21e..c14e9807361 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -546,6 +546,80 @@ QUnit.module('Views', { kanban.destroy(); }); + QUnit.test('quick create several records on slow network', function (assert) { + assert.expect(6); + + var self = this; + var def; + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '' + + '' + + '' + + '
' + + '
', + groupBy: ['bar'], + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'name_create') { + return result.then(function (result) { + // simulate a dynamic default value for foo based on display_name, + // so that we can check that the record has been fetched before + // being displayed + var newRec = _.findWhere(self.data.partner.records, {id: result[0]}); + newRec.foo = newRec.display_name; + return result; + }); + } + if (args.method === 'read') { + return $.when(def).then(_.constant(result)); + } + return result; + }, + }); + + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 1, + "first column should contain one record"); + + // click to add a record, and add two in a row (first one will be delayed) + kanban.$('.o_kanban_header .o_kanban_quick_add i').first().click(); + + assert.strictEqual(kanban.$('.o_kanban_quick_create').length, 1, + "the quick create should be open"); + + def = $.Deferred(); + kanban.$('.o_kanban_quick_create input') + .val('new partner 1') + .trigger($.Event('keypress', { + which: $.ui.keyCode.ENTER, + keyCode: $.ui.keyCode.ENTER, + })); + var firstDef = def; + def = null; + kanban.$('.o_kanban_quick_create input') + .val('new partner 2') + .trigger($.Event('keypress', { + which: $.ui.keyCode.ENTER, + keyCode: $.ui.keyCode.ENTER, + })); + + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 2, + "first column should now contain two records"); + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record:first').text(), + 'new partner 2', "new record should have been fetched"); + + firstDef.resolve(); + + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 3, + "first column should now contain three records"); + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record:first').text(), + 'new partner 1', "new record should have been fetched"); + + kanban.destroy(); + }); + QUnit.test('quick create fail in grouped', function (assert) { assert.expect(7);