1219 lines
32 KiB
JavaScript
1219 lines
32 KiB
JavaScript
/*
|
|
* Axelor Business Solutions
|
|
*
|
|
* Copyright (C) 2005-2019 Axelor (<http://axelor.com>).
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License, version 3,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
(function() {
|
|
|
|
"use strict";
|
|
|
|
var ui = angular.module('axelor.ui');
|
|
|
|
var equals = angular.equals,
|
|
forEach = angular.forEach,
|
|
isArray = angular.isArray,
|
|
isObject = angular.isObject,
|
|
isDate = angular.isDate;
|
|
|
|
function dummyEquals(a, b) {
|
|
if (a === b) return true;
|
|
if (a === null || b === null) return false;
|
|
if (a !== a && b !== b) return true; // NaN === NaN
|
|
var keys = _.keys(a).filter(function (k) { return k.indexOf('$') === 0; });
|
|
if (keys.length === 0) {
|
|
return true;
|
|
}
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var k = keys[i];
|
|
if (!equals(a[k], b[k])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function updateValues(source, target, itemScope, formScope) {
|
|
|
|
if (equals(source, target) && dummyEquals(source, target) && (!source || !source.$force)) {
|
|
return;
|
|
}
|
|
|
|
// handle json records
|
|
if (source && formScope && formScope._model === 'com.axelor.meta.db.MetaJsonRecord') {
|
|
if (source.attrs || source.id) {
|
|
source = source.id > 0
|
|
? _.pick(source, 'jsonModel', 'name', 'attrs', 'id', 'version')
|
|
: _.pick(source, 'jsonModel', 'name', 'attrs');
|
|
}
|
|
|
|
var values = source.attrs ? _.extend({}, JSON.parse(source.attrs)) : source;
|
|
var fix = function (rec) {
|
|
if (!rec) return rec;
|
|
if (_.isArray(rec)) return _.map(rec, fix);
|
|
if (rec.id > 0 && (rec.version || rec.attrs)) {
|
|
rec = _.pick(rec, 'id', 'name', 'selected');
|
|
if (!rec.selected) delete rec.selected;
|
|
}
|
|
return rec;
|
|
};
|
|
|
|
_.each(values, function (v, k) {
|
|
values[k] = fix(v);
|
|
});
|
|
|
|
// if called from form fields
|
|
if (itemScope && itemScope.updateJsonValues) {
|
|
return itemScope.updateJsonValues(values);
|
|
}
|
|
|
|
// onNew or onSave from main form
|
|
var current = target && target.attrs ? JSON.parse(target.attrs) : {};
|
|
if (source.attrs || !source.jsonModel) {
|
|
source.attrs = JSON.stringify(_.extend({}, current, values));
|
|
}
|
|
|
|
} else if (itemScope && itemScope.updateJsonValues) {
|
|
return itemScope.updateJsonValues(source);
|
|
}
|
|
|
|
function compact(value) {
|
|
if (!value) return value;
|
|
if (value.version === undefined) return value;
|
|
if (!value.id) return value;
|
|
var res = _.extend(value);
|
|
res.$version = res.version;
|
|
res.version = undefined;
|
|
return res;
|
|
}
|
|
|
|
var changed = false;
|
|
|
|
forEach(source, function(value, key) {
|
|
var dest;
|
|
var newValue = value;
|
|
var oldValue = target[key];
|
|
if (oldValue === newValue) {
|
|
return;
|
|
}
|
|
if (isArray(value)) {
|
|
dest = target[key] || [];
|
|
newValue = _.map(value, function(item) {
|
|
var found = _.find(dest, function(v){
|
|
return item.id && v.id === item.id;
|
|
});
|
|
if (_.has(item, "version") && item.id) item.$fetched = true;
|
|
if (found) {
|
|
var found_ = _.extend({}, found);
|
|
var changed_ = updateValues(item, found_);
|
|
changed = changed || changed_;
|
|
return changed_ ? found_ : found;
|
|
}
|
|
return item;
|
|
});
|
|
} else if (isObject(value) && !isDate(value)) {
|
|
dest = target[key] || {};
|
|
if (dest.id === value.id) {
|
|
if (_.isNumber(dest.version)) {
|
|
dest = _.extend({}, dest);
|
|
changed = updateValues(value, dest, itemScope, formScope) || changed;
|
|
} else {
|
|
dest.$updatedValues = value;
|
|
if (formScope) {
|
|
formScope.$broadcast('on:check-nested-values', value);
|
|
}
|
|
}
|
|
} else {
|
|
dest = compact(value);
|
|
}
|
|
newValue = dest;
|
|
}
|
|
|
|
if (!equals(oldValue, newValue)) {
|
|
changed = true;
|
|
target[key] = newValue;
|
|
}
|
|
});
|
|
|
|
if (target && changed) {
|
|
target.$dirty = true;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
function handleError(scope, item, message) {
|
|
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
var ctrl = item.data('$ngModelController');
|
|
if (!ctrl) {
|
|
return;
|
|
}
|
|
|
|
if (ctrl.$doReset) {
|
|
ctrl.$doReset();
|
|
}
|
|
|
|
if (!message) {
|
|
ctrl.$doReset = null;
|
|
return;
|
|
}
|
|
|
|
var e = $('<span class="error"></span>').text(message);
|
|
var p = item.parent('.form-item');
|
|
|
|
if (item.children(':first').is(':input,.input-append,.picker-input')) {
|
|
p.append(e);
|
|
} else {
|
|
p.prepend(e);
|
|
}
|
|
|
|
var clear = scope.$on('on:edit', function(){
|
|
ctrl.$doReset();
|
|
});
|
|
|
|
function cleanUp(items) {
|
|
var idx = items.indexOf(ctrl.$doReset);
|
|
if (idx > -1) {
|
|
items.splice(idx, 1);
|
|
}
|
|
}
|
|
|
|
ctrl.$doReset = function(value) {
|
|
|
|
cleanUp(ctrl.$viewChangeListeners);
|
|
cleanUp(ctrl.$formatters);
|
|
|
|
ctrl.$setValidity('invalid', true);
|
|
ctrl.$doReset = null;
|
|
|
|
e.remove();
|
|
clear();
|
|
|
|
return value;
|
|
};
|
|
|
|
if (!item.hasClass('readonly')) {
|
|
ctrl.$setValidity('invalid', false);
|
|
}
|
|
ctrl.$viewChangeListeners.push(ctrl.$doReset);
|
|
ctrl.$formatters.push(ctrl.$doReset);
|
|
}
|
|
|
|
function ActionHandler($scope, ViewService, options) {
|
|
|
|
if (!options || !options.action)
|
|
throw 'No action provided.';
|
|
|
|
this.canSave = options.canSave;
|
|
this.name = options.name;
|
|
this.prompt = options.prompt;
|
|
this.action = options.action;
|
|
this.element = options.element || $();
|
|
|
|
this.scope = $scope;
|
|
this.ws = ViewService;
|
|
this.viewType = $scope.viewType;
|
|
}
|
|
|
|
ActionHandler.prototype = {
|
|
|
|
constructor: ActionHandler,
|
|
|
|
onLoad : function() {
|
|
return this.handle();
|
|
},
|
|
|
|
onNew: function() {
|
|
return this.handle();
|
|
},
|
|
|
|
onSave: function() {
|
|
var self = this;
|
|
return this._fireBeforeSave().then(function() {
|
|
return self.handle();
|
|
});
|
|
},
|
|
|
|
onTabSelect: function(unblocked) {
|
|
return this.onSelect.apply(this, arguments);
|
|
},
|
|
|
|
onSelect: function(unblocked) {
|
|
var self = this;
|
|
var blockUI = this._blockUI;
|
|
if (unblocked) {
|
|
this._blockUI = angular.noop;
|
|
}
|
|
function reset() {
|
|
self._blockUI = blockUI;
|
|
}
|
|
var promise = this.handle();
|
|
promise.then(reset, reset);
|
|
return promise;
|
|
},
|
|
|
|
onClick: function(event) {
|
|
var self = this;
|
|
var prompt = this._getPrompt();
|
|
if (prompt) {
|
|
var deferred = this.ws.defer(),
|
|
promise = deferred.promise;
|
|
axelor.dialogs.confirm(prompt, function(confirmed){
|
|
if (confirmed) {
|
|
self._fireBeforeSave().then(function() {
|
|
self.handle().then(deferred.resolve, deferred.reject);
|
|
});
|
|
} else {
|
|
self.scope.$timeout(deferred.reject);
|
|
}
|
|
}, {
|
|
yesNo: false
|
|
});
|
|
return promise;
|
|
}
|
|
return this._fireBeforeSave().then(function() {
|
|
return self.handle();
|
|
});
|
|
},
|
|
|
|
onChange: function(event) {
|
|
return this.handle({ wait: 100 });
|
|
},
|
|
|
|
_getPrompt: function () {
|
|
var prompt = this.prompt;
|
|
var itemScope = this.element.scope();
|
|
if (_.isFunction(itemScope.attr) && !this.element.is('[ui-slick-grid]')) {
|
|
prompt = itemScope.attr('prompt') || prompt;
|
|
}
|
|
return _.isString(prompt) ? prompt : null;
|
|
},
|
|
|
|
_getContext: function() {
|
|
var scope = this.scope,
|
|
context = scope.getContext ? scope.getContext() : scope.record,
|
|
viewParams = scope._viewParams || {};
|
|
|
|
context = _.extend({}, viewParams.context, context);
|
|
if (context._model === undefined) {
|
|
context._model = scope._model;
|
|
}
|
|
|
|
// include button name as _signal (used by workflow engine)
|
|
if (this.element.is("button,a.button-item,li.action-item")) {
|
|
context._signal = this.element.attr('name') || this.element.attr('x-name');
|
|
}
|
|
|
|
return context;
|
|
},
|
|
|
|
_getRootFormElement: function () {
|
|
var formElement = $(this.element).parents('form[ui-form]:last');
|
|
if (formElement.length === 0) {
|
|
formElement = this._getFormElement();
|
|
}
|
|
return formElement;
|
|
},
|
|
|
|
_getFormElement: function () {
|
|
|
|
var elem = $(this.element);
|
|
var formElement = elem;
|
|
|
|
if (formElement.is('form')) {
|
|
return formElement;
|
|
}
|
|
|
|
formElement = elem.data('$editorForm') || elem.parents('form:first');
|
|
if (!formElement || !formElement.get(0)) { // toolbar button
|
|
formElement = this.element.parents('.form-view:first').find('form:first');
|
|
}
|
|
if (formElement.length === 0) {
|
|
formElement = this.element;
|
|
}
|
|
return formElement;
|
|
},
|
|
|
|
handle: function(options) {
|
|
var that = this;
|
|
var action = this.action.trim();
|
|
var deferred = this.ws.defer();
|
|
|
|
var all = this.scope.$actionPromises || [];
|
|
var pending = all.slice();
|
|
var opts = _.extend({}, options);
|
|
|
|
all.push(deferred.promise);
|
|
|
|
this.scope.waitForActions(function () {
|
|
var promise = that._handleAction(action);
|
|
function done() {
|
|
setTimeout(function () {
|
|
var i = all.indexOf(deferred.promise);
|
|
if (i > -1) {
|
|
all.splice(i, 1);
|
|
}
|
|
}, 10);
|
|
}
|
|
promise.then(done, done);
|
|
promise.then(deferred.resolve, deferred.reject);
|
|
}, opts.wait || 10, pending);
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
_blockUI: function() {
|
|
// block the entire ui (auto unblocks when actions are complete)
|
|
_.delay(axelor.blockUI, 100);
|
|
},
|
|
|
|
_fireBeforeSave: function() {
|
|
var scope = this._getRootFormElement().scope();
|
|
var event = scope.$broadcast('on:before-save', scope.record);
|
|
var deferred = this.ws.defer();
|
|
|
|
if (event.defaultPrevented) {
|
|
if (event.error) {
|
|
axelor.dialogs.error(event.error);
|
|
}
|
|
setTimeout(function() {
|
|
deferred.reject(event.error);
|
|
});
|
|
} else {
|
|
scope.$timeout(function() {
|
|
scope.ajaxStop(function() {
|
|
deferred.resolve();
|
|
}, 100);
|
|
}, 50);
|
|
}
|
|
return deferred.promise;
|
|
},
|
|
|
|
_checkVersion: function() {
|
|
var self = this;
|
|
var scope = this.scope;
|
|
var deferred = this.ws.defer();
|
|
|
|
if (scope.checkVersion) {
|
|
scope.checkVersion(function (verified) {
|
|
if (verified) {
|
|
return deferred.resolve();
|
|
}
|
|
axelor.dialogs.error(
|
|
_t("The record has been updated or delete by another action."));
|
|
deferred.reject();
|
|
});
|
|
} else {
|
|
deferred.resolve();
|
|
}
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
_handleNew: function() {
|
|
var self = this;
|
|
var scope = this.scope;
|
|
var deferred = this.ws.defer();
|
|
|
|
if (scope.onNew) {
|
|
return scope.onNew();
|
|
}
|
|
if (scope.editRecord) {
|
|
scope.editRecord(null);
|
|
deferred.resolve();
|
|
} else {
|
|
deferred.reject();
|
|
}
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
_handleSave: function(validateOnly) {
|
|
if (validateOnly) {
|
|
return this.__handleSave(validateOnly);
|
|
}
|
|
var self = this;
|
|
var deferred = this.ws.defer();
|
|
|
|
this._checkVersion().then(function () {
|
|
self.__handleSave().then(deferred.resolve, deferred.reject);
|
|
}, deferred.reject);
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
__handleSave: function(validateOnly) {
|
|
var self = this;
|
|
var scope = this.scope;
|
|
var id = (scope.record||{}).id;
|
|
var o2mPopup = scope._isPopup && (scope.$parent.field||{}).serverType === "one-to-many";
|
|
if (o2mPopup && !validateOnly && this.name == 'onLoad' && (!id || id < 0)) {
|
|
var deferred = this.ws.defer();
|
|
var msg = _t("The {0}={1} event can't call 'save' action on unsaved o2m item.", this.name, this.action);
|
|
deferred.reject(msg);
|
|
console.error(msg);
|
|
return deferred.promise;
|
|
}
|
|
return this._fireBeforeSave().then(function() {
|
|
return self.__doHandleSave(validateOnly);
|
|
});
|
|
},
|
|
|
|
__doHandleSave: function(validateOnly) {
|
|
|
|
this._blockUI();
|
|
|
|
// save should be done on root form scope only
|
|
var rootForm = this._getRootFormElement();
|
|
var scope = rootForm.is('[ui-view-grid]') ? this.scope : rootForm.scope();
|
|
var deferred = this.ws.defer();
|
|
|
|
if (scope.isValid && !scope.isValid()) {
|
|
if (scope.showErrorNotice) {
|
|
scope.showErrorNotice();
|
|
} else {
|
|
axelor.notify.error(_t('Please correct the invalid form values.'), {
|
|
title: _t('Validation error')
|
|
});
|
|
}
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
if (validateOnly || (scope.isDirty && !scope.isDirty())) {
|
|
deferred.resolve();
|
|
return deferred.promise;
|
|
}
|
|
|
|
function doEdit(rec) {
|
|
var params = scope._viewParams || {};
|
|
scope.editRecord(rec);
|
|
if (params.$viewScope) {
|
|
params.$viewScope.updateRoute();
|
|
}
|
|
deferred.resolve();
|
|
}
|
|
|
|
function doSave(values) {
|
|
var ds = scope._dataSource;
|
|
ds.save(values).success(function(rec, page) {
|
|
if (scope.doRead) {
|
|
return scope.doRead(rec.id).success(doEdit);
|
|
}
|
|
return ds.read(rec.id).success(doEdit);
|
|
});
|
|
}
|
|
|
|
var values = _.extend({ _original: scope.$$original }, scope.record);
|
|
if (scope.onSave) {
|
|
scope.onSave({
|
|
values: values,
|
|
callOnSave: false,
|
|
wait: false
|
|
}).then(deferred.resolve, deferred.reject);
|
|
} else {
|
|
doSave(values);
|
|
}
|
|
|
|
this._invalidateContext = true;
|
|
return deferred.promise;
|
|
},
|
|
|
|
_closeView: function (scope) {
|
|
if (scope.onOK) {
|
|
return scope.onOK();
|
|
}
|
|
var tab = scope._viewParams || scope.selectedTab;
|
|
if (scope.closeTab) {
|
|
scope.closeTab(tab);
|
|
} else if (scope.$parent) {
|
|
this._closeView(scope.$parent);
|
|
}
|
|
},
|
|
|
|
_isSameViewType: function () {
|
|
return this.viewType === this.scope.viewType;
|
|
},
|
|
|
|
_handleAction: function(action) {
|
|
|
|
this._blockUI();
|
|
|
|
var self = this,
|
|
scope = this.scope,
|
|
context = this._getContext(),
|
|
deferred = this.ws.defer();
|
|
|
|
if (!this._isSameViewType()) {
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
|
|
function resolveLater() {
|
|
deferred.resolve();
|
|
return deferred.promise;
|
|
}
|
|
|
|
function chain(items) {
|
|
var first = _.first(items);
|
|
if (first === undefined) {
|
|
return resolveLater();
|
|
}
|
|
return self._handleSingle(first).then(function(pending) {
|
|
if (_.isString(pending) && pending.trim().length) {
|
|
return self._handleAction(pending);
|
|
}
|
|
|
|
var _deferred = self.ws.defer();
|
|
scope.$timeout(function () {
|
|
scope.ajaxStop(function() {
|
|
_deferred.resolve();
|
|
});
|
|
});
|
|
|
|
return _deferred.promise.then(function () {
|
|
return chain(_.rest(items));
|
|
});
|
|
});
|
|
}
|
|
|
|
if (!action) {
|
|
return resolveLater();
|
|
}
|
|
|
|
action = action.replace(/(^\s*,?\s*)|(\s*,?\s*$)/, '');
|
|
|
|
var pattern = /,\s*(sync)\s*(,|$)/;
|
|
if (pattern.test(action)) {
|
|
var which = pattern.exec(action)[1];
|
|
axelor.dialogs.error(_t('Invalid use of "{0}" action, must be the first action.', which));
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
|
|
pattern = /(^sync\s*,\s*)|(^sync$)/;
|
|
if (pattern.test(action)) {
|
|
action = action.replace(pattern, '');
|
|
return this._fireBeforeSave().then(function() {
|
|
return self._handleAction(action);
|
|
});
|
|
}
|
|
|
|
pattern = /(^|,)\s*(new)\s*,/;
|
|
if (pattern.test(action)) {
|
|
var which = pattern.exec(action)[2];
|
|
axelor.dialogs.error(_t('Invalid use of "{0}" action, must be the last action.', which));
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
|
|
pattern = /(^|,)\s*(close)\s*,/;
|
|
if (pattern.test(action)) {
|
|
axelor.dialogs.error(_t('Invalid use of "{0}" action, must be the last action.', pattern.exec(action)[2]));
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (action === 'close') {
|
|
this._closeView(scope);
|
|
deferred.resolve();
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (action === 'new') {
|
|
return this._handleNew();
|
|
}
|
|
|
|
if (action === 'validate') {
|
|
return this._handleSave(true);
|
|
}
|
|
|
|
if (action === 'save') {
|
|
return this._handleSave();
|
|
}
|
|
|
|
if (this._invalidateContext) {
|
|
context = this._getContext();
|
|
this._invalidateContext = false;
|
|
}
|
|
|
|
var model = context._model || scope._model;
|
|
var data = scope.getActionData ? scope.getActionData(context) : null;
|
|
if (data && context._signal) {
|
|
data._signal = context._signal;
|
|
}
|
|
|
|
var promise = this.ws.action(action, model, context, data).then(function(response){
|
|
var resp = response.data,
|
|
data = resp.data || [];
|
|
if (resp.errors) {
|
|
data.splice(0, 0, {
|
|
errors: resp.errors
|
|
});
|
|
}
|
|
return chain(data);
|
|
});
|
|
|
|
promise.then(deferred.resolve, deferred.reject);
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
_handleSingle: function(data) {
|
|
|
|
var deferred = this.ws.defer();
|
|
|
|
if (!data || data.length === 0) {
|
|
deferred.resolve();
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (!this._isSameViewType()) {
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
|
|
var self = this,
|
|
scope = this.scope,
|
|
formElement = this._getFormElement(),
|
|
formScope = formElement.data('$scope') || scope,
|
|
rootForm = this._getRootFormElement(),
|
|
rootScope = rootForm.is('[ui-view-grid]') ? scope : rootForm.scope();
|
|
|
|
function doReload(pending) {
|
|
self._invalidateContext = true;
|
|
var promise = _.isFunction(rootScope.reload) ? rootScope.reload() : scope.reload();
|
|
if (promise) {
|
|
promise.then(function(){
|
|
deferred.resolve(pending);
|
|
}, deferred.reject);
|
|
} else {
|
|
deferred.resolve(pending);
|
|
}
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (data.exportFile) {
|
|
(function () {
|
|
var link = "ws/files/data-export/" + data.exportFile;
|
|
var frame = $('<iframe>').appendTo('body').hide();
|
|
frame.attr("src", link);
|
|
setTimeout(function(){
|
|
frame.attr("src", "");
|
|
frame.remove();
|
|
frame = null;
|
|
}, 5000);
|
|
})();
|
|
}
|
|
|
|
if (data.signal === 'refresh-app') {
|
|
if(data.flash || data.info) {
|
|
axelor.dialogs.box(data.flash || data.info, {
|
|
onClose: function () {
|
|
window.location.reload();
|
|
}
|
|
});
|
|
} else {
|
|
window.location.reload();
|
|
}
|
|
return deferred.promise;
|
|
}
|
|
|
|
if(data.flash || data.info) {
|
|
axelor.dialogs.box(data.flash || data.info, {
|
|
onClose: function () {
|
|
if (data.pending) {
|
|
scope.$applyAsync(function(){
|
|
if (data.reload) {
|
|
return doReload(data.pending);
|
|
}
|
|
deferred.resolve(data.pending);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if (data.pending) {
|
|
return deferred.promise;
|
|
}
|
|
}
|
|
|
|
if(data.notify) {
|
|
axelor.notify.info(data.notify);
|
|
}
|
|
|
|
if(data.error) {
|
|
axelor.dialogs.error(data.error, function(){
|
|
scope.$applyAsync(function(){
|
|
if (data.action) {
|
|
self._handleAction(data.action);
|
|
}
|
|
deferred.reject();
|
|
});
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (data.alert) {
|
|
axelor.dialogs.confirm(data.alert, function(confirmed){
|
|
scope.$applyAsync(function(){
|
|
if (confirmed) {
|
|
return deferred.resolve(data.pending);
|
|
}
|
|
if (data.action) {
|
|
self._handleAction(data.action);
|
|
}
|
|
deferred.reject();
|
|
});
|
|
}, {
|
|
title: _t('Warning'),
|
|
yesNo: false
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (!_.isEmpty(data.errors)) {
|
|
var hasError = false;
|
|
_.each(data.errors, function(v, k){
|
|
var item = (findItems(k) || $()).first();
|
|
handleError(scope, item, v);
|
|
if(v && v.length > 0) {
|
|
hasError = true;
|
|
}
|
|
});
|
|
if(hasError) {
|
|
deferred.reject();
|
|
return deferred.promise;
|
|
}
|
|
}
|
|
|
|
if (data.values) {
|
|
updateValues(data.values, scope.record, scope, formScope);
|
|
if (scope.onChangeNotify) {
|
|
scope.onChangeNotify(scope, data.values);
|
|
}
|
|
this._invalidateContext = true;
|
|
axelor.$adjustSize();
|
|
}
|
|
|
|
if (data.reload) {
|
|
return (function () {
|
|
var promise = doReload(data.pending);
|
|
if (data.view) {
|
|
promise.then(function () {
|
|
doOpenView(data.view);
|
|
});
|
|
}
|
|
return promise;
|
|
})();
|
|
}
|
|
|
|
if (data.validate || data.save) {
|
|
scope.$timeout(function () {
|
|
self._handleSave(!!data.validate).then(function(){
|
|
scope.ajaxStop(function () {
|
|
deferred.resolve(data.pending);
|
|
}, 100);
|
|
}, deferred.reject);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (data['new']) {
|
|
scope.$timeout(function () {
|
|
self._handleNew().then(function(){
|
|
scope.ajaxStop(function () {
|
|
deferred.resolve(data.pending);
|
|
}, 100);
|
|
}, deferred.reject);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
if (data.signal) {
|
|
formScope.$broadcast(data.signal, data['signal-data']);
|
|
}
|
|
|
|
function findItems(name) {
|
|
|
|
var items;
|
|
var toolbar;
|
|
var containers;
|
|
|
|
if (formElement.is('[ui-slick-editors]')) {
|
|
containers = formElement.parent().add(formElement);
|
|
} else if (formElement.parent().is('[ui-slick-editors],.slick-cell')) {
|
|
containers = formElement.parent().parent().add(formElement);
|
|
} else if (formElement.parent().is('[ui-panel-editor]')) {
|
|
containers = formElement.parent().add(formElement).is('.m2o-editor-form,.o2m-editor-form') ? formElement : formElement.parents('[ui-form]:first').add(formElement);
|
|
} else {
|
|
containers = formElement;
|
|
toolbar = formElement.parents('.form-view:first,.search-view:first')
|
|
.find('.record-toolbar:first,.search-view-toolbar:first');
|
|
}
|
|
|
|
var formPath = formScope.formPath;
|
|
if (formScope._model === 'com.axelor.meta.db.MetaJsonRecord') {
|
|
formPath = formPath || 'attrs';
|
|
}
|
|
|
|
items = containers.find('[x-path="' + (formPath ? formPath + '.' + name : name) + '"]');
|
|
if (items.length === 0 && formPath != 'attrs') {
|
|
items = containers.find('[x-path="attrs.' + name + '"]');
|
|
}
|
|
if (toolbar) {
|
|
return toolbar.find('[name="' + name + '"],[x-name="' + name + '"]').add(items);
|
|
}
|
|
return items;
|
|
}
|
|
|
|
function setAttrs(item, itemAttrs, itemIndex) {
|
|
|
|
var label = item.data('label'),
|
|
itemScope = item.data('$scope'),
|
|
hasValues = false,
|
|
column;
|
|
|
|
if (item.is('[ui-menu-item]')) {
|
|
itemScope = item.isolateScope();
|
|
}
|
|
|
|
// handle o2m/m2m columns
|
|
if (item.is('.slick-dummy-column')) {
|
|
column = item.data('column');
|
|
itemScope = item.parents('[x-path]:first,.portlet-grid').data('$scope');
|
|
forEach(itemAttrs, function(value, attr){
|
|
if (attr == 'hidden')
|
|
itemScope.showColumn(column.id, !value);
|
|
if (attr == 'title')
|
|
setTimeout(function(){
|
|
itemScope.setColumnTitle(column.id, value);
|
|
});
|
|
});
|
|
return;
|
|
}
|
|
|
|
//handle o2m/m2m title
|
|
if(item.is('.one2many-item') || item.is('.many2many-item')){
|
|
forEach(itemAttrs, function(value, attr){
|
|
if (attr == 'title') {
|
|
itemScope.title = value;
|
|
}
|
|
});
|
|
}
|
|
|
|
// handle notebook
|
|
if (item.is('.tab-pane')) {
|
|
forEach(itemAttrs, function(value, attr){
|
|
if (attr == 'hidden') {
|
|
itemScope.attr('hidden', value);
|
|
}
|
|
if (attr == 'title') {
|
|
itemScope.title = value;
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
function isDotted() {
|
|
var name = item.attr('x-field') || '';
|
|
var dotted = name.indexOf('.') > -1;
|
|
if (dotted) {
|
|
itemAttrs.$hasDotted = true;
|
|
}
|
|
return dotted;
|
|
}
|
|
|
|
forEach(itemAttrs, function(value, attr){
|
|
|
|
if ((attr === "value" || attr.indexOf('value:') === 0)) {
|
|
hasValues = true;
|
|
if (itemScope && itemScope.$setForceWatch) {
|
|
itemScope.$setForceWatch(true);
|
|
}
|
|
if (isDotted()) return;
|
|
if (itemAttrs.$hasDotted) {
|
|
itemAttrs.$hasDotted = false;
|
|
} else if (itemIndex > 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(attr) {
|
|
case 'hidden':
|
|
if (itemScope.field && itemScope.field.hideIf === "true") return;
|
|
case 'required':
|
|
case 'readonly':
|
|
case 'collapse':
|
|
case 'precision':
|
|
case 'scale':
|
|
case 'prompt':
|
|
case 'css':
|
|
case 'icon':
|
|
case 'selection-in':
|
|
itemScope.attr(attr, value);
|
|
break;
|
|
case 'title':
|
|
(function () {
|
|
var span = $(label).add(item).children('span[ui-help-popover]:first');
|
|
if (span.length === 0) {
|
|
span = label;
|
|
}
|
|
if (span && span.length > 0) {
|
|
span.html(value);
|
|
} else if (item.is('label')) {
|
|
item.html(value);
|
|
}
|
|
})();
|
|
itemScope.attr('title', value);
|
|
break;
|
|
case 'domain':
|
|
if (itemScope.setDomain)
|
|
itemScope.setDomain(value);
|
|
break;
|
|
case 'refresh':
|
|
itemScope.waitForActions(function () {
|
|
itemScope.$broadcast('on:attrs-change:refresh');
|
|
}, 100);
|
|
break;
|
|
case 'url':
|
|
case 'url:set':
|
|
if (item.is('[ui-portlet]')) {
|
|
item.find('iframe:first').attr('src', value);
|
|
}
|
|
break;
|
|
case 'value':
|
|
case 'value:set':
|
|
if (itemScope.setValue) {
|
|
itemScope.setValue(value);
|
|
}
|
|
break;
|
|
case 'value:add':
|
|
if (itemScope.fetchData && itemScope.select) {
|
|
itemScope.fetchData(value, function(records){
|
|
itemScope.select(records);
|
|
});
|
|
}
|
|
break;
|
|
case 'value:del':
|
|
if (itemScope.removeItems) {
|
|
itemScope.removeItems(value);
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
|
|
if (hasValues && formScope.onChangeNotify) {
|
|
formScope.onChangeNotify(formScope, formScope.record);
|
|
}
|
|
}
|
|
|
|
forEach(data.attrs, function(itemAttrs, itemName) {
|
|
var items = findItems(itemName);
|
|
if (!items || items.length === 0) {
|
|
// dashlet still not loaded ?
|
|
if (itemName.indexOf('.') > -1) {
|
|
var parentName = itemName.substring(0, itemName.indexOf('.'));
|
|
var parentElem = findItems(parentName);
|
|
if (parentElem.is('[ui-dashlet]')) {
|
|
parentElem.scope().$$pendingAttrs = parentElem.scope().$$pendingAttrs || {};
|
|
parentElem.scope().$$pendingAttrs[itemName.substring(itemName.indexOf('.')+1)] = itemAttrs;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
items.each(function(i) {
|
|
setAttrs($(this), itemAttrs, i);
|
|
});
|
|
});
|
|
|
|
if (data.report) {
|
|
return openReport(data);
|
|
}
|
|
|
|
function openReport(data) {
|
|
var record = formScope.record || {};
|
|
if (data.attached) {
|
|
record.$attachments = (record.$attachments || 0) + 1;
|
|
axelor.dialogs.confirm(_t('Report attached to current object. Would you like to download?'),
|
|
function(confirmed) {
|
|
scope.$applyAsync(function() {
|
|
if (confirmed) {
|
|
var url = "ws/rest/com.axelor.meta.db.MetaFile/" + data.attached.id + "/content/download";
|
|
ui.download(url);
|
|
return deferred.resolve();
|
|
}
|
|
deferred.reject();
|
|
});
|
|
}, {
|
|
title: _t('Download'),
|
|
yesNo: false
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
var url = "ws/files/report/" + data.reportLink + "?name=" + data.reportFile;
|
|
var tab = {
|
|
title: data.reportFile,
|
|
resource: url,
|
|
viewType: 'html'
|
|
};
|
|
|
|
if (axelor.device.mobile && data.reportFormat !== "html") {
|
|
ui.download(url, data.reportFile);
|
|
} else if (['pdf', 'html'].indexOf(data.reportFormat) > -1) {
|
|
doOpenView(tab);
|
|
} else {
|
|
ui.download(url);
|
|
}
|
|
|
|
scope.$timeout(deferred.resolve);
|
|
return deferred.promise;
|
|
}
|
|
|
|
function openTab(scope, tab) {
|
|
if (scope.openTab) {
|
|
scope.openTab(tab);
|
|
} else if (scope.$parent) {
|
|
openTab(scope.$parent, tab);
|
|
}
|
|
}
|
|
|
|
function doOpenView(tab) {
|
|
tab.action = _.uniqueId('$act');
|
|
if (!tab.viewType)
|
|
tab.viewType = 'grid';
|
|
if (tab.viewType == 'grid' || tab.viewType == 'form')
|
|
tab.model = tab.model || tab.resource;
|
|
if (!tab.views) {
|
|
tab.views = [{ type: tab.viewType }];
|
|
if (tab.viewType === 'html') {
|
|
angular.extend(tab.views[0], {
|
|
resource: tab.resource,
|
|
title: tab.title
|
|
});
|
|
}
|
|
}
|
|
if (tab.viewType === "html" && (tab.params||{}).download) {
|
|
var view = _.findWhere(tab.views, { type: "html" });
|
|
if (view) {
|
|
var url = view.name || view.resource;
|
|
var fileName = tab.params.fileName || "true";
|
|
ui.download(url, fileName);
|
|
return scope.$applyAsync();
|
|
}
|
|
}
|
|
if (tab.viewType === "html" && (tab.params||{}).target === "_blank") {
|
|
var view = _.findWhere(tab.views, { type: "html" });
|
|
if (view) {
|
|
var url = view.name || view.resource;
|
|
setTimeout(function () {
|
|
window.open(url);
|
|
});
|
|
return scope.$applyAsync();
|
|
}
|
|
}
|
|
if ((tab.params && tab.params.popup) || axelor.device.mobile) {
|
|
tab.$popupParent = formScope;
|
|
}
|
|
openTab(scope, tab);
|
|
scope.$applyAsync();
|
|
}
|
|
|
|
if (data.view) {
|
|
doOpenView(data.view);
|
|
}
|
|
|
|
if (data.close || data.canClose) {
|
|
this._closeView(scope);
|
|
}
|
|
|
|
deferred.resolve();
|
|
|
|
return deferred.promise;
|
|
}
|
|
};
|
|
|
|
ui.factory('ActionService', ['ViewService', function(ViewService) {
|
|
|
|
function handler(scope, element, options) {
|
|
var opts = _.extend({}, options, { element: element });
|
|
return new ActionHandler(scope, ViewService, opts);
|
|
}
|
|
|
|
return {
|
|
handler: handler
|
|
};
|
|
}]);
|
|
|
|
var EVENTS = ['onClick', 'onChange', 'onSelect', 'onTabSelect', 'onNew', 'onLoad', 'onSave'];
|
|
|
|
ui.directive('uiActions', ['ViewService', function(ViewService) {
|
|
|
|
function link(scope, element, attrs) {
|
|
|
|
var props = _.isEmpty(scope.field) ? scope.schema : scope.field;
|
|
if (!props) {
|
|
return;
|
|
}
|
|
|
|
_.each(EVENTS, function(name){
|
|
var action = props[name];
|
|
if (!action) {
|
|
return;
|
|
}
|
|
|
|
var handler = new ActionHandler(scope, ViewService, {
|
|
name: name,
|
|
element: element,
|
|
action: action,
|
|
canSave: props.canSave,
|
|
prompt: props.prompt
|
|
});
|
|
scope.$events[name] = _.bind(handler[name], handler);
|
|
});
|
|
}
|
|
|
|
return {
|
|
link: function(scope, element, attrs) {
|
|
scope.$evalAsync(function() {
|
|
link(scope, element, attrs);
|
|
});
|
|
}
|
|
};
|
|
}]);
|
|
|
|
ui.directive('uiActionClick', ['ViewService', function(ViewService) {
|
|
return {
|
|
link: function(scope, element, attrs) {
|
|
var action = attrs.uiActionClick;
|
|
scope.$evalAsync(function() {
|
|
var handler = new ActionHandler(scope, ViewService, {
|
|
element: element,
|
|
action: action
|
|
});
|
|
element.on("click", function () {
|
|
handler.handle();
|
|
scope.$applyAsync();
|
|
});
|
|
});
|
|
}
|
|
};
|
|
}]);
|
|
|
|
})();
|