From 512113b78ad4b6c1fb59c313ec73f6c4a04f8737 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 19 Oct 2017 16:42:25 +0200 Subject: [FIX] web: reordering lines using the handle widget triggers onchange issue: When reorder each line trigger an onchange on the line (if they are an onchange linked to the field) and one onchange per line on the parent. Adding an option doNotNotifyChange to avoid the trigger on the parent. --- addons/web/static/src/js/views/basic/basic_model.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 717dcc9f6af..50cd7165a82 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1092,6 +1092,9 @@ var BasicModel = AbstractModel.extend({ * @param {boolean} [options.doNotSetDirty=false] if this flag is set to * true, then we will not tag the record as dirty. This should be avoided * for most situations. + * @param {boolean} [options.notifyChange=true] if this flag is set to + * false, then we will not notify and not trigger the onchange, even though + * it was changed. * @param {string} [options.viewType] current viewType. If not set, we will assume * main viewType from the record * @returns {Deferred} @@ -1119,6 +1122,10 @@ var BasicModel = AbstractModel.extend({ } } + if (options.notifyChange === false) { + return $.when(); + } + return $.when.apply($, defs).then(function () { var onChangeFields = []; // the fields that have changed and that have an on_change for (var fieldName in changes) { -- cgit v1.2.3 From d58d6e270f4106d717821438490f60bc4f71306e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 24 Oct 2017 09:11:17 +0200 Subject: [FIX] web: propagate view context in all x2m operations It could happen in some cases that a rpc coming from a view was not made with the context of the view (and from the action). For example, - enable "Lots & Serial Numbers" in the inventory settings - create a stockable product tracked by lot - go to the inventory dashboard, on the "receipts" card click on the three dots and then on "planned transfer" this link forces the context key "planned_transfer" in the action https://github.com/odoo/odoo/blob/11.0/addons/stock/views/stock_picking_views.xml#L725 - now add a stock move to move some quantity of the product you just created - see that no button appears on the one2many list - save - see that a button appears on the one2many list This is an issue because the read on the o2m at the end is made with the wrong context. The reason is that when we fetch x2m, we create a new datapoint of type 'list' which did not reuse the context of its parent. --- addons/web/static/src/js/views/basic/basic_model.js | 1 + 1 file changed, 1 insertion(+) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 50cd7165a82..c450e2bedc7 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -2295,6 +2295,7 @@ var BasicModel = AbstractModel.extend({ var ids = record.data[fieldName] || []; var list = self._makeDataPoint({ count: ids.length, + context: record.context, fieldsInfo: fieldsInfo, fields: view ? view.fields : fieldInfo.relatedFields, limit: fieldInfo.limit, -- cgit v1.2.3 From 2a1456a29c7be2365115d58b3555e69ab5604c77 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 25 Oct 2017 17:58:01 +0200 Subject: [FIX] web: _applyChange() should return list of changed fields, even when no onchange are performed --- addons/web/static/src/js/views/basic/basic_model.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 f3b23bdab7a..a2731d881d3 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1097,7 +1097,7 @@ var BasicModel = AbstractModel.extend({ * it was changed. * @param {string} [options.viewType] current viewType. If not set, we will assume * main viewType from the record - * @returns {Deferred} + * @returns {Deferred} list of changed fields */ _applyChange: function (recordID, changes, options) { var self = this; @@ -1123,7 +1123,7 @@ var BasicModel = AbstractModel.extend({ } if (options.notifyChange === false) { - return $.when(); + return $.Deferred().resolve(_.keys(changes)); } return $.when.apply($, defs).then(function () { -- cgit v1.2.3 From 7b8001a847e9a89dc7203c5cf2ead715b9bcd45b Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 25 Oct 2017 10:15:25 +0200 Subject: [FIX] hr_expense,web: explicit link_to command for o2m The ORM doesn't automatically link subrecords when it receives an update command for a one2many. However, it may be necessary when the default_get returns existing records for a one2many field. It only worked before this rev. for the hr_expense model because of an hack from 2016 (to reproduce: go to Expenses, check some expenses from the list view, in Actions, click on 'Submit to Manager', the new hr_expense_sheet contains the checked expenses (one2many), and the webclient only sent an update command by linked subrecord). This rev. fixes the problem properly: when the default_get returns existing subrecords for a one2many field, a link_to command (4) is generated alongside the update (1) command. --- addons/web/static/src/js/views/basic/basic_model.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 c450e2bedc7..1d8e68c3e62 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -2634,13 +2634,21 @@ var BasicModel = AbstractModel.extend({ } commands[fieldName].push(command); } else if (_.contains(addedIds, list.res_ids[i])) { - // this is a new id + // this is a new id (maybe existing in DB, but new in JS) relRecord = _.findWhere(relRecordAdded, {res_id: list.res_ids[i]}); changes = this._generateChanges(relRecord, options); if ('id' in changes) { + // the subrecord already exists in db delete changes.id; - commands[fieldName].push(x2ManyCommands.update(relRecord.res_id, changes)); + if (this.isNew(record.id)) { + // if the main record is new, link the subrecord to it + commands[fieldName].push(x2ManyCommands.link_to(relRecord.res_id)); + } + if (!_.isEmpty(changes)) { + commands[fieldName].push(x2ManyCommands.update(relRecord.res_id, changes)); + } } else { + // the subrecord is new, so create it commands[fieldName].push(x2ManyCommands.create(changes)); } } -- cgit v1.2.3 From 6fa63eb15ca809fce2770f8ee75af592a4609c6a Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 25 Oct 2017 13:18:35 +0200 Subject: [FIX] web: default value for o2m inside o2m A traceback was raised when opening a new record for a model with a one2many displayed inside a one2many (e.g. simply displaying the number of records in the relation), and with an onchange setting a default value to the inner one2many (for example, linking it to existing records). As the inner o2m has no subviews, it has no fieldsInfo, and the code assumed that fieldsInfo was always set. This was for example reproducible from v11 as follows: - create a product (with MTO/Buy and call for tender (need purchase agreement module)) - create an SO with this product and confirm it - go to purchase order form view and add a move_dest_ids field in the tree definition of field order_line - go to menu purchase agreement and choose the PA generated by your SO - add a vendor, confirm the PA and click on 'New Quotation'. --- addons/web/static/src/js/views/basic/basic_model.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 1d8e68c3e62..2cf96815b5e 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -2881,7 +2881,8 @@ var BasicModel = AbstractModel.extend({ * @returns {boolean} */ _isFieldProtected: function (record, fieldName, viewType) { - var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName]; + var fieldInfo = record.fieldsInfo && + (record.fieldsInfo[viewType || record.viewType][fieldName]); if (fieldInfo) { var rawModifiers = JSON.parse(fieldInfo.modifiers || "{}"); var modifiers = this._evalModifiers(record, rawModifiers); -- cgit v1.2.3 From 16c90138bde648782bef81c9b499a486ccdcb7c6 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 25 Oct 2017 15:17:38 +0200 Subject: [FIX] web: handle onchange on nested one2manys Suppose that we have a form view with a one2many field o2m_1, displayed as a list. The model of o2m_1 contains a one2many field o2m_2 which isn't displayed in the list view, but which is in the form view. If an onchange sets the value of o2m_1, e.g. with a command 0 to create a new record, and that the value of o2m_2 for this new record is set (e.g. with a command 0 as well), it crashed. --- addons/web/static/src/js/views/basic/basic_model.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 a2731d881d3..9f8ea3227f5 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1313,15 +1313,17 @@ var BasicModel = AbstractModel.extend({ } else { var fieldInfo = record.fieldsInfo[viewType][name]; if (!fieldInfo) { - return; //ignore changes of x2many not in view + return; // ignore changes of x2many not in view } + var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode]; list = self._makeDataPoint({ - fieldsInfo: fieldInfo.fieldsInfo, + fields: view ? view.fields : fieldInfo.relatedFields, + fieldsInfo: view ? view.fieldsInfo : fieldInfo.fieldsInfo, modelName: field.relation, parentID: record.id, static: true, type: 'list', - viewType: fieldInfo.viewType, + viewType: view ? view.type : fieldInfo.viewType, }); } // TODO: before registering the changes, verify that the x2many -- cgit v1.2.3 From c163351e8a6776e046f04e6c264bf4372499dbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 25 Oct 2017 21:50:37 +0200 Subject: [FIX] web: revert to previous state when onchange fails When an onchange fails (for example, because the onchange python code raised a ValidationError, it is convenient to revert the form view to a valid state, which is the previous state. To do that, we keep in the _applyChange function the initial state, and restore it in the case the onchange fails. --- .../web/static/src/js/views/basic/basic_model.js | 41 ++++++++++++---------- 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') 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 2cf96815b5e..27a1e12ecfe 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1109,6 +1109,10 @@ var BasicModel = AbstractModel.extend({ if (!options.doNotSetDirty) { record._isDirty = true; } + var initialData = {}; + this._visitChildren(record, function (elem) { + initialData[elem.id] = $.extend(true, {}, _.pick(elem, 'data', '_changes')); + }); // apply changes to local data for (var fieldName in changes) { @@ -1137,12 +1141,18 @@ var BasicModel = AbstractModel.extend({ } } } - var onchangeDef; + var onchangeDef = $.Deferred(); if (onChangeFields.length) { - onchangeDef = self._performOnChange(record, onChangeFields, options.viewType).then(function (result) { - delete record._warning; - return _.keys(changes).concat(Object.keys(result && result.value || {})); - }); + self._performOnChange(record, onChangeFields, options.viewType) + .then(function (result) { + delete record._warning; + onchangeDef.resolve(_.keys(changes).concat(Object.keys(result && result.value || {}))); + }).fail(function () { + self._visitChildren(record, function (elem) { + _.extend(elem, initialData[elem.id]); + }); + onchangeDef.resolve({}); + }); } else { onchangeDef = $.Deferred().resolve(_.keys(changes)); } @@ -3232,15 +3242,18 @@ var BasicModel = AbstractModel.extend({ }); return $.when.apply($, defs) .then(function () { - return self._performOnChange(record, fields_key).then(function () { + var def = $.Deferred(); + self._performOnChange(record, fields_key).always(function () { if (record._warning) { if (params.allowWarning) { delete record._warning; } else { - return $.Deferred().reject(); + def.reject(); } } + def.resolve(); }); + return def; }) .then(function () { return self._fetchRelationalData(record); @@ -3307,10 +3320,7 @@ var BasicModel = AbstractModel.extend({ * @param {string[]} fields changed fields * @param {string} [viewType] current viewType. If not set, we will assume * main viewType from the record - * @returns {Deferred} Note: this deferred cannot fail. It is either in the - * 'success' or 'pending' state. If the onchange rpc fails, it will be - * resolved with an empty dictionary. Note that the initial change was - * already applied. + * @returns {Deferred} */ _performOnChange: function (record, fields, viewType) { var self = this; @@ -3330,9 +3340,7 @@ var BasicModel = AbstractModel.extend({ var context = this._getContext(record, options); var currentData = this._generateOnChangeData(record, {changesOnly: false}); - var def = $.Deferred(); - - self._rpc({ + return self._rpc({ model: record.model, method: 'onchange', args: [idList, currentData, fields, onchangeSpec, context], @@ -3357,12 +3365,9 @@ var BasicModel = AbstractModel.extend({ record._domains = _.extend(record._domains, result.domain); } return self._applyOnChange(result.value, record).then(function () { - def.resolve(result); + return result; }); - }).fail(function () { - def.resolve({}); }); - return def; }, /** * Once a record is created and some data has been fetched, we need to do -- cgit v1.2.3