From 1e8f6d01ffe324348ce5d75f8643f170f6478657 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 9 Aug 2017 09:21:13 +0200 Subject: [PATCH] [FIX] web,*: send correct context on button clicked *account_asset When a button of type 'action' is clicked (execute a given action), special keys must be set in the context (active_id, active_ids and active_model). They must be computed regarding the record containing the clicked button, i.e. if we are in a modal, it must be the id and model of the record displayed in the modal. Before this rev., we always sent the id and model of the record displayed in the background (i.e. the id and model of the url). This caused a bug in MRP that could be reproduced as follows: - open a manufacturing order in form view - click on edit - click on one of the line of the one2many, and click on the green icon to edit a product - click on the update product quantity button on top of it - the product field must be correctly filled, which was not the case before this rev. --- .../static/tests/account_asset_tests.js | 5 +- .../web/static/src/js/chrome/view_manager.js | 33 +++++----- .../src/js/views/basic/basic_controller.js | 12 ++-- .../src/js/views/kanban/kanban_controller.js | 8 ++- .../tests/fields/relational_fields_tests.js | 62 +++++++++++++++++-- addons/web/static/tests/views/form_tests.js | 15 ++--- addons/web/static/tests/views/list_tests.js | 4 +- 7 files changed, 102 insertions(+), 37 deletions(-) diff --git a/addons/account_asset/static/tests/account_asset_tests.js b/addons/account_asset/static/tests/account_asset_tests.js index 8f353f3bf5c..3ae5ac40073 100644 --- a/addons/account_asset/static/tests/account_asset_tests.js +++ b/addons/account_asset/static/tests/account_asset_tests.js @@ -125,7 +125,7 @@ QUnit.test('basic rendering', function (assert) { }); QUnit.test('click events are correctly triggered', function (assert) { - assert.expect(3); + assert.expect(2); var form = createView({ View: FormView, @@ -146,8 +146,7 @@ QUnit.test('click events are correctly triggered', function (assert) { intercepts: { execute_action: function (event) { var data = event.data; - assert.strictEqual(data.model, 'line', "should have correct model"); - assert.deepEqual(data.res_ids, [4], "should have correct res_ids"); + assert.strictEqual(data.env.model, 'line', "should have correct model"); assert.strictEqual(data.action_data.name, 'create_move', "should call correct method"); }, diff --git a/addons/web/static/src/js/chrome/view_manager.js b/addons/web/static/src/js/chrome/view_manager.js index ebd063d7360..25f320670e9 100644 --- a/addons/web/static/src/js/chrome/view_manager.js +++ b/addons/web/static/src/js/chrome/view_manager.js @@ -20,7 +20,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, { custom_events: { execute_action: function(event) { var data = event.data; - this.do_execute_action(data.action_data, data.model, data.res_ids, data.on_closed) + this.do_execute_action(data.action_data, data.env, data.on_closed) .then(data.on_success, data.on_fail); }, search: function(event) { @@ -551,16 +551,19 @@ var ViewManager = Widget.extend(ControlPanelMixin, { * @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``) * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'`` * @param {Object} [action_data.context=null] additional action context, to add to the current context - * @param {DataSet} dataset a dataset object used to communicate with the server - * @param {integer[]} [res_ids] the res_ids of the objects on which the action is to be applied (fallback on env.ids if not set) + * @param {Object} env + * @param {string} env.model the model of the record(s) triggering the action + * @param {integer[]} [env.resIDs] the current ids in the environment where the action is triggered + * @param {integer} [env.currentID] the id of the record triggering the action + * @param {Object} [env.context] a context to pass to the action * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action) */ - do_execute_action: function (action_data, model, res_ids, on_closed) { + do_execute_action: function (action_data, env, on_closed) { var self = this; var result_handler = on_closed || function () {}; - var context = new Context(this.env.context, action_data.context || {}); + var context = new Context(env.context, action_data.context || {}); // OR NULL hereunder: pyeval waits specifically for a null value, different from undefined - var record_id = res_ids && res_ids[0] || null; + var recordID = env.currentID || null; // response handler var handler = function (action) { @@ -574,11 +577,11 @@ var ViewManager = Widget.extend(ControlPanelMixin, { })) ); ncontext.add(action_data.context || {}); - ncontext.add({active_model: self.env.modelName}); - if (res_ids) { + ncontext.add({active_model: env.model}); + if (recordID) { ncontext.add({ - active_id: record_id, - active_ids: res_ids, + active_id: recordID, + active_ids: [recordID], }); } ncontext.add(action.context || {}); @@ -595,7 +598,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, { if (action_data.special === 'cancel') { return handler({"type":"ir.actions.act_window_close"}); } else if (action_data.type === "object") { - var args = res_ids ? [res_ids] : [this.env.ids]; + var args = recordID ? [[recordID]] : [env.resIDs]; if (action_data.args) { try { // Warning: quotes and double quotes problem due to json and xml clash @@ -607,13 +610,13 @@ var ViewManager = Widget.extend(ControlPanelMixin, { } } args.push(context); - var dataset = new data.DataSet(this, model, this.env.context); + var dataset = new data.DataSet(this, env.model, env.context); return dataset.call_button(action_data.name, args).then(handler); } else if (action_data.type === "action") { return data_manager.load_action(action_data.name, _.extend(pyeval.eval('context', context), { - active_model: this.env.modelName, - active_ids: this.env.ids, - active_id: record_id, + active_model: env.model, + active_ids: env.resIDs, + active_id: recordID, })).then(handler); } }, diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js index 3a47f65ab03..6ba9dbea477 100644 --- a/addons/web/static/src/js/views/basic/basic_controller.js +++ b/addons/web/static/src/js/views/basic/basic_controller.js @@ -232,13 +232,17 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { return self.isDestroyed() ? $.when() : self.reload(); }; record = record || this.model.get(this.handle); - var recordID = record.data.id; + this.trigger_up('execute_action', { action_data: _.extend({}, attrs, { - context: record.getContext({additionalContext: attrs.context || {}}), + context: record.getContext({additionalContext: attrs.context || {}}), }), - model: record.model, - res_ids: [recordID], + env: { + context: record.getContext(), + currentID: record.data.id, + model: record.model, + resIDs: record.res_ids, + }, on_closed: function (reason) { if (!_.isObject(reason)) { reload(reason); diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js index 2cf6a94285e..6870dbbbe24 100644 --- a/addons/web/static/src/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/js/views/kanban/kanban_controller.js @@ -237,8 +237,12 @@ var KanbanController = BasicController.extend({ } this.trigger_up('execute_action', { action_data: attrs, - model: record.model, - res_ids: [record.res_id], + env: { + context: record.getContext(), + currentID: record.res_id, + model: record.model, + resIDs: record.res_ids, + }, on_closed: function () { self.model.reload(record.id).then(function (db_id) { var data = self.model.get(db_id); diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 6d4c68d245f..29f8666455a 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -1002,6 +1002,60 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('list in form: call button in sub view', function (assert) { + assert.expect(6); + + this.data.partner.records[0].p = [2]; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + mockRPC: function (route, args) { + if (route === '/web/dataset/call_kw/product/get_formview_id') { + return $.when(false); + } + return this._super.apply(this, arguments); + }, + intercepts: { + execute_action: function (event) { + assert.strictEqual(event.data.env.model, 'product', + 'should call with correct model in env'); + assert.strictEqual(event.data.env.currentID, 37, + 'should call with correct currentID in env'); + assert.deepEqual(event.data.env.resIDs, [37], + 'should call with correct resIDs in env'); + }, + }, + archs: { + 'product,false,form': '
' + + '
' + + '
' + + '
', + }, + }); + + form.$buttons.find('.o_form_button_edit').click(); + form.$('td.o_data_cell:first').click(); // edit first one2many line + form.$('.o_external_button').click(); // open product sub view in modal + $('button:contains("Just do it !")').click(); // click on action button + $('button:contains("Just don\'t do it !")').click(); // click on object button + + form.destroy(); + }); + QUnit.test('autocompletion in a many2one, in form view with a domain', function (assert) { assert.expect(1); @@ -4036,9 +4090,9 @@ QUnit.module('relational_fields', { res_id: 1, intercepts: { execute_action: function (event) { - assert.deepEqual(event.data.res_ids, [2], + assert.deepEqual(event.data.env.currentID, 2, 'should call with correct id'); - assert.strictEqual(event.data.model, 'partner', + assert.strictEqual(event.data.env.model, 'partner', 'should call with correct model'); assert.strictEqual(event.data.action_data.name, 'method_name', "should call correct method"); @@ -4080,9 +4134,9 @@ QUnit.module('relational_fields', { res_id: 1, intercepts: { execute_action: function (event) { - assert.deepEqual(event.data.res_ids, [2], + assert.deepEqual(event.data.env.currentID, 2, 'should call with correct id'); - assert.strictEqual(event.data.model, 'partner', + assert.strictEqual(event.data.env.model, 'partner', 'should call with correct model'); assert.strictEqual(event.data.action_data.name, 'method_name', "should call correct method"); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 2a08a305fd0..1f572aa8325 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -901,7 +901,7 @@ QUnit.module('Views', { testUtils.intercept(form, 'execute_action', function (event) { assert.strictEqual(event.data.action_data.name, "post", "should trigger execute_action with correct method name"); - assert.deepEqual(event.data.res_ids, [2], "should have correct id in event data"); + assert.deepEqual(event.data.env.currentID, 2, "should have correct id in event data"); event.data.on_success(); event.data.on_closed(); }); @@ -954,7 +954,7 @@ QUnit.module('Views', { testUtils.intercept(form, 'execute_action', function (event) { assert.step('execute_action'); - assert.deepEqual(event.data.res_ids, [resID], + assert.deepEqual(event.data.env.currentID, resID, "execute action should be done on correct record id"); event.data.on_success(); event.data.on_closed(); @@ -1004,7 +1004,7 @@ QUnit.module('Views', { testUtils.intercept(form, 'execute_action', function (event) { assert.step('execute_action'); - assert.deepEqual(event.data.res_ids, [resID], + assert.deepEqual(event.data.env.currentID, resID, "execute action should be done on correct record id"); event.data.on_success(); event.data.on_closed(); @@ -3561,8 +3561,9 @@ QUnit.module('Views', { }, intercepts: { execute_action: function (e) { - assert.deepEqual(e.data.action_data.context, {test: 2}, - "button context should have been evaluated and given to the action, without previous context"); + assert.deepEqual(e.data.action_data.context, { + 'test': 2 + }, "button context should have been evaluated and given to the action, with magicc without previous context"); }, }, }); @@ -3595,8 +3596,8 @@ QUnit.module('Views', { }, intercepts: { execute_action: function (e) { - assert.deepEqual(e.data.action_data.context, {}, - "button context should have been evaluated and given to the action, without previous context"); + assert.deepEqual(e.data.action_data.context, { + }, "button context should have been evaluated and given to the action, with magic keys but without previous context"); }, }, }); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 859622e8a49..2247a64fa3c 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -1171,9 +1171,9 @@ QUnit.module('Views', { }, intercepts: { execute_action: function (event) { - assert.deepEqual(event.data.res_ids, [1], + assert.deepEqual(event.data.env.currentID, 1, 'should call with correct id'); - assert.strictEqual(event.data.model, 'foo', + assert.strictEqual(event.data.env.model, 'foo', 'should call with correct model'); assert.strictEqual(event.data.action_data.name, 'button_action', "should call correct method");