From ad1cceb4fbd2f054af647833bd34e4e06012bb6c Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 20 Feb 2018 11:01:22 +0100 Subject: [FIX] web: one2many and onchange corner case issue Let's assume a one2many list view inside a form view, displaying a single char field A. There is an onchange on the form view returning a command 0 (CREATE) for the one2many. The command 0 only specifies a value for field A. The one2many list isn't editable, so when a sub-record is clicked, it is open in a form view (in a dialog). In this form view, an x2many field B is displayed in a sub list or kanban view. Before this rev., it crashed when the user tried to open the created one2many sub-record (returned by the onchange) in the form view, because of the presence of the x2many field B, which wasn't correctly processed by the BasicModel (its value was undefined, whereas it must be a valid, empty, dataPoint). Moreover, the limit of the x2many field B wasn't set, so a pager was displayed as soon as it contained at least one record. Fixes #22050 --- .../static/tests/fields/relational_fields_tests.js | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'addons/web/static/tests/fields/relational_fields_tests.js') diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 4f7041ad2d5..89764ce319b 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -3063,6 +3063,108 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('onchange on one2many containing x2many in form view', function (assert) { + assert.expect(16); + + this.data.partner.onchanges = { + foo: function (obj) { + obj.turtles = [[0, false, {turtle_foo: 'new record'}]]; + }, + }; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + archs: { + 'partner,false,list': '', + 'partner,false,search': '', + }, + }); + + assert.strictEqual(form.$('.o_data_row').length, 1, + "the onchange should have created one record in the relation"); + + // open the created o2m record in a form view, and add a m2m subrecord + // in its relation + form.$('.o_data_row').click(); + + assert.strictEqual($('.modal').length, 1, "should have opened a dialog"); + assert.strictEqual($('.modal .o_data_row').length, 0, + "there should be no record in the one2many in the dialog"); + + // add a many2many subrecord + $('.modal .o_field_x2many_list_row_add a').click(); + + assert.strictEqual($('.modal').length, 2, + "should have opened a second dialog"); + + // select a many2many subrecord + $('.modal:nth(1) .o_list_view .o_data_cell:first').click(); + + assert.strictEqual($('.modal').length, 1, + "second dialog should be closed"); + assert.strictEqual($('.modal .o_data_row').length, 1, + "there should be one record in the one2many in the dialog"); + assert.notOk($('.modal .o_x2m_control_panel .o_cp_pager div').is(':visible'), + 'm2m pager should be hidden'); + + // click on 'Save & Close' + $('.modal .modal-footer .btn-primary:first').click(); + + assert.strictEqual($('.modal').length, 0, "dialog should be closed"); + + // reopen o2m record, and another m2m subrecord in its relation, but + // discard the changes + form.$('.o_data_row').click(); + + assert.strictEqual($('.modal').length, 1, "should have opened a dialog"); + assert.strictEqual($('.modal .o_data_row').length, 1, + "there should be one record in the one2many in the dialog"); + + // add another m2m subrecord + $('.modal .o_field_x2many_list_row_add a').click(); + + assert.strictEqual($('.modal').length, 2, + "should have opened a second dialog"); + + $('.modal:nth(1) .o_list_view .o_data_cell:first').click(); + + assert.strictEqual($('.modal').length, 1, + "second dialog should be closed"); + assert.strictEqual($('.modal .o_data_row').length, 2, + "there should be two records in the one2many in the dialog"); + + // click on 'Discard' + $('.modal .modal-footer .btn-default').click(); + + assert.strictEqual($('.modal').length, 0, "dialog should be closed"); + + // reopen o2m record to check that second changes have properly been discarded + form.$('.o_data_row').click(); + + assert.strictEqual($('.modal').length, 1, "should have opened a dialog"); + assert.strictEqual($('.modal .o_data_row').length, 1, + "there should be one record in the one2many in the dialog"); + + form.destroy(); + }); + QUnit.test('embedded one2many with handle widget with minimum setValue calls', function (assert) { var done = assert.async(); assert.expect(20); -- cgit v1.2.3 From a16013be6636db58a4252fafd67033c6643a1e48 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 23 Feb 2018 13:48:20 +0100 Subject: [FIX] web: multi pages ordered o2m with onchange Let's assume a one2many displayed over several pages with a default order such that a record, say R, that would normally be on page 1 is actually on another page. Moreover, there is an onchange on that one2many which converts all commands 4 (LINK_TO) into commands 1 (UPDATE), which is standard in the ORM. When the first line of the one2many was edited, the record R was fully loaded, whereas it shouldn't have been (as it is not on the current page). So basically, a useless read was performed. Moreover, if there was an x2many field in the one2many list, a crash occurred because relational data of R that shouldn't have been read wasn't postprocessed by the BasicModel. The useless read occurred because the one2many subrecords weren't correctly sorted before checking if some of them/some of their fields should be read. This is not the case anymore. OPW-1816800 --- .../static/tests/fields/relational_fields_tests.js | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'addons/web/static/tests/fields/relational_fields_tests.js') diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 89764ce319b..7344d0032d4 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -8112,6 +8112,80 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('one2many with several pages, onchange and default order', function (assert) { + // This test reproduces a specific scenario where a one2many is displayed + // over several pages, and has a default order such that a record that + // would normally be on page 1 is actually on another page. Moreover, + // there is an onchange on that one2many which converts all commands 4 + // (LINK_TO) into commands 1 (UPDATE), which is standard in the ORM. + // This test ensures that the record displayed on page 2 is never fully + // read. + assert.expect(8); + + var data = this.data; + data.partner.records[0].turtles = [1, 2, 3]; + data.turtle.records[0].partner_ids = [1]; + data.partner.onchanges = { + turtles: function (obj) { + var res = _.map(obj.turtles, function (command) { + if (command[0] === 1) { // already an UPDATE command: do nothing + return command; + } + // convert LINK_TO commands to UPDATE commands + var id = command[1]; + var record = _.findWhere(data.turtle.records, {id: id}); + return [1, id, _.pick(record, ['turtle_int', 'turtle_foo', 'partner_ids'])]; + }); + obj.turtles = [[5]].concat(res); + }, + }; + + var form = createView({ + View: FormView, + model: 'partner', + data: data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + var ids = args.method === 'read' ? ' [' + args.args[0] + ']' : ''; + assert.step(args.method + ids); + return this._super.apply(this, arguments); + }, + res_id: 1, + viewOptions: { + mode: 'edit', + }, + }); + + assert.strictEqual(form.$('.o_data_cell.foo').text(), 'blipkawa', + "should display two records out of three, in the correct order"); + + // edit turtle_int field of first row + form.$('.o_data_cell:first').click(); + form.$('.o_field_widget[name=turtle_int]').val(3).trigger('input'); + form.$el.click(); // leave edition + + assert.strictEqual(form.$('.o_data_cell.foo').text(), 'blipkawa', + "should still display the same two records"); + + assert.verifySteps([ + 'read [1]', // main record + 'read [1,2,3]', // one2many (turtle_foo, all records) + 'read [2,3]', // one2many (all fields in view, records of first page) + 'read [2,4]', // many2many inside one2many (partner_ids), first page only + 'onchange', + ]); + + form.destroy(); + }); + QUnit.test('new record, with one2many with more default values than limit', function (assert) { assert.expect(2); -- cgit v1.2.3 From 132137939cc8460868a0e77ff78814a9d95e5a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 26 Feb 2018 09:38:44 +0100 Subject: [FIX] web: prevent footer from being rerendered In some cases, the footer in a form view dialog could be duplicated. The reason was that the form view was rerendered completely, footer included, by the renderer, out of the formcontroller control (it is the formcontroller's job to move the footer buttons to the footer). A possible fix is to work on the form renderer to make it aware that the footer buttons are expected to be only rendered once, but it has the disadvantage to be a fix specific to this issue, and any other form view behaviour with some side effects may have the same issue. This commit tries to prevent the form view from rerendering itself completely in those cases. This also has the disadvantage of possibly not being a complete fix, if some edge cases are missed. However, I have the feeling that we still want to avoid rerendering the full view if possible. --- addons/web/static/tests/fields/relational_fields_tests.js | 4 ---- 1 file changed, 4 deletions(-) (limited to 'addons/web/static/tests/fields/relational_fields_tests.js') diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 7344d0032d4..fa22f5d4561 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -368,10 +368,6 @@ QUnit.module('relational_fields', { "should have an open record button"); form.$('input.o_input').click(); - form.$('input.o_input').trigger($.Event('keyup', { - which: $.ui.keyCode.ESC, - keyCode: $.ui.keyCode.ESC, - })); assert.strictEqual(form.$('button.o_external_button:visible').length, 1, "should still have an open record button"); -- cgit v1.2.3