562 lines
14 KiB
JavaScript
562 lines
14 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.RefFieldCtrl = RefFieldCtrl;
|
|
|
|
function RefFieldCtrl($scope, $element, DataSource, ViewService, initCallback) {
|
|
|
|
var field = $scope.getViewDef($element),
|
|
params = {
|
|
model: field.target || $element.attr('x-target'),
|
|
views: field.views || {},
|
|
domain: field.domain,
|
|
context: field.context
|
|
},
|
|
views = {};
|
|
|
|
if (field.jsonTarget) {
|
|
params.context = _.extend({}, params.context, { jsonModel: field.jsonTarget });
|
|
}
|
|
|
|
if (!$element.is('fieldset')) {
|
|
|
|
_.each(field.views, function(view){
|
|
views[view.type] = view;
|
|
});
|
|
|
|
var formView = null,
|
|
gridView = null,
|
|
summaryView = null;
|
|
|
|
if (field.summaryView === "" || field.summaryView === "true") {
|
|
summaryView = views.form;
|
|
}
|
|
|
|
if (field.gridView) {
|
|
gridView = {
|
|
type: 'grid',
|
|
name: field.gridView
|
|
};
|
|
}
|
|
if (field.formView) {
|
|
formView = {
|
|
type: 'form',
|
|
name: field.formView
|
|
};
|
|
}
|
|
|
|
if (field.summaryView === "" || field.summaryView === "true") {
|
|
summaryView = views.form || formView || { type: 'form' };
|
|
} else if (field.summaryView) {
|
|
summaryView = {
|
|
type: "form",
|
|
name: field.summaryView
|
|
};
|
|
}
|
|
|
|
views.form = formView || views.form;
|
|
views.grid = gridView || views.grid;
|
|
params.summaryView = angular.copy(summaryView);
|
|
params.summaryViewDefault = params.summaryView || views.form;
|
|
|
|
params.views = _.compact([views.grid, views.form]);
|
|
$scope._viewParams = params;
|
|
}
|
|
|
|
ui.ViewCtrl($scope, DataSource, ViewService);
|
|
|
|
$scope.ngModel = null;
|
|
$scope.editorCanSave = true;
|
|
$scope.editorCanReload = field.canReload;
|
|
|
|
if (initCallback) {
|
|
initCallback.call(this);
|
|
}
|
|
|
|
var editor = null;
|
|
var selector = null;
|
|
var embedded = null;
|
|
|
|
$scope.createNestedEditor = function() {
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Show/Hide the nested editor according to the show parameter, if
|
|
* undefined then toggle.
|
|
*
|
|
*/
|
|
$scope.showNestedEditor = function showNestedEditor(show) {
|
|
if (!params.summaryView) {
|
|
return;
|
|
}
|
|
if (embedded === null) {
|
|
embedded = $scope.createNestedEditor();
|
|
}
|
|
var es = embedded.data('$scope');
|
|
if (es !== null) {
|
|
es.visible = (show === undefined ? !es.visible : show);
|
|
embedded.toggle(es.visible);
|
|
}
|
|
return embedded;
|
|
};
|
|
|
|
$scope.showPopupEditor = function(record) {
|
|
if (!record && this.isReadonly()) {
|
|
return;
|
|
}
|
|
if (editor == null) {
|
|
editor = ViewService.compile('<div ui-editor-popup></div>')($scope);
|
|
editor.data('$target', $element);
|
|
}
|
|
|
|
var popup = editor.isolateScope();
|
|
popup.show(record);
|
|
popup._afterPopupShow = function() {
|
|
if (record == null) {
|
|
popup.$broadcast("on:new");
|
|
}
|
|
};
|
|
};
|
|
|
|
function _showEditor(record) {
|
|
|
|
if (!$scope._isPopup && field.editWindow === "blank" && record && record.id > 0) {
|
|
var checkVersion = "" + axelor.config["view.form.check-version"];
|
|
var context = ($scope.selectedTab || {}).context || {};
|
|
if (context.__check_version !== undefined) {
|
|
checkVersion = "" + context.__check_version;
|
|
}
|
|
var tab = {
|
|
action: _.uniqueId('$act'),
|
|
title: field.title,
|
|
model: field.target,
|
|
recordId: record.id,
|
|
views: [{
|
|
type: 'form',
|
|
name: field.formView
|
|
}, {
|
|
type: 'grid',
|
|
name: field.gridView
|
|
}]
|
|
};
|
|
if (checkVersion) {
|
|
tab.context = { __check_version: checkVersion };
|
|
}
|
|
|
|
return $scope.$root.openTab(tab);
|
|
}
|
|
|
|
if ($scope.editorCanReload && record && record.id) {
|
|
var parent = $scope.$parent;
|
|
if (parent && parent.canSave()) {
|
|
var opts = {
|
|
callOnSave: field.callOnSave
|
|
};
|
|
return parent.onSave(opts).then(function(){
|
|
$scope.showPopupEditor(record);
|
|
});
|
|
}
|
|
}
|
|
return $scope.showPopupEditor(record);
|
|
}
|
|
|
|
$scope.showEditor = function(record) {
|
|
var perm = record ? "read" : "create";
|
|
var id = (record||{}).id;
|
|
|
|
if (perm === 'read' && (!id || id < 0)) {
|
|
return _showEditor(record);
|
|
}
|
|
return $scope.isPermitted(perm, record, function(){
|
|
_showEditor(record);
|
|
});
|
|
};
|
|
|
|
$scope.parentReload = function() {
|
|
var parent = $scope.$parent;
|
|
if (parent) {
|
|
parent.reload();
|
|
}
|
|
};
|
|
|
|
$scope.showSelector = function() {
|
|
if (this.isReadonly()) {
|
|
return;
|
|
}
|
|
function doShow() {
|
|
if (selector == null) {
|
|
selector = $('<div ui-selector-popup></div>').attr('x-select-mode', $scope.selectMode || "multi");
|
|
selector = ViewService.compile(selector)($scope);
|
|
selector.data('$target', $element);
|
|
}
|
|
var popup = selector.isolateScope();
|
|
popup._domain = $scope._domain; // make sure that popup uses my domain (#1233)
|
|
popup.show();
|
|
}
|
|
|
|
var onSelect = this.$events.onSelect;
|
|
if (onSelect) {
|
|
onSelect().then(function(){
|
|
doShow();
|
|
});
|
|
} else {
|
|
doShow();
|
|
}
|
|
};
|
|
|
|
$scope.$on("on:edit", function(record){
|
|
var domain = ($scope.field||field).domain;
|
|
var context = ($scope.field||field).context;
|
|
if (domain !== undefined) $scope._domain = domain;
|
|
if (context !== undefined) $scope._context = context;
|
|
});
|
|
|
|
$scope.setDomain = function(domain, context) {
|
|
if (domain !== undefined) $scope._domain = domain;
|
|
if (context !== undefined) $scope._context = context;
|
|
};
|
|
|
|
$scope.getDomain = function() {
|
|
return {
|
|
_domain: $scope._domain,
|
|
_context: $scope._context
|
|
};
|
|
};
|
|
|
|
var fetchDS = (function () {
|
|
var fds = null;
|
|
return function () {
|
|
if (fds) return fds;
|
|
var ds = $scope._dataSource;
|
|
return fds = DataSource.create(ds._model, {
|
|
domain: ds._domain,
|
|
context: ds._context
|
|
});
|
|
};
|
|
})();
|
|
|
|
$scope.fetchData = function(value, success) {
|
|
|
|
var records = $.makeArray(value),
|
|
ids = [];
|
|
|
|
_.each(records, function(item) {
|
|
if (_.isNumber(item)) {
|
|
return ids.push(item);
|
|
}
|
|
if (_.isNumber(item.id) && item.id > 0 &&
|
|
_.isUndefined(item.version) &&
|
|
_.isUndefined(item.$fetched)) {
|
|
return ids.push(item.id);
|
|
}
|
|
});
|
|
|
|
if (ids.length === 0) {
|
|
return success(value);
|
|
}
|
|
|
|
var fields = $scope.selectFields();
|
|
|
|
function doFetch(view) {
|
|
var domain = "self.id in (:_field_ids)";
|
|
var context = _.pick($scope.getContext(), ['id', '_model']);
|
|
|
|
var sortBy = view.sortBy || view.orderBy;
|
|
if (sortBy) {
|
|
sortBy = sortBy.split(",");
|
|
}
|
|
if (view.canMove && fields.indexOf('sequence') === -1) {
|
|
fields.push('sequence');
|
|
}
|
|
|
|
context._field = field.name;
|
|
context._field_ids = ids;
|
|
|
|
return fetchDS().search({
|
|
fields: fields,
|
|
sortBy: fetchDS()._sortBy || sortBy,
|
|
archived: true,
|
|
limit: -1,
|
|
domain: domain,
|
|
context: context
|
|
}).success(function(records, page){
|
|
// only edited records should have version property
|
|
var items = _.map(records, function(item){
|
|
item.$version = item.version;
|
|
item.$fetched = false;
|
|
delete item.version;
|
|
return item;
|
|
});
|
|
success(items, page);
|
|
});
|
|
}
|
|
|
|
if ($scope.isHidden()) {
|
|
return doFetch($scope.view || {});
|
|
}
|
|
|
|
return $scope._viewPromise.then(function(view) {
|
|
return doFetch(view || {});
|
|
});
|
|
};
|
|
|
|
$scope.fetchSelection = function(request, response) {
|
|
var fn = fetchSelection.bind(this);
|
|
var onSelect = this.$events.onSelect;
|
|
if (onSelect) {
|
|
return onSelect(true).then(function() {
|
|
return fn(request, response);
|
|
});
|
|
}
|
|
return fn(request, response);
|
|
};
|
|
|
|
function fetchSelection(request, response) {
|
|
|
|
/* jshint validthis: true */
|
|
|
|
var field = this.field;
|
|
var nameField = field.targetName || 'id',
|
|
fields = field.targetSearch || [],
|
|
filter = {},
|
|
limit = field.limit || (axelor.device.small ? 6 : 10),
|
|
sortBy = field.orderBy;
|
|
|
|
fields = ["id", nameField].concat(fields);
|
|
fields = _.chain(fields).compact().unique().value();
|
|
|
|
_.each(fields, function(name){
|
|
if (name !== "id" && request.term) {
|
|
filter[name] = request.term;
|
|
}
|
|
});
|
|
|
|
var domain = this._domain,
|
|
context = this._context;
|
|
|
|
if (domain !== undefined && this.getContext) {
|
|
context = _.extend({}, context, this.getContext());
|
|
}
|
|
|
|
if (sortBy) {
|
|
sortBy = sortBy.split(",");
|
|
}
|
|
|
|
var params = {
|
|
filter: filter,
|
|
fields: fields,
|
|
sortBy: sortBy,
|
|
limit: limit
|
|
};
|
|
|
|
if (domain !== undefined) {
|
|
params.domain = domain;
|
|
params.context = context;
|
|
}
|
|
|
|
fetchDS().search(params).success(function(records, page){
|
|
var trKey = '$t:' + nameField;
|
|
var items = _.map(records, function(record) {
|
|
return {
|
|
label: record[trKey] || record[nameField],
|
|
value: record
|
|
};
|
|
});
|
|
response(items, page);
|
|
});
|
|
}
|
|
|
|
$scope.createOnTheFly = function (term, popup, onSaveCallback) {
|
|
|
|
var field = $scope.field;
|
|
var targetFields = null;
|
|
var requiredFields = (field.create||"").split(/,\s*/);
|
|
|
|
function createItem(fields, term, popup) {
|
|
var ds = $scope._dataSource,
|
|
data = { $forceDirty: true }, missing = false;
|
|
|
|
_.each(fields, function(field) {
|
|
if (field.name === "name") return data["name"] = term;
|
|
if (field.name === "code") return data["code"] = term;
|
|
if (field.nameColumn) return data[field.name] = term;
|
|
if (requiredFields.indexOf(field.name) > -1) {
|
|
return data[field.name] = term;
|
|
}
|
|
if (field.required) {
|
|
missing = true;
|
|
}
|
|
});
|
|
if (popup || missing || _.isEmpty(data)) {
|
|
return $scope.showPopupEditor(data);
|
|
}
|
|
return ds.save(data).success(onSaveCallback);
|
|
}
|
|
|
|
|
|
if (targetFields) {
|
|
return createItem(targetFields, term, popup);
|
|
}
|
|
|
|
return $scope.loadView("form").success(function(fields, view){
|
|
targetFields = fields;
|
|
return createItem(fields, term, popup);
|
|
});
|
|
};
|
|
|
|
$scope.attachTagEditor = function attachTagEditor(scope, element, attrs) {
|
|
|
|
var field = scope.field;
|
|
var input = null;
|
|
|
|
if (!field.target) {
|
|
return;
|
|
}
|
|
|
|
function onTagEdit(e, item) {
|
|
|
|
var elem = $(e.target);
|
|
var field = scope.field;
|
|
var value = item[field.targetName];
|
|
|
|
function onKeyDown(e) {
|
|
|
|
// enter key
|
|
if (e.keyCode === 13) {
|
|
item[field.targetName] = input.val();
|
|
saveAndSelect(item);
|
|
hideEditor();
|
|
}
|
|
|
|
// escape
|
|
if (e.keyCode === 27) {
|
|
hideEditor();
|
|
}
|
|
}
|
|
|
|
function hideEditor(forceSave) {
|
|
|
|
$(document).off('mousedown.tag-editor');
|
|
$(input).off('keydown.tag-editor').hide();
|
|
|
|
if (forceSave && value !== input.val()) {
|
|
item[field.targetName] = input.val();
|
|
saveAndSelect(item);
|
|
}
|
|
}
|
|
|
|
if (input === null) {
|
|
input = $('<input class="tag-editor" type="text">').appendTo(element);
|
|
}
|
|
|
|
input.val(value)
|
|
.width(element.width() - 6)
|
|
.show().focus()
|
|
.position({
|
|
my: 'left top',
|
|
at: 'left+3 top+3',
|
|
of: element
|
|
});
|
|
|
|
$(input).on('keydown.tag-editor', onKeyDown);
|
|
$(document).on('mousedown.tag-editor', function (e) {
|
|
if (!input.is(e.target)) {
|
|
hideEditor(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
function saveAndSelect(record) {
|
|
var ds = scope._dataSource;
|
|
var data = _.extend({}, record, {
|
|
version: record.version || record.$version
|
|
});
|
|
ds.save(data).success(function (rec) {
|
|
scope.select(rec);
|
|
});
|
|
}
|
|
|
|
scope.onTagEdit = onTagEdit;
|
|
};
|
|
|
|
$scope.canSelect = function() {
|
|
var canSelect = $scope.attr('canSelect');
|
|
if (canSelect !== undefined) return canSelect;
|
|
if ($scope.selectEnable !== undefined) return $scope.selectEnable;
|
|
return true;
|
|
};
|
|
|
|
$scope.canNew = function() {
|
|
return $scope.attr('canNew') !== false;
|
|
};
|
|
|
|
$scope.canEdit = function() {
|
|
return !$scope.isReadonly();
|
|
};
|
|
|
|
$scope.canView = function() {
|
|
return $scope.attr('canView') !== false;
|
|
};
|
|
|
|
$scope.canRemove = function() {
|
|
return $scope.attr('canRemove') !== false;
|
|
};
|
|
|
|
$scope.select = function(value) {
|
|
|
|
};
|
|
|
|
$scope.onNew = function() {
|
|
$scope.showEditor(null);
|
|
};
|
|
|
|
$scope.onEdit = function() {
|
|
|
|
};
|
|
|
|
$scope.onSelect = function() {
|
|
$scope.showSelector();
|
|
};
|
|
|
|
$scope.onRemove = function() {
|
|
|
|
};
|
|
|
|
var hasPermission = $scope.hasPermission;
|
|
$scope.hasPermission = function(perm) {
|
|
if (hasPermission && !hasPermission.apply($scope, arguments)) {
|
|
return false;
|
|
}
|
|
if (!field.perms) return true;
|
|
var perms = field.perms;
|
|
var permitted = perms[perm];
|
|
if (!permitted) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
}
|
|
|
|
})();
|