diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 5c670e11c73..c9fe8648c17 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -1293,10 +1293,7 @@ var FieldOne2Many = FieldX2Many.extend({ var self = this; var id = ev.data.id; - var onSaved = function (record, hasChanged) { - if (!hasChanged) { - return; - } + var onSaved = function (record) { if (_.some(self.value.data, {id: record.id})) { // the record already exists in the relation, so trigger an // empty 'UPDATE' operation when the user clicks on 'Save' in diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index bb945d6fbb0..7ce12926fba 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -224,6 +224,14 @@ var AbstractView = Class.extend({ _.each(this.loadParams.fieldsInfo.form, function (attrs, fieldName) { var field = fields[fieldName]; + if (!field) { + // when a one2many record is opened in a form view, the fields + // of the main one2many view (list or kanban) are added to the + // fieldsInfo of its form view, but those fields aren't in the + // loadParams.fields, as they are not displayed in the view, so + // we can ignore them. + return; + } if (field.type !== 'one2many' && field.type !== 'many2many') { return; } diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 9d4dc877473..d325d558b96 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1054,7 +1054,13 @@ var BasicModel = AbstractModel.extend({ addFieldsInfo: function (recordID, viewInfo) { var record = this.localData[recordID]; record.fields = _.extend({}, record.fields, viewInfo.fields); - record.fieldsInfo = _.extend({}, record.fieldsInfo, viewInfo.fieldsInfo); + // complete the given fieldsInfo with the fields of the main view, so + // that those field will be reloaded if a reload is triggered by the + // secondary view + var fieldsInfo = _.mapObject(viewInfo.fieldsInfo, function (fieldsInfo) { + return _.defaults({}, fieldsInfo, record.fieldsInfo[record.viewType]); + }); + record.fieldsInfo = _.extend({}, record.fieldsInfo, fieldsInfo); }, /** * For list resources, this freezes the current records order. @@ -1992,7 +1998,8 @@ var BasicModel = AbstractModel.extend({ */ _fetchRecord: function (record, options) { var self = this; - var fieldNames = options && options.fieldNames || record.getFieldNames(); + options = options || {}; + var fieldNames = options.fieldNames || record.getFieldNames(options); fieldNames = _.uniq(fieldNames.concat(['display_name'])); return this._rpc({ model: record.model, @@ -2492,8 +2499,9 @@ var BasicModel = AbstractModel.extend({ _fetchX2Manys: function (record, options) { var self = this; var defs = []; - var fieldNames = options && options.fieldNames || record.getFieldNames(); - var viewType = options && options.viewType || record.viewType; + options = options || {}; + var fieldNames = options.fieldNames || record.getFieldNames(options); + var viewType = options.viewType || record.viewType; _.each(fieldNames, function (fieldName) { var field = record.fields[fieldName]; if (field.type === 'one2many' || field.type === 'many2many') { @@ -3016,11 +3024,15 @@ var BasicModel = AbstractModel.extend({ * default view type. * * @param {Object} element an element from the localData + * @param {Object} [options] + * @param {Object} [options.viewType] current viewType. If not set, we will + * assume main viewType from the record * @returns {string[]} the list of field names */ - _getFieldNames: function (element) { + _getFieldNames: function (element, options) { var fieldsInfo = element.fieldsInfo; - return Object.keys(fieldsInfo && fieldsInfo[element.viewType] || {}); + var viewType = options && options.viewType || element.viewType; + return Object.keys(fieldsInfo && fieldsInfo[viewType] || {}); }, /** * Evaluate the record evaluation context. This method is supposed to be @@ -3500,13 +3512,16 @@ var BasicModel = AbstractModel.extend({ * @see _fetchRecord @see _makeDefaultRecord * * @param {Object} record - * @param {Object} record + * @param {Object} [options] + * @param {Object} [options.viewType] current viewType. If not set, we will + * assume main viewType from the record * @returns {Deferred} resolves to the finished resource */ _postprocess: function (record, options) { var self = this; var defs = []; - _.each(record.getFieldNames(), function (name) { + + _.each(record.getFieldNames(options), function (name) { var field = record.fields[name]; var fieldInfo = record.fieldsInfo[record.viewType][name] || {}; var options = fieldInfo.options || {}; diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index b21f9d435f8..8e777c1acd9 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -213,6 +213,16 @@ var FormController = BasicController.extend({ return changedFields; }); }, + /** + * Overrides to force the viewType to 'form', so that we ensure that the + * correct fields are reloaded (this is only useful for one2many form views). + * + * @override + */ + update: function (params, options) { + params = _.extend({viewType: 'form'}, params); + return this._super(params, options); + }, //-------------------------------------------------------------------------- // Private diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index dcc765dfc6e..d31d8429340 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -5692,7 +5692,7 @@ QUnit.module('relational_fields', { rpcCount++; if (args.method === 'write') { assert.deepEqual(args.args[1].p, [[0, args.args[1].p[0][1], { - int_field: 123, product_id: 41 + foo: false, int_field: 123, product_id: 41, }]]); } return this._super(route, args); @@ -8813,6 +8813,74 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('one2many form view with action button', function (assert) { + // once the action button is clicked, the record is reloaded (via the + // on_close handler, executed because the python method does not return + // any action, or an ir.action.act_window_close) ; this test ensures that + // it reloads the fields of the opened view (i.e. the form in this case). + // See https://github.com/odoo/odoo/issues/24189 + assert.expect(7); + + var data = this.data; + data.partner.records[0].p = [2]; + + var form = createView({ + View: FormView, + model: 'partner', + data: data, + res_id: 1, + arch: '
' + + '' + + '' + + '' + + '