Files
ERP/sophal/js/view/view.form.js

1508 lines
39 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');
ui.prepareContext = function(model, values, dummyValues, parentContext) {
var context = _.extend({}, values);
var IGNORE = ['$changed', '$editorModel', '$version', '$fetched', '$fetchedRelated'];
function findDummy(item) {
var dummy = {};
if (item) {
_.each(item, function (v, k) {
if (k[0] === '$' && IGNORE.indexOf(k) === -1) {
dummy[k.substring(1)] = v;
}
});
}
return dummy;
}
function compact(item) {
if (!item || _.isNumber(item)) return item;
if (item.id > 0 && item.version === undefined && !item.$dirty) {
return _.extend({
id: item.id,
selected: item.selected,
$version: item.$version
}, findDummy(item));
}
item = _.extend({}, item);
if (item.id <= 0) {
item.id = null;
}
if (item.version === undefined && item.$version !== undefined) {
item.version = item.$version;
}
return item;
}
var dummy = _.extend({}, dummyValues);
_.each(dummy, function (value, name) {
if (value && value.$updatedValues) {
dummy[name] = value.$updatedValues;
}
if (name.indexOf('$') === 0) {
dummy[name.substring(1)] = dummy[name];
}
});
context = _.extend(context, dummy);
context._model = model || context._model;
context._parent = parentContext || context._parent;
if (context.id <= 0) {
context.id = null;
}
// use selected flag for o2m/m2m fields
// see onSelectionChanged in o2m controller
_.each(context.$many, function (getItems, name) {
if (!getItems) return;
if (name.indexOf('$') === 0) name = name.substring(1);
var items = getItems();
var value = context[name] || [];
if (items && items.length === value.length) {
context[name] = _.map(items, compact);
}
});
// compact o2m/m2m records
_.each(context, function (value, name) {
if (_.isArray(value)) {
context[name] = _.map(value, compact);
}
// make sure to have proper selected flags in nested o2m/m2m values, see uiPanelEditor
if (value && value.$editorModel) {
context[name] = ui.prepareContext(value.$editorModel, value, findDummy(value));
}
});
return context;
};
ui.controller('FormViewCtrl', FormViewCtrl);
ui.FormViewCtrl = FormViewCtrl;
ui.FormViewCtrl.$inject = ['$scope', '$element'];
function FormViewCtrl($scope, $element) {
ui.DSViewCtrl('form', $scope, $element);
var ds = $scope._dataSource;
$scope.fields = {};
$scope.fields_view = {};
$scope.fields_related = {};
$scope.record = {};
$scope.$$original = null;
$scope.$$dirty = false;
$scope.$$dirtyGrids = [];
$scope.$events = {};
/**
* Get field attributes.
*
* @param field field name or field element
*/
$scope.getViewDef = function(field) {
var id = field,
name = field,
elem = $(field),
attrs = {};
if (_.isObject(field)) { // assume element
name = elem.attr('x-field') || elem.attr('data-field') || elem.attr('name');
id = elem.attr('x-for-widget') || elem.attr('id') || name;
}
attrs = _.extend({}, this.fields[name], this.fields_view[id]);
return attrs;
};
$scope.doRead = function(id) {
var params = {
fields : _.pluck($scope.fields, 'name'),
related: $scope.fields_related
};
var promise = ds.read(id, params);
promise.success(function (record) {
record.$fetched = true;
});
return promise;
};
function doEdit(id, dummy, fireOnLoad) {
return $scope.doRead(id).success(function(record){
if ($scope.viewType && $scope.viewType !== 'form') {
$scope.edit(null);
return;
}
if (dummy) {
record = _.extend(dummy, record);
}
$scope.edit(record, fireOnLoad);
});
}
var initialized = false;
var routeId = null;
$scope.onShow = function(viewPromise) {
var params = this._viewParams;
var context = params.context || {};
var recordId = params.recordId || context._showRecord;
if (params.recordId) {
params.recordId = undefined;
}
if (context._showRecord) {
context._showRecord = undefined;
}
$scope.$locationChangeCheck();
$scope.$broadcast("on:form-show");
if (recordId) {
routeId = recordId;
return viewPromise.then(function(){
doEdit(recordId);
});
}
if (!initialized && params.options && params.options.mode === "edit") {
initialized = true;
$scope._routeSearch = params.options.search;
recordId = +params.options.state;
if (recordId > 0) {
routeId = recordId;
return viewPromise.then(function(){
doEdit(recordId);
});
}
}
var page = ds.page(),
record = null;
if (page.index > -1) {
record = ds.at(page.index);
}
routeId = record && record.id > 0 ? record.id : null;
viewPromise.then(function(){
$scope.ajaxStop(function(){
record = ($scope.record || {}).id ? $scope.record : record;
if (record === undefined) {
$scope.edit(null, false);
return $scope.ajaxStop(function(){
if (!$scope.record || !$scope.record.id) {
$scope.$broadcast("on:new");
}
});
}
if (record && record.id)
return doEdit(record.id);
$scope.edit(record);
});
});
};
var editable = false;
$scope.isForceEdit = function () {
var params = this._viewParams || {};
return params.forceEdit || (params.params && params.params.forceEdit);
};
$scope.isForceReadonly = function () {
var params = this._viewParams || {};
return params.forceReadonly || (params.params && params.params.forceReadonly);
};
$scope.isEditable = function() {
return $scope.isForceEdit() || (editable && !$scope.isForceReadonly());
};
$scope.setEditable = function() {
editable = arguments.length === 1 ? _.first(arguments) : true;
};
$scope.$$resetForm = function $$resetForm() {
routeId = null;
$scope.setEditable(false);
$scope.editRecord(null);
};
var locationChangeOff = null;
function locationChangeCheck() {
if (locationChangeOff) {
return;
}
var tab = $scope.selectedTab || {};
var params = $scope._viewParams;
if (tab !== params) {
return;
}
locationChangeOff = $scope.$on("$locationChangeStart", function (event, newUrl, oldUrl) {
// block navigation if popup is open
var hasDialogs = $('body .ui-dialog:visible').length > 0;
if (hasDialogs) {
event.preventDefault();
return;
}
var $location = $scope.$location;
if (!$location || tab !== params || tab.$viewScope != $scope || !$scope.isDirty()) {
$scope.$timeout(function () {
if (params && params.viewType !== 'form') {
$scope.$$resetForm();
}
});
return;
}
var path = $location.path();
var search = $location.search();
// only handle /ds path changes
if (path.indexOf("/ds/") !== 0 || oldUrl.indexOf("#/ds/") === -1) {
return;
}
event.preventDefault();
$scope.confirmDirty(function() {
$scope.$$resetForm();
$scope.$locationChangeOff();
$location.path(path).search(search);
}, function () {
$scope.$locationChangeOff();
locationChangeCheck();
});
});
}
$scope.$locationChangeOff = function () {
if (locationChangeOff) {
locationChangeOff();
locationChangeOff = null;
}
};
$scope.$locationChangeCheck = function () {
$scope._viewPromise.then(function () {
$scope.waitForActions(locationChangeCheck, 200);
});
};
$scope.$on("$destroy", function () {
$scope.$locationChangeOff();
});
$scope.getRouteOptions = function() {
var rec = $scope.record,
args = [];
if (rec && rec.id > 0) {
args.push(rec.id);
} else if (routeId > 0) {
args.push(routeId);
}
return {
mode: 'edit',
args: args,
query: $scope._routeSearch
};
};
$scope._routeSearch = {};
$scope.setRouteOptions = function(options) {
var opts = options || {},
record = $scope.record || {},
state = +opts.state || null;
$scope.$locationChangeCheck();
$scope._routeSearch = opts.search;
if (record.id == state) {
return $scope.updateRoute();
}
if (routeId === state && state) {
return record.id && record.id !== state ? doEdit(state) : $scope.updateRoute();
}
var params = $scope._viewParams;
if (params.viewType !== "form") {
return $scope.show();
}
return state ? doEdit(state) : $scope.edit(null, false);
};
$scope.edit = function(record, fireOnLoad) {
$scope.$$disableDirtyCheck = true;
$scope.editRecord(record);
$scope.updateRoute();
$scope.$applyAsync(function () {
$scope.$$disableDirtyCheck = false;
if (fireOnLoad === false) return;
$scope._viewPromise.then(function(){
$scope.ajaxStop(function(){
var handler = $scope.$events.onLoad,
record = $scope.record;
if (handler && !ds.equals({}, record)) {
setTimeout(handler);
}
});
});
});
};
$scope.$$fixUndefined = function (orig, current) {
_.keys(current).forEach(function (name) {
var value = current[name];
if (value == undefined && orig[name] === null) {
current[name] = null;
}
});
};
$scope.editRecord = function(record) {
$scope.$$original = angular.copy(record) || {};
$scope.$$dirty = false;
$scope.record = angular.copy($scope.$$original);
$scope._viewPromise.then(function(){
$scope.ajaxStop(function(){
$scope.$$fixUndefined($scope.$$original, $scope.record);
$scope.$broadcast("on:edit", $scope.record);
$scope.$broadcast("on:record-change", $scope.record);
if ($scope.__canForceEdit && $scope.canEdit()) {
$scope.__canForceEdit = undefined;
$scope.onEdit();
}
});
});
};
$scope.getContext = function() {
var dummy = $scope.getDummyValues();
var context = _.extend({}, $scope._routeSearch, $scope.record);
if ($scope.$parent && $scope.$parent.getContext) {
context._parent = $scope.$parent.getContext();
} else {
context = _.extend({}, $scope._viewParams.context, context);
}
if (!$scope.$hasPanels) {
context._form = true;
}
$scope.$broadcast('on:update-context', context);
return ui.prepareContext(ds._model, context, dummy);
};
$scope.$dirtyGrid = function (gridId, dirty) {
var i = $scope.$$dirtyGrids.indexOf(gridId);
if (dirty && i === -1) {
$scope.$$dirtyGrids.push(gridId);
} else if (!dirty && i > -1) {
$scope.$$dirtyGrids.splice(i, 1);
}
return $scope.isDirty();
};
$scope.isDirty = function() {
$scope.$$dirty = $scope.$$disableDirtyCheck ? false : $scope.$$dirtyGrids.length > 0 || !ds.equals($scope.record, $scope.$$original);
return $scope.$$dirty;
};
$scope.$watch("record", function formRecordWatch(rec, old) {
if (rec === old) {
$scope.$$dirty = false;
return;
}
$scope.$broadcast("on:record-change", rec);
$scope.$$dirty = $scope.isDirty();
}, true);
$scope.$broadcastRecordChange = function () {
$scope.$broadcast("on:record-change", $scope.record);
};
$scope.$on("on:record-change", function () {
var view = $scope.schema;
if (view && view.readonlyIf) {
var readonly = axelor.$eval($scope, view.readonlyIf, _.extend({}, $scope._context, $scope.record));
if (_.isFunction($scope.attr)) {
$scope.attr('readonly', readonly);
}
editable = !readonly;
}
});
$scope.isValid = function() {
return $scope.form && $scope.form.$valid;
};
$scope.canNew = function() {
return $scope.hasButton('new');
};
$scope.canEdit = function() {
return $scope.hasButton('edit');
};
$scope.canSave = function() {
return $scope.hasPermission('write') && $scope.$$dirty && $scope.isValid();
};
$scope.canDelete = function() {
return $scope.hasButton('delete') && ($scope.record || {}).id > 0;
};
$scope.canArchive = function() {
return $scope.hasPermission('write')
&& $scope.hasButton('archive')
&& ($scope.record || {}).id > 0
&& !$scope.$$dirty
&& !$scope.record.archived
&& $scope.isValid();
};
$scope.canUnarchive = function() {
return $scope.hasPermission('write')
&& $scope.hasButton('archive')
&& ($scope.record || {}).id > 0
&& !$scope.$$dirty
&& $scope.record.archived
&& $scope.isValid();
};
$scope.canCopy = function() {
return $scope.canNew() && $scope.hasButton('copy') && !$scope.$$dirty && ($scope.record || {}).id;
};
$scope.canAttach = function() {
return $scope.hasButton('attach');
};
$scope.canCancel = function() {
return $scope.$$dirty;
};
$scope.canBack = function() {
return !$scope.$$dirty;
};
$scope.onNew = function() {
var defer = $scope._defer();
$scope.confirmDirty(function(){
routeId = null;
$scope.$locationChangeOff();
$scope.edit(null, false);
$scope.setEditable();
$scope.$broadcast("on:new");
$scope.$locationChangeCheck();
defer.resolve();
}, defer.reject);
return defer.promise;
};
$scope.onNewPromise = null;
$scope.defaultValues = null;
$scope.onNewHandler = function onNewHandler(event) {
routeId = null;
function handleOnNew() {
var handler = $scope.$events.onNew;
var last = $scope.$parent.onNewPromise || $scope.onNewPromise;
var params = ($scope._viewParams || {}).params || {};
function reset() {
$scope.onNewPromise = null;
}
function handle(defaults) {
var promise = handler();
if (promise && promise.then) {
promise.then(reset, reset);
promise = promise.then(function () {
if ($scope.record && $scope.record.id > 0) return; // record may have been saved, see RM-13558
if ($scope.isDirty()) {
var rec = _.extend({}, defaults, $scope.record);
var old = $scope.$$original;
var res = $scope.editRecord(rec);
if (rec) {
rec._dirty = true;
}
if (!params['details-view']) {
$scope.$$original = old;
}
return res;
} else if (defaults) {
$scope.editRecord(defaults);
}
});
}
return promise;
}
$scope.setEditable();
if (handler && $scope.record) {
if (last) {
$scope.onNewPromise = last.then(handle);
return;
}
$scope.onNewPromise = handle($scope.defaultValues);
} else if ($scope.defaultValues) {
$scope.editRecord($scope.defaultValues);
}
}
function afterVewLoaded() {
if ($scope.defaultValues === null) {
var defaultValues = {};
_.each($scope.fields, function (field, name) {
if (field.defaultValue !== undefined) {
defaultValues[name] = field.defaultValue;
}
});
$scope.defaultValues = _.isEmpty(defaultValues) ? undefined : defaultValues;
}
// ensure correct date/datetime
_.each($scope.fields, function (field, name) {
if (field.defaultNow && $scope.defaultValues[name] !== undefined) {
$scope.defaultValues[name] = field.type === 'date'
? moment().startOf('day').format('YYYY-MM-DD')
: moment().toISOString();
}
});
return handleOnNew();
}
$scope._viewPromise.then(function() {
$scope.waitForActions(afterVewLoaded);
});
};
$scope.$on("on:new", function (event) {
$scope.onNewHandler(event);
});
$scope.$on("on:nav-click", function(event, tab) {
var record, context, checkVersion;
if (event.defaultPrevented || tab.$viewScope !== $scope) {
return;
}
event.preventDefault();
context = tab.context || {};
record = $scope.record || {};
checkVersion = "" + axelor.config["view.form.check-version"];
if (context.__check_version !== undefined) {
checkVersion = "" + context.__check_version;
}
if (!record.id || !(checkVersion === "true" || checkVersion === "silent")) {
return;
}
return $scope.checkVersion(true, function (verified) {
var params = $scope._viewParams;
if (verified) {
return;
}
if (checkVersion === "silent"
&& (!$scope.isDirty() || (params.params && params.params['show-confirm'] === false))) {
return $scope.onRefresh();
}
axelor.dialogs.confirm(
_t("The record has been updated or delete by another action.") + "<br>" +
_t("Would you like to reload the current record?"),
function(confirmed){
if (confirmed) {
$scope.onRefresh();
}
});
});
});
$scope.checkVersion = function (callback) {
var record = $scope.record || {};
var done = callback;
var graph = callback === true;
if (graph) {
done = arguments[1];
}
done = _.isFunction(done) ? done : angular.noop;
function compact(rec) {
var res = {
id: rec.id,
version: rec.version
};
if (res.version === undefined) {
res.version = rec.$version;
}
if (graph) {
_.each(rec, function(v, k) {
if (!v) return;
if (v.id) res[k] = compact(v);
if (_.isArray(v)) res[k] = _.map(v, compact);
});
}
return res;
}
if (!record.id) {
return done(true);
}
return ds.verify(compact(record))
.success(function(res) {
done(res.status === 0);
}).error(function (err) {
done(false);
});
};
$scope.onEdit = function() {
$scope.setEditable();
};
$scope.onCopy = function() {
var record = $scope.record;
ds.copy(record.id).success(function(record){
$scope.$locationChangeOff();
routeId = null;
$scope.edit(record);
$scope.setEditable();
$scope.$timeout(function () {
record._dirty = true;
$scope.$$original = {};
locationChangeCheck();
});
});
};
$scope.getDummyValues = function() {
if (!$scope.record) return {};
var fields = _.keys($scope.fields);
var extra = _.chain($scope.fields_view)
.filter(function(f) { return f.name && !_.contains(fields, f.name); })
.pluck('name')
.compact()
.value();
if ($scope._model === 'com.axelor.auth.db.User') {
extra = extra.filter(function (n) {
return ['change', 'oldPassword', 'newPassword', 'chkPassword'].indexOf(n) === -1;
});
}
return _.pick($scope.record, extra);
};
$scope.onSave = function(options) {
var opts = _.extend({ fireOnLoad: true }, options);
var defer = $scope._defer();
var saveAction = $scope.$events.onSave;
var fireOnLoad = opts.fireOnLoad;
function fireBeforeSave() {
var event = $scope.$broadcast('on:before-save', $scope.record);
if (event.defaultPrevented) {
if (event.error) {
axelor.dialogs.error(event.error);
}
setTimeout(function() {
defer.reject(event.error);
});
return false;
}
return true;
}
if (opts.callOnSave === false) {
saveAction = null;
fireOnLoad = false;
}
if (fireBeforeSave() === false) {
return defer.promise;
}
function doSave() {
var dummy = $scope.getDummyValues(),
values = _.extend({}, $scope.record, opts.values),
promise;
values = ds.diff(values, $scope.$$original);
promise = ds.save(values).success(function(record, page) {
$scope.$$dirtyGrids.length = 0;
return doEdit(record.id, dummy, fireOnLoad);
});
promise.success(function(record) {
defer.resolve(record);
});
promise.error(function(error) {
defer.reject(error);
});
}
function waitForActions(callback) {
if (opts.wait === false) {
return callback();
}
return $scope.waitForActions(callback, 100);
}
function doOnSave() {
if (!$scope.hasPermission('write') || !$scope.isValid()) {
$scope.showErrorNotice();
defer.reject();
return defer.promise;
}
if (saveAction) {
return saveAction().then(doSave);
}
// repeat on:before-save to ensure if any o2m/m2m is updated gets applied
if (fireBeforeSave()) {
waitForActions(doSave);
}
}
waitForActions(doOnSave);
return defer.promise;
};
$scope.confirmDirty = function(callback, cancelCallback) {
var params = $scope._viewParams || {};
if (!$scope.isDirty() || (params.params && params.params['show-confirm'] === false)) {
return callback();
}
axelor.dialogs.confirm(_t("Current changes will be lost. Do you really want to proceed?"), function(confirmed){
if (!confirmed) {
if (cancelCallback) {
cancelCallback();
}
return;
}
$scope.$applyAsync(callback);
});
};
$scope.onDelete = function() {
var record = $scope.record || {};
if (!record.id || record.id < 0) {
return;
}
axelor.dialogs.confirm(_t("Do you really want to delete the selected record?"),
function(confirmed){
if (!confirmed) {
return;
}
ds.removeAll([record]).success(function(records, page){
if ($scope.switchBack() === false) {
$scope.onNew();
}
});
});
};
$scope.onArchive = function() {
var record = $scope.record || {};
if (!record.id || record.id < 0) {
return;
}
axelor.dialogs.confirm(_t("Do you really want to archive the selected record?"),
function(confirmed) {
if (!confirmed) {
return;
}
var item = _.extend({}, record, { archived: true });
ds.saveAll([item]).success(function() {
$scope.switchBack();
});
});
};
$scope.onUnarchive = function() {
var record = $scope.record || {};
if (!record.id || record.id < 0) {
return;
}
axelor.dialogs.confirm(_t("Do you really want to unarchive the selected record?"),
function(confirmed) {
if (!confirmed) {
return;
}
var item = _.extend({}, record, { archived: false });
ds.saveAll([item]).success(function() {
$scope.reload();
});
});
};
$scope.onBack = function() {
var record = $scope.record || {};
var editable = $scope.isEditable();
if (record.id && editable && $scope.canEdit()) {
$scope.setEditable(false);
return;
}
$scope.switchBack();
};
$scope.onRefresh = function() {
$scope.confirmDirty($scope.reload);
};
$scope.reload = function() {
var record = $scope.record;
if (record && record.id) {
return doEdit(record.id).success(function (rec) {
var shared = ds.get(record.id);
if (shared) {
shared = _.extend(shared, rec);
}
});
}
$scope.edit(null, false);
$scope.$broadcast("on:new");
};
$scope.onCancel = function() {
var e = $scope.$broadcast("cancel:grid-edit");
if (e.defaultPrevented) {
return;
}
$scope.confirmDirty(function() {
$scope.reload();
});
};
var __switchTo = $scope.switchTo;
$scope.switchBack = function () {
var __switchBack = null;
if($scope._viewTypeLast && $scope._viewTypeLast !== "form") {
__switchBack = $scope._viewTypeLast;
}
if (__switchBack === null) {
var views = ($scope._viewParams||{}).views || [];
for (var i = 0 ; i < views.length; i++) {
var view = views[i];
if (view.type !== "form") {
__switchBack = view.type;
break;
}
}
}
if (__switchBack) {
return $scope.switchTo(__switchBack);
}
return false;
};
$scope.switchTo = function(type, callback) {
$scope.waitForActions(function () {
$scope.confirmDirty(function() {
$scope.setEditable(false);
$scope.editRecord(null);
__switchTo(type, function () {
$scope.$locationChangeOff();
if (callback) {
callback();
}
});
});
});
};
$scope.onSearch = function() {
var e = $scope.$broadcast("cancel:grid-edit");
if (e.defaultPrevented) {
return;
}
$scope.switchBack();
};
$scope.pagerText = function() {
var page = ds.page(),
record = $scope.record || {};
if (page && page.from !== undefined) {
if (page.total === 0 || page.index === -1 || !record.id) return null;
return _t("{0} of {1}", (page.from + page.index + 1), page.total);
}
};
$scope.canNext = function() {
var page = ds.page();
return (page.index < page.size - 1) || (page.from + page.index < page.total - 1);
};
$scope.canPrev = function() {
var page = ds.page();
return page.index > 0 || ds.canPrev();
};
$scope.onNext = function() {
$scope.confirmDirty(function() {
ds.nextItem(function(record){
if (record && record.id) doEdit(record.id);
});
});
};
$scope.onPrev = function() {
$scope.confirmDirty(function() {
ds.prevItem(function(record){
if (record && record.id) doEdit(record.id);
});
});
};
function focusFirst() {
$scope._viewPromise.then(function() {
setTimeout(function() {
$element.find('form :input:visible').not('[readonly],[type=checkbox]').first().focus().select();
});
});
}
function showLog() {
ds.read($scope.record.id, {
fields: ['createdBy', 'createdOn', 'updatedBy', 'updatedOn']
}).success(function (record) {
showLogDialog(record);
});
}
function showLogDialog(record) {
function nameOf(user) {
if (!user) {
return "";
}
var name = axelor.config['user.nameField'] || 'name';
return user[name] || "";
}
var info = {};
if (record.createdOn) {
info.createdOn = moment(record.createdOn).format('DD/MM/YYYY HH:mm');
info.createdBy = nameOf(record.createdBy);
}
if (record.updatedOn) {
info.updatedOn = moment(record.updatedOn).format('DD/MM/YYYY HH:mm');
info.updatedBy = nameOf(record.updatedBy);
}
var table = $("<table class='table field-details'>");
var tr;
tr = $("<tr></tr>").appendTo(table);
$("<th></th>").text(_t("Created By:")).appendTo(tr);
$("<td></td>").text(info.createdBy).appendTo(tr);
tr = $("<tr></tr>").appendTo(table);
$("<th></th>").text(_t("Created On:")).appendTo(tr);
$("<td></td>").text(info.createdOn).appendTo(tr);
tr = $("<tr></tr>").appendTo(table);
$("<th></th>").text(_t("Updated By:")).appendTo(tr);
$("<td></td>").text(info.updatedBy).appendTo(tr);
tr = $("<tr></tr>").appendTo(table);
$("<th></th>").text(_t("Updated On:")).appendTo(tr);
$("<td></td>").text(info.updatedOn).appendTo(tr);
var text = $('<div>').append(table).html();
axelor.dialogs.say(text);
}
$scope.toolmenu = [{
isButton: true,
items: [{
title: _t('Refresh'),
click: function(e) {
$scope.onRefresh();
}
}, {
title: _t('Delete'),
active: function () {
return $scope.canDelete();
},
click: function(e) {
$scope.onDelete();
}
}, {
title: _t('Duplicate'),
active: function () {
return $scope.canCopy();
},
click: function(e) {
$scope.onCopy();
}
}, {
visible: function () {
return $scope.canArchive() || $scope.canUnarchive();
},
}, {
title: _t('Archive'),
active: function () {
return $scope.canArchive();
},
visible: function () {
return $scope.canArchive();
},
click: function(e) {
$scope.onArchive();
},
}, {
title: _t('Unarchive'),
active: function () {
return $scope.canUnarchive();
},
visible: function () {
return $scope.canUnarchive();
},
click: function(e) {
$scope.onUnarchive();
},
}, {
}, {
active: function () {
return $scope.hasAuditLog();
},
title: _t('Last modified...'),
click: showLog
}]
}];
$scope.onHotKey = function (e, action) {
if (action === "save") {
if (!$scope.canSave()) {
$scope.showErrorNotice();
} else if ($scope.hasButton('save')) {
$(e.target).blur().focus();
$scope.onSave();
}
}
if (action === "refresh") {
$scope.onRefresh();
}
if (action === "new") {
$scope.onNew();
}
if (action === "edit") {
if ($scope.canEdit()) {
$scope.onEdit();
}
focusFirst();
}
if (action === "select") {
focusFirst();
}
if (action === "prev" && $scope.canPrev()) {
$scope.onPrev();
}
if (action === "next" && $scope.canNext()) {
$scope.onNext();
}
if (action === "search") {
$scope.onBack();
}
$scope.$applyAsync();
return false;
};
$scope.$text = function (name) {
var field = $scope.fields[name] || {},
format = ui.formatters[field.type],
record = $scope.record || {};
if (format) {
return format(field, record[name]);
}
return record[name];
};
$scope.$evalViewerExpr = axelor.$evalViewerExpr;
}
ui.formBuild = function (scope, schema, fields) {
var path = scope.formPath || "";
var hasPanels = false;
function update(e, attrs) {
_.each(attrs, function(v, k) {
if (_.isUndefined(v)) return;
e.attr(k, v);
});
}
function process(items, parent) {
$(items).each(function(){
if (this.type == 'break') {
return $('<br>').appendTo(parent);
}
if (this.type == 'field') {
delete this.type;
}
if (['panel', 'panel-json', 'panel-related'].indexOf(this.type) > -1) {
scope.$hasPanels = hasPanels = true;
}
var widget = this.widget,
widgetAttrs = {},
attrs = {};
_.extend(attrs, this.widgetAttrs);
_.each(this.widgetAttrs, function(value, key) {
widgetAttrs['x-' + key] = value;
});
var item = $('<div></div>').appendTo(parent),
field = fields[this.name] || {},
widgetId = _.uniqueId('_formWidget'),
type = widget;
attrs = angular.extend(attrs, field, this);
widget = widget || attrs.widget;
type = ui.getWidget(widget) ||
ui.getWidget(attrs.type) ||
ui.getWidget(attrs.serverType) ||
attrs.type || attrs.serverType || 'string';
if (_.isArray(attrs.selectionList) && !widget) {
type = attrs.multiple ? 'multi-select' : 'select';
}
if (attrs.password) {
type = 'password';
}
if (attrs.image) {
type = "image";
}
if (type == 'label') {
type = 'static-label';
}
if (attrs.type == 'panel-related') {
type = 'panel-' + (field.type || attrs.serverType || type);
if (attrs.items && attrs.items.length) {
attrs.views = [{
type: 'grid',
title: attrs.title || field.title || field.autoTitle,
items: attrs.items,
fields: attrs.fields,
canMove: attrs.canMove,
orderBy: attrs.orderBy,
editable: attrs.editable,
editIcon: attrs.editIcon === undefined ? true : attrs.editIcon
}];
}
this.items = attrs.items = null;
}
if ((attrs.editor || attrs.viewer) && attrs.target && type !== 'image') {
type = 'inline-' + type;
}
attrs.serverType = field.serverType || attrs.serverType || attrs.type;
attrs.type = type;
item.attr('ui-' + type, '');
item.attr('id', widgetId);
if (parent.is('[ui-panel-tabs]')) {
item.attr('ui-panel-tab', '');
if (attrs.showTitle === undefined) {
attrs.showTitle = false;
}
if (attrs.showFrame === undefined) {
attrs.showFrame = false;
}
}
scope.fields_view[widgetId] = attrs;
//TODO: cover all attributes
var _attrs = _.extend({}, attrs.attrs, this.attrs, widgetAttrs, {
'name' : attrs.name || this.name,
'x-cols' : this.cols,
'x-colspan' : this.colSpan || (type === 'help' ? 12 : undefined),
'x-coloffset' : this.colOffset,
'x-rowspan' : this.rowSpan,
'x-sidebar' : this.sidebar,
'x-stacked' : this.stacked,
'x-flexbox' : this.flexbox,
'x-widths' : this.colWidths,
'x-field' : this.name,
'x-title' : attrs.title
});
if (attrs.showTitle !== undefined) {
attrs.showTitle = attrs.showTitle !== false;
_attrs['x-show-title'] = attrs.showTitle;
}
if (attrs.required)
_attrs['ng-required'] = true;
if (attrs.readonly)
_attrs['x-readonly'] = true;
if (_attrs.name) {
_attrs['x-path'] = path ? path + "." + _attrs.name : _attrs.name;
}
update(item, _attrs);
// enable actions & conditional expressions
item.attr('ui-actions', '');
item.attr('ui-widget-states', '');
if (type == 'button' || type == 'static-label') {
item.html(this.title);
}
if (/button|group|tabs|tab|separator|spacer|static|static-label/.test(type)) {
item.attr('x-show-title', false);
}
if (attrs.translatable && (attrs.serverType === 'string' || attrs.serverType === 'text')) {
item.attr('ui-translate-icon', '');
}
var items = this.items || this.pages;
if (items && this.type != 'panel-related') {
process(items, item);
if (type === 'panel') {
item.attr('ui-panel-layout', '');
item.attr('x-item-span', attrs.itemSpan);
} else if (['tabs', 'panel-tabs', 'panel-stack', 'panel-related', 'panel-mail', 'button-group'].indexOf(type) == -1) {
item.attr('ui-table-layout', '');
}
}
if (type === 'group' && _.all(items, function (x){ return x.type === 'button'; })) {
item.addClass('button-group');
}
});
return parent;
}
var elem = $('<form name="form" ui-form-gate ui-form ui-table-layout ui-actions ui-widget-states></form>');
elem.attr('x-cols', schema.cols)
.attr('x-widths', schema.colWidths);
if (schema.css) {
elem.addClass(schema.css);
}
process(schema.items, elem);
if (hasPanels) {
elem.removeAttr('ui-table-layout').attr('ui-bar-layout', '');
}
return elem;
};
ui.directive('uiViewForm', ['$compile', 'ViewService', function($compile, ViewService){
return function(scope, element, attrs) {
scope.canShowAttachments = function() {
return scope.canAttach() && (scope.record || {}).id;
};
scope.onShowAttachments = function(){
var popup = ViewService.compile('<div ui-dms-popup></div>')(scope.$new(true));
popup.isolateScope().showPopup(scope);
};
scope.hasAuditLog = function() {
return scope.record && scope.record.id > -1;
};
scope.hasWidth = function() {
var view = scope.schema;
return view && view.width;
};
var translatted = null;
scope.showErrorNotice = function () {
var form = scope.form || $(element).data('$formController'),
names;
if (!form || form.$valid) {
return;
}
var elems = element.find('[x-field].ng-invalid:not(fieldset)').filter(function() {
var isInline = $(this).parents('.slickgrid,.nested-not-required').length > 0;
if (isInline) {
return false;
}
var elemScope = $(this).scope();
if (elemScope.isHidden &&
elemScope.isHidden()) {
return false;
}
return true;
});
var items = elems.map(function () {
return {
name: $(this).attr('x-field'),
title: $(this).attr('x-title')
};
});
items = _.unique(_.compact(items), function (item) { return item.name; });
if (items.length === 0) {
return;
}
if (!translatted) {
translatted = {};
_.each(scope.fields_view, function (v, k) {
if (v.name) {
translatted[v.name] = v.title;
}
});
}
items = _.map(items, function(item) {
var value = item.title;
if (item.name) {
value = translatted[item.name] || value;
}
return '<li>' + value + '</li>';
});
items = '<ul>' + items.join('') + '</ul>';
axelor.notify.error(items, {
title: _t("The following fields are invalid:")
});
};
element.scroll(function (e) {
$(document).trigger('adjust:scroll', element);
});
scope.$on("on:form-show", function () {
setTimeout(function () {
element.animate({ scrollTop: 0 }, 200);
}, 300);
});
var unwatch = scope.$watch('schema.loaded', function formSchemaWatch(viewLoaded){
if (!viewLoaded) return;
unwatch();
var preparing = true;
scope.$watchChecker(function () {
return !preparing;
});
var params = (scope._viewParams || {}).params || {};
var schema = scope.schema;
var form = ui.formBuild(scope, schema, scope.fields);
form = $compile(form)(scope);
var numFields = form.find('[x-field]').length;
if (!scope._isPopup && !scope._isPanelForm) {
element.addClass('has-width');
}
var width = schema.width || params.width;
if (width && !(/^(large|mid|mini)$/.test(width))) {
if (width === '100%' || width === '*') {
element.removeClass('has-width');
}
form.css({
width: width,
minWidth: schema.minWidth || params.minWidth,
maxWidth: schema.maxWidth || params.maxWidth
});
form.removeClass('large-form mid-form mini-form');
}
scope.$timeout(function () {
element.append(form);
preparing = false;
if (scope._viewResolver) {
scope._viewResolver.resolve(schema, element);
scope.$broadcast("adjust:dialog");
}
}, Math.min(300, numFields * 2));
});
element.on('dblclick', '[x-field].readonly', function (e) {
if (!scope.isEditable()) {
element.prev('.record-toolbar')
.find('button[ng-click="onEdit()"] i')
.effect('pulsate', { times: 3 }, 600);
}
});
};
}]);
})();