From b048a6fc2750bb2111c5627b054bbfde5b8e8cfa Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Fri, 10 Jan 2020 16:01:25 +0000 Subject: [PATCH] [FIX] web: widget noreset if rerendered for modifier If we have a many2many_tags widget that is writable then becomes readonly, we can have in the code this chain of event: - get list of widgets we need to reset some data for - apply modifiers - reset the widgets but applying modifiers can delete a widget (eg. an editable field that becomes readonly), so when resetting we could call code on a deleted widget which caused an error. For example, if we have an attribute: `` if the field goes from editable to readonly and is affected by an onchange, we will call `setIDForLabel` on the deleted instance of the widget which will cause an error. Added test without fix fails with error: TypeError: Cannot read property 'attr' of undefined at Class.setIDForLabel (/web/static/src/js/fields/abstract_field.js:324:35) because as said, the function is called on the editable widget that is removed and replaced with readonly widget at that point. opw-2154471 closes #43137 closes odoo/odoo#43244 X-original-commit: 6f5ca16524be7c5bb45b03e7421154a20edc4cf6 Signed-off-by: Nicolas Lempereur (nle) --- .../src/js/views/basic/basic_renderer.js | 8 +++--- addons/web/static/tests/views/form_tests.js | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 99e6926d503..8af8540dd57 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -93,6 +93,7 @@ var BasicRenderer = AbstractRenderer.extend({ * that have been reset */ confirmChange: function (state, id, fields, ev) { + var self = this; this.state = state; var record = this._getRecord(id); if (!record) { @@ -106,12 +107,10 @@ var BasicRenderer = AbstractRenderer.extend({ // Reset all the field widgets that are marked as changed and the ones // which are configured to always be reset on any change - var resetWidgets = []; _.each(this.allFieldWidgets[id], function (widget) { var fieldChanged = _.contains(fields, widget.name); if (fieldChanged || widget.resetOnAnyFieldChange) { defs.push(widget.reset(record, ev, fieldChanged)); - resetWidgets.push(widget); } }); @@ -121,7 +120,10 @@ var BasicRenderer = AbstractRenderer.extend({ defs.push(this._updateAllModifiers(record)); return Promise.all(defs).then(function () { - return resetWidgets; + return _.filter(self.allFieldWidgets[id], function (widget) { + var fieldChanged = _.contains(fields, widget.name); + return fieldChanged || widget.resetOnAnyFieldChange; + }); }); }, /** diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index e7729c878ee..66cfdc2413f 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -4838,6 +4838,33 @@ QUnit.module('Views', { form.destroy(); }); + QUnit.test('readonly set by modifier do not break many2many_tags', async function (assert) { + assert.expect(0); + + this.data.partner.onchanges = { + bar: function (obj) { + obj.timmy = [[6, false, [12]]]; + }, + }; + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '
', + res_id: 5, + }); + + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_widget[name=bar] input')); + + form.destroy(); + }); + QUnit.test('check if id and active_id are defined', async function (assert) { assert.expect(2);