From e9de309511d64897444645f8a9ea4bf701709bb8 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 2 Jun 2017 14:28:07 +0200 Subject: [PATCH] [FIX] web: editable list: setRowMode returns a deferred This function should return a deferred, as indicated in the docstring. However, in some cases, it doesn't. It then crashed as the FieldX2Many tries to call done() on undefined. This happens for instance on the customer invoice form view: create a new record, select a partner, add a line, select a product, directly click on the invoice date field (this will trigger an onchange which will totally override the value of the one2many). The FielX2Many calls setRowMode to switch the edited row back to readonly, but this row doesn't exist anymore as the one2many value has been overriden by the onchange, so setRowMode wrongly returns nothing, and it crashes. --- .../js/views/list/list_editable_renderer.js | 2 +- .../tests/fields/relational_fields_tests.js | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 9364d1ba9fe..08b9e7f1602 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -155,7 +155,7 @@ ListRenderer.include({ var self = this; var rowIndex = _.findIndex(this.state.data, {id: recordID}); if (rowIndex < 0) { - return; + return $.when(); } var editMode = (mode === 'edit'); var record = this.state.data[rowIndex]; diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 4f326e14f98..1c2a0ab86d3 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -3852,6 +3852,61 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('editable list: value reset by an onchange', function (assert) { + // this test reproduces a subtle behavior that may occur in a form view: + // the user adds a record in a one2many field, and directly clicks on a + // datetime field of the form view which has an onchange, which totally + // overrides the value of the one2many (commands 5 and 0). The handler + // that switches the edited row to readonly is then called after the + // new value of the one2many field is applied (the one returned by the + // onchange), so the row that must go to readonly doesn't exist anymore. + assert.expect(2); + + this.data.partner.onchanges = { + datetime: function (obj) { + obj.turtles = [[5], [0, 0, {display_name: 'new'}]]; + }, + }; + + var def; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'onchange') { + return $.when(def).then(_.constant(result)); + } + return result; + }, + }); + + // trigger the two onchanges + form.$('.o_field_x2many_list_row_add a').click(); + form.$('.o_data_row .o_field_widget').val('a name').trigger('input'); + def = $.Deferred(); + form.$('.o_datepicker_input').click(); // focusout o2m and set value to today + + // resolve the onchange def + def.resolve(); + + assert.strictEqual(form.$('.o_data_row').length, 1, + "should have one record in the o2m"); + assert.strictEqual(form.$('.o_data_row .o_data_cell').text(), 'new', + "should be the record created by the onchange"); + + form.destroy(); + }); + QUnit.module('FieldMany2Many'); QUnit.test('many2many kanban: edition', function (assert) {