[FIX] web: prevent 'reload' event from being handled twice

When the external button of a many2one field is clicked, the
related record is opened in a dialog. When the user clicks on
'Save' in this dialog, a 'reload' event is triggered to reload the
parent view (the one in the background), as the display name might
have changed.

However, when there is another many2one field in the opened dialog,
and if its external button is clicked, there are 2 opened dialogs,
and thus 3 nested form views. If the user clicks on 'Save' in the
third one, only the direct parent view must be reloaded (the one
in the first dialog).

Before this rev., the 'reload' event wasn't stopPropagated, so it
was handled by the view in the dialog, and by the one in the
background, which produced a crash.

This could happen for instance in Sales Order, edit a sale order
line, click on the external button of the product, in the dialog,
click on the external button of the category, edit some field and
click on 'Save'.

Closes #19052
This commit is contained in:
Aaron Bohy
2017-08-30 11:23:29 +02:00
parent 551dc26940
commit 96b1db4cd4
2 changed files with 56 additions and 0 deletions
@@ -539,6 +539,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
* reload
*/
_onReload: function (event) {
event.stopPropagation(); // prevent other controllers from handling this request
var data = event && event.data || {};
var handle = data.db_id;
if (handle) {
@@ -5734,5 +5734,60 @@ QUnit.module('Views', {
config.debug = initialDebugMode;
form.destroy();
});
QUnit.test('reload event is handled only once', function (assert) {
// In this test, several form controllers are nested (two of them are
// opened in dialogs). When the users clicks on save in the last
// opened dialog, a 'reload' event is triggered up to reload the (direct)
// parent view. If this event isn't stopPropagated by the first controller
// catching it, it will crash when the other one will try to handle it,
// as this one doesn't know at all the dataPointID to reload.
assert.expect(9);
var arch = '<form>' +
'<field name="display_name"/>' +
'<field name="trululu"/>' +
'</form>';
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: arch,
archs: {
'partner,false,form': arch,
},
res_id: 2,
mockRPC: function (route, args) {
assert.step(args.method);
if (args.method === 'get_formview_id') {
return $.when(false);
}
return this._super.apply(this, arguments);
},
viewOptions: {
mode: 'edit',
},
});
form.$('.o_external_button').click(); // open the many2one record in a modal
$('.modal .o_external_button').click(); // in the modal, open the many2one record in another modal
$('.modal:nth(1) .o_field_widget[name=display_name]').val('new name').trigger('input');
$('.modal:nth(1) .modal-footer .btn-primary').first().click(); // save changes
assert.strictEqual($('.modal .o_field_widget[name=trululu] input').val(), 'new name',
"record should have been reloaded");
assert.verifySteps([
"read", // main record
"get_formview_id", // id of first form view opened in a dialog
"read", // first dialog
"get_formview_id", // id of second form view opened in a dialog
"read", // second dialog
"write", // save second dialog
"read", // reload first dialog
]);
form.destroy();
});
});
});