diff --git a/client/src/views/record/base.js b/client/src/views/record/base.js index be127e861d..814803cc44 100644 --- a/client/src/views/record/base.js +++ b/client/src/views/record/base.js @@ -1070,10 +1070,10 @@ class BaseRecordView extends View { this.trigger('before:save'); model.trigger('before:save'); - let onError = (xhr, reject) => { - this.handleSaveError(xhr, options); - this.afterSaveError(); + let onError = (xhr, reject, resolve) => { + const promise = this.handleSaveError(xhr, options, resolve); + this.afterSaveError(); this.setModelAttributes(beforeSaveAttributes); this.lastSaveCancelReason = 'error'; @@ -1081,7 +1081,13 @@ class BaseRecordView extends View { this.trigger('error:save'); this.trigger('cancel:save', {reason: 'error'}); - reject('error'); + promise.then(skipReject => { + if (skipReject) { + return; + } + + reject('error'); + }) }; return new Promise((resolve, reject) => { @@ -1108,7 +1114,7 @@ class BaseRecordView extends View { resolve(); }) .catch(xhr => { - onError(xhr, reject); + onError(xhr, reject, resolve); }); }); } @@ -1118,15 +1124,17 @@ class BaseRecordView extends View { * * @param {module:ajax.Xhr} xhr XHR. * @param {module:views/record/base~saveOptions} [options] Options. + * @param {function} saveResolve Resolve save promise. + * @return {Promise} */ - handleSaveError(xhr, options) { + handleSaveError(xhr, options, saveResolve) { let handlerData = null; if (~[409, 500].indexOf(xhr.status)) { let statusReason = xhr.getResponseHeader('X-Status-Reason'); if (!statusReason) { - return; + return Promise.resolve(false); } try { @@ -1148,7 +1156,7 @@ class BaseRecordView extends View { catch (e) { console.error('Could not parse error response body.'); - return; + return Promise.resolve(false); } handlerData.data = data; @@ -1157,7 +1165,7 @@ class BaseRecordView extends View { } if (!handlerData || !handlerData.reason) { - return; + return Promise.resolve(false); } let reason = handlerData.reason; @@ -1168,25 +1176,31 @@ class BaseRecordView extends View { this.getMetadata() .get(['clientDefs', 'Global', 'saveErrorHandlers', reason]); - if (handlerName) { - Espo.loader.require(handlerName, Handler => { - let handler = new Handler(this); + return new Promise(resolve => { + if (handlerName) { + Espo.loader.require(handlerName, Handler => { + let handler = new Handler(this); - handler.process(handlerData.data, options); - }); + handler.process(handlerData.data, options); - xhr.errorIsHandled = true; + resolve(false); + }); - return; - } + xhr.errorIsHandled = true; - let methodName = 'errorHandler' + Espo.Utils.upperCaseFirst(reason); + return; + } - if (methodName in this) { - xhr.errorIsHandled = true; + let methodName = 'errorHandler' + Espo.Utils.upperCaseFirst(reason); - this[methodName](handlerData.data, options); - } + if (methodName in this) { + xhr.errorIsHandled = true; + + let skipReject = this[methodName](handlerData.data, options, saveResolve); + + resolve(skipReject || false); + } + }); } /** diff --git a/client/src/views/record/detail.js b/client/src/views/record/detail.js index 8cdf67b65c..a6591a7925 100644 --- a/client/src/views/record/detail.js +++ b/client/src/views/record/detail.js @@ -606,13 +606,14 @@ class DetailRecordView extends BaseRecordView { * A `save` action. * * @param {{options?: module:views/record/base~saveOptions}} [data] Data. + * @return Promise */ actionSave(data) { data = data || {}; let modeBeforeSave = this.mode; - this.save(data.options) + const promise = this.save(data.options) .catch(reason => { if (modeBeforeSave === this.MODE_EDIT && reason === 'error') { this.setEditMode(); @@ -625,6 +626,8 @@ class DetailRecordView extends BaseRecordView { this.focusOnFirstDiv(); $(window).scrollTop(0); } + + return promise; } actionCancelEdit() { @@ -2521,7 +2524,7 @@ class DetailRecordView extends BaseRecordView { this.enableActionItems(); } - errorHandlerDuplicate(duplicates) { + errorHandlerDuplicate(duplicates, o, resolve) { Espo.Ui.notify(false); this.createView('duplicate', 'views/modals/duplicate', { @@ -2538,9 +2541,11 @@ class DetailRecordView extends BaseRecordView { 'X-Skip-Duplicate-Check': 'true', } } - }); + }).then(() => resolve()); }); }); + + return true; } // noinspection JSUnusedGlobalSymbols diff --git a/client/src/views/record/edit.js b/client/src/views/record/edit.js index c1b5a2ea90..94bf5d8f46 100644 --- a/client/src/views/record/edit.js +++ b/client/src/views/record/edit.js @@ -92,7 +92,7 @@ class EditRecordView extends DetailRecordView { let isNew = this.isNew; - this.save(data.options) + return this.save(data.options) .then(() => { if (this.options.duplicateSourceId) { this.returnUrl = null;