Files
ERP/sophal/js/axelor.ds.js

852 lines
24 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 ds = angular.module('axelor.ds', ['ngResource']);
var forEach = angular.forEach,
extend = angular.extend,
isArray = angular.isArray;
ds.factory('MenuService', ['$http', function($http) {
function get(parent) {
return $http.get('ws/action/menu', {
cache: true,
params : {
parent : parent
}
});
}
function all() {
return $http.get('ws/action/menu/all', {
cache: true
});
}
function tags() {
return $http.get('ws/action/menu/tags', {
silent: true,
transformRequest: []
});
}
function action(name, options) {
return $http.post('ws/action/' + name, {
model : 'com.axelor.meta.db.MetaAction',
data : options
});
}
return {
get: get,
all: all,
tags: tags,
action: action
};
}]);
ds.factory('TagService', ['$q', '$timeout', '$rootScope', 'MenuService', function($q, $timeout, $rootScope, MenuService) {
var POLL_INTERVAL = 10000;
var pollResult = {};
var pollPromise = null;
var pollIdle = null;
var listeners = [];
function cancelPolling() {
if (pollPromise) {
$timeout.cancel(pollPromise);
pollPromise = null;
}
if (pollIdle) {
clearTimeout(pollIdle);
pollIdle = null;
}
}
function startPolling() {
if (pollPromise === null) {
findTags();
}
}
var starting = false;
function findTags() {
if (starting) { return; }
if (pollPromise) {
$timeout.cancel(pollPromise);
}
starting = true;
MenuService.tags().success(function (res) {
var data = _.first(res.data);
var values = data.values;
for (var i = 0; i < listeners.length; i++) {
listeners[i](values);
}
pollPromise = $timeout(findTags, POLL_INTERVAL);
if (pollIdle === null) {
pollIdle = setTimeout(cancelPolling, POLL_INTERVAL * 2);
}
starting = false;
});
}
window.addEventListener("mousemove", startPolling, false);
window.addEventListener("mousedown", startPolling, false);
window.addEventListener("keypress", startPolling, false);
window.addEventListener("DOMMouseScroll", startPolling, false);
window.addEventListener("mousewheel", startPolling, false);
window.addEventListener("touchmove", startPolling, false);
window.addEventListener("MSPointerMove", startPolling, false);
// start polling
startPolling();
return {
find: findTags,
listen: function(listener) {
listeners.push(listener);
return function () {
var i = listeners.indexOf(listener);
if (i >= 0) {
listeners.splice(i, 1);
}
return listener;
};
}
};
}]);
ds.factory('ViewService', ['$http', '$q', '$cacheFactory', '$compile', function($http, $q, $cacheFactory, $compile) {
var ViewService = function() {
};
ViewService.prototype.accept = function(params) {
var views = {};
forEach(params.views, function(view){
var type = view.type || view.viewType;
params.viewType = params.viewType || type;
views[type] = extend({}, view, {
deferred: $q.defer()
});
});
return views;
};
ViewService.prototype.compile = function(template) {
return $compile(template);
};
ViewService.prototype.process = function(meta, view, parent) {
var fields = {};
meta = meta || {};
view = view || {};
if (meta.jsonAttrs && view && view.items) {
if (view.type === 'grid') {
view.items = (function (items) {
var button = _.findWhere(items, { type: 'button' });
var index = items.indexOf(button);
if (index < 0) {
index = items.length;
}
items.splice(index, 0, {
type: 'field',
name: 'attrs',
jsonFields: meta.jsonAttrs
});
return items;
})(view.items);
}
if (view.type === 'form') {
view.items.push({
type: 'panel',
title: _t('Attributes'),
itemSpan: 12,
items: [{
type: 'field',
name: 'attrs',
jsonFields: meta.jsonAttrs
}]
});
}
}
view = processJsonForm(view);
meta.fields = processFields(meta.fields);
(function () {
var helps = meta.helps = meta.helps || {};
var items = [];
if (view.helpOverride && view.helpOverride.length) {
helps = _.groupBy(view.helpOverride || [], 'type');
helps = meta.helps = _.object(_.map(helps, function(items, key) {
return [key, _.reduce(items, function(memo, item) {
memo[item.field] = item;
return memo;
}, {})];
}));
if (helps.tooltip && helps.tooltip.__top__) {
view.help = helps.tooltip.__top__.help;
}
}
var help = helps.tooltip || {};
var placeholder = helps.placeholder || {};
var inline = helps.inline || {};
forEach(view.items, function (item) {
if (help[item.name]) {
item.help = help[item.name].help;
}
if (meta.view && meta.view.type === 'form') {
if (placeholder[item.name]) {
item.placeholder = placeholder[item.name].help;
}
if (inline[item.name] && !inline[item.name].used) {
inline[item.name].used = true;
items.push({
type: 'help',
text: inline[item.name].help,
css: inline[item.name].style,
colSpan: 12
});
}
}
items.push(item);
});
forEach(view.toolbar, function (item) {
if (help[item.name]) {
item.help = help[item.name].help;
}
});
if (items.length) {
view.items = items;
}
})();
forEach(view.items || view.pages, function(item) {
processWidget(item);
processSelection(item);
forEach(fields[item.name], function(value, key){
if (!item.hasOwnProperty(key)) {
item[key] = value;
}
});
["canNew", "canView", "canEdit", "canRemove", "canSelect"].forEach(function (name) {
if (item[name] === "false" || item[name] === "true") {
item[name] = item[name] === "true";
}
});
if (item.items || item.pages) {
ViewService.prototype.process(meta, item, view);
}
if (item.password) {
item.widget = "password";
}
if (item.jsonFields && item.widget !== 'json-raw') {
var editor = {
layout: view.type === 'panel-json' ? 'table' : undefined,
flexbox: true,
items: [],
};
var panel = null;
var panelTab = null;
item.jsonFields.sort(function (x, y) { return x.sequence - y.sequence; });
item.jsonFields.forEach(function (field) {
if (field.widgetAttrs) {
field.widgetAttrs = angular.fromJson(field.widgetAttrs);
processWidget(field);
if (field.widgetAttrs.showTitle !== undefined) {
field.showTitle = field.widgetAttrs.showTitle;
}
if (field.widgetAttrs.multiline) {
field.type = 'text';
}
if (field.widgetAttrs.targetName) {
field.targetName = field.widgetAttrs.targetName;
}
}
if (field.type === 'panel' || field.type === 'separator') {
field.visibleInGrid = false;
}
if (field.type === 'panel') {
panel = _.extend({}, field, { items: [] });
if ((field.widgetAttrs || {}).sidebar && parent) {
panel.sidebar = true;
parent.width = 'large';
}
if ((field.widgetAttrs || {}).tab) {
panelTab = panelTab || {
type: 'panel-tabs',
colSpan: 12,
items: []
};
panelTab.items.push(panel);
} else {
editor.items.push(panel);
}
return;
}
if (field.type !== 'separator') {
field.title = field.title || field.autoTitle;
}
var colSpan = (field.widgetAttrs||{}).colSpan || field.colSpan;
if (field.type == 'one-to-many') {
field.type = 'many-to-many';
field.canSelect = false;
}
if (field.type == 'separator' || (field.type == 'many-to-many' && !field.widget)) {
field.showTitle = false;
field.colSpan = colSpan || 12;
}
if (panel) {
panel.items.push(field);
} else {
editor.items.push(field);
}
});
if (panelTab) {
editor.items.push(panelTab);
}
item.widget = 'json-field';
item.editor = editor;
if (!item.viewer) {
item.editor.viewer = true;
}
}
});
// include json fields in grid
if (view.type === 'grid') {
var items = [];
_.each(view.items, function (item) {
if (item.jsonFields) {
_.each(item.jsonFields, function (field) {
var type = field.type || 'text';
if (type.indexOf('-to-many') === -1 && field.visibleInGrid) {
items.push(_.extend({}, field, { name: item.name + '.' + field.name }));
}
});
} else {
items.push(item);
}
});
items = items.sort(function (x, y) { return x.columnSequence - y.columnSequence; });
view.items = items;
}
};
function processJsonForm(view) {
if (view.type !== 'form') return view;
if (view.model !== 'com.axelor.meta.db.MetaJsonRecord') return view;
var panel = _.first(view.items) || {};
var jsonField = _.first(panel.items) || {};
var jsonFields = jsonField.jsonFields || [];
var first = _.first(jsonFields) || {};
if (first.type === 'panel') {
panel.type = 'panel-json';
if (first.widgetAttrs) {
var attrs = angular.fromJson(first.widgetAttrs);
view.width = view.width || attrs.width;
}
}
return view;
}
function processFields(fields) {
var result = {};
if (isArray(fields)) {
forEach(fields, function(field){
field.type = _.chain(field.type || 'string').underscored().dasherize().value();
field.title = field.title || field.autoTitle;
result[field.name] = field;
// if nested field then make it readonly
if (field.name.indexOf('.') > -1) {
field.readonly = true;
field.required = false;
}
processSelection(field);
});
} else {
result = fields || {};
}
return result;
}
function processSelection(field) {
_.each(field.selectionList, function (item) {
if (_.isString(item.data)) {
item.data = angular.fromJson(item.data);
}
});
}
function processWidget(field) {
var attrs = {};
_.each(field.widgetAttrs || {}, function (value, name) {
if (value === "true") value = true;
if (value === "false") value = false;
if (value === "null") value = null;
if (/^(-)?\d+$/.test(value)) value = +(value);
attrs[_.str.camelize(name)] = value;
});
if (field.serverType) {
field.serverType = _.chain(field.serverType).underscored().dasherize().value();
}
field.widgetAttrs = attrs;
}
function useIncluded(view) {
function useMenubar(menubar) {
if (!menubar) return;
var my = view.menubar || menubar;
if (my !== menubar && menubar) {
my = my.concat(menubar);
}
view.menubar = my;
}
function useToolbar(toolbar) {
if (!toolbar) return;
var my = view.toolbar || toolbar;
if (my !== toolbar) {
my = my.concat(toolbar);
}
view.toolbar = my;
}
function useItems(view) {
return useIncluded(view);
}
var items = [];
_.each(view.items, function(item) {
if (item.type === "include") {
if (item.view) {
items = items.concat(useItems(item.view));
useMenubar(item.view.menubar);
useToolbar(item.view.toolbar);
}
} else {
items.push(item);
}
});
return items;
}
function findFields(view, res) {
var result = res || {
fields: [],
related: {}
};
var items = result.fields;
var fields = view.items || view.pages;
if (!fields) return items;
if (view.items && !view._included) {
view._included = true;
fields = view.items = useIncluded(view);
}
function acceptEditor(item) {
var collect = items;
var editor = item.editor;
if (item.target) {
collect = result.related[item.name] || (result.related[item.name] = []);
}
if (editor.fields) {
editor.fields = processFields(editor.fields);
}
var acceptItems = function (items) {
_.each(items, function (child) {
if (child.name && collect.indexOf(child.name) === -1 && child.type === 'field') {
collect.push(child.name);
} else if (child.type === 'panel') {
acceptItems(child.items);
}
if (/RefSelect|ref-select/.test(child.widget)) {
collect.push(child.related);
}
if (child.depends) {
child.depends.split(/\s*,\s*/).forEach(function (name) {
collect.push(name);
});
}
});
};
acceptItems(editor.items);
}
function acceptViewer(item) {
var collect = items;
var viewer = item.viewer;
if (item.target) {
collect = result.related[item.name] || (result.related[item.name] = []);
}
_.each(viewer.fields, function (item) {
collect.push(item.name);
});
if (viewer.fields) {
viewer.fields = processFields(viewer.fields);
}
}
_.each(fields, function(item) {
if (item.editor) acceptEditor(item);
if (item.viewer) acceptViewer(item);
if (item.type === 'panel-related') {
items.push(item.name);
} else if (item.items || item.pages) {
findFields(item, result);
} else if (item.type === 'field') {
items.push(item.name);
}
});
if (view.type === "calendar") {
items.push(view.eventStart);
items.push(view.eventStop);
items.push(view.colorBy);
}
if (view.type === "kanban") {
items.push(view.columnBy);
items.push(view.sequenceBy);
}
if (view.type === "gantt") {
items.push(view.taskUser);
}
return result;
}
var viewCache = $cacheFactory("viewCache", { capacity: 1000 });
function createStore(prefix) {
var toKey = function (name) {
return prefix + ':' + axelor.config['user.id'] + ':' + name;
};
return {
get: function (name) {
return new $q(function (resolve) {
resolve(angular.copy(viewCache.get(toKey(name))));
});
},
set: function (name, value) {
if (value) {
return new $q(function (resolve) {
var val = viewCache.put(toKey(name), angular.copy(value));
resolve(val);
});
}
return $q.resolve(value);
}
};
}
var PENDING_REQUESTS = {};
var FIELDS = createStore('f');
var VIEWS = createStore('v');
var PERMS = createStore('p');
ViewService.prototype.getMetaDef = function(model, view, context) {
var self = this;
var deferred = $q.defer();
var promise = deferred.promise;
promise.success = function(fn) {
promise.then(function(res) {
fn(res.fields, res.view);
});
return promise;
};
function process(data) {
data.view.perms = data.view.perms || data.perms;
self.process(data, data.view);
if (data.perms && data.perms.write === false) {
data.view.editable = false;
}
return data;
}
function updateFields(fetched) {
return FIELDS.get(model).then(function (current) {
current = current || {};
if (current !== fetched.fields) {
_.extend(current, _.object(_.pluck(fetched.fields, 'name'), fetched.fields));
}
return $q.all([FIELDS.set(model, current), PERMS.set(model, fetched.perms)]);
});
}
function fetchFields(data) {
var fields_data = findFields(data.view);
var fields = _.unique(_.compact(fields_data.fields.sort()));
data.related = fields_data.related;
if (_.isArray(data.fields) && data.fields.length > 0) {
updateFields(data).then(function () {
deferred.resolve(process(data));
});
return promise;
}
if (!model || _.isEmpty(fields)) {
deferred.resolve(data);
return promise;
}
function resolve(fetched) {
return updateFields(fetched).then(function (res) {
var current = res[0];
var perms = res[1];
var result = _.extend({}, fetched, {
fields: _.map(fields, function(n) { return current[n]; }),
perms: perms
});
result.view = data.view || view;
result.fields = _.compact(result.fields);
result.related = data.related;
result = process(result);
deferred.resolve(result);
return promise;
});
}
$q.all([FIELDS.get(model), PERMS.get(model)]).then(function (res) {
var fetchedFields = res[0] || {};
var pendingFields = _.filter(fields, function (n) { return !fetchedFields.hasOwnProperty(n); });
if (pendingFields.length == 0) {
resolve({
fields: _.values(fetchedFields),
perms: res[1]
});
return promise;
}
var key = _.flatten([model, pendingFields]).join();
var pending = PENDING_REQUESTS[key];
function clear() {
delete PENDING_REQUESTS[key];
}
if (pending) {
pending.then(clear, clear);
pending.then(function (response) {
resolve((response.data || {}).data);
});
return promise;
}
pending = $http.post('ws/meta/view/fields', { model: model, fields: pendingFields }).then(function (response) {
resolve((response.data || {}).data);
});
pending.then(clear, clear);
PENDING_REQUESTS[key] = pending;
});
return promise;
}
function fetchView() {
var key = [model, view.type, view.name].join(':');
function resolve(response) {
var result = (response.data || {}).data;
if (!result || !result.view) {
return deferred.reject('view not found', view);
}
if (result.searchForm) {
result.view.searchForm = result.searchForm;
}
if (_.isArray(result.view.items)) {
var fieldsPromise = fetchFields(result, key);
if (!_.isArray(view.items)) {
// only cache fetched views
return fieldsPromise.then(function (res) {
return VIEWS.set(key, _.extend({}, res, { view: result.view }));
});
}
return fieldsPromise;
}
result = {
fields: result.view.items,
view: result.view
};
VIEWS.set(key, result).then(function () {
deferred.resolve(result);
});
}
VIEWS.get(key).then(function (loaded) {
if (loaded) {
return deferred.resolve(loaded);
}
function clear() {
delete PENDING_REQUESTS[key];
}
var pending = PENDING_REQUESTS[key];
if (pending) {
pending.then(clear, clear);
return pending.then(resolve);
}
pending = $http.post('ws/meta/view', {
model: model,
data: {
type: view.type,
name: view.name,
context: context
}
});
pending.then(resolve);
pending.then(clear, clear);
PENDING_REQUESTS[key] = pending;
});
return promise;
}
if (_.isArray(view.items)) {
return fetchFields({ view: view, fields: view.fields });
}
return fetchView();
};
ViewService.prototype.defer = function() {
return $q.defer();
};
ViewService.prototype.action = function(action, model, context, data) {
var params = {
model: model,
action: action,
data: data || {
context: _.extend({ _model: model }, context)
}
};
var promise = $http.post('ws/action', params);
promise.success = function(fn) {
promise.then(function(response){
fn(response.data);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, fn);
return promise;
};
return promise;
};
ViewService.prototype.getFields = function(model, jsonModel) {
var that = this,
promise = $http.get('ws/meta/fields/' + model, {
cache: true,
params: jsonModel ? {
jsonModel: jsonModel
} : undefined
});
promise.success = function(fn) {
promise.then(function(response) {
var res = response.data,
data = res.data;
that.process(data);
fn(data.fields, data.jsonFields);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, fn);
return promise;
};
return promise;
};
ViewService.prototype.save = function(schema) {
var promise = $http.post("ws/meta/view/save", {
data: schema
});
promise.success = function(fn) {
promise.then(function(response) {
var res = response.data,
data = res.data;
fn(data);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, fn);
return promise;
};
return promise;
};
return new ViewService();
}]);
})();