diff --git a/.liquibase/Data_alias/basic/2019.2.1/alter_SerialLetter.xml b/.liquibase/Data_alias/basic/2019.2.1/alter_SerialLetter.xml
new file mode 100644
index 0000000000000000000000000000000000000000..de3d63640b8f58836f30da4224ea3637ef577529
--- /dev/null
+++ b/.liquibase/Data_alias/basic/2019.2.1/alter_SerialLetter.xml
@@ -0,0 +1,9 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
+    <changeSet author="s.listl" id="f1a65325-8a60-409e-9271-512c14b5e1b7">
+        <addColumn tableName="SERIALLETTER">
+            <column name="STATUS" type="CHAR(36)"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/2019.2.1/changelog.xml b/.liquibase/Data_alias/basic/2019.2.1/changelog.xml
index 760c8f453f66ed38fd346408e1f3473bfd6ddc4f..9320c36971e495d855c24727aa2c7e85d21d5a7b 100644
--- a/.liquibase/Data_alias/basic/2019.2.1/changelog.xml
+++ b/.liquibase/Data_alias/basic/2019.2.1/changelog.xml
@@ -2,4 +2,6 @@
 <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
     <include relativeToChangelogFile="true" file="update_attribute_Ticket_Support_Product.xml"/>
+    
+    <include relativeToChangelogFile="true" file="alter_SerialLetter.xml"/>
 </databaseChangeLog>
diff --git a/entity/BulkMail_entity/BulkMail_entity.aod b/entity/BulkMail_entity/BulkMail_entity.aod
index fab79de45c56b332b8703adb7ab831d879ced62b..3c902dff3f227405316889f9680bf5752e69dbb7 100644
--- a/entity/BulkMail_entity/BulkMail_entity.aod
+++ b/entity/BulkMail_entity/BulkMail_entity.aod
@@ -27,6 +27,7 @@
       <name>NAME</name>
       <title>Name</title>
       <mandatory v="true" />
+      <valueProcess>%aditoprj%/entity/BulkMail_entity/entityfields/name/valueProcess.js</valueProcess>
     </entityField>
     <entityField>
       <name>SUBJECT</name>
@@ -36,6 +37,7 @@
     <entityField>
       <name>DESCRIPTION</name>
       <title>Description</title>
+      <contentType>LONG_TEXT</contentType>
     </entityField>
     <entityField>
       <name>DOCUMENTTEMPLATE_ID</name>
diff --git a/entity/BulkMail_entity/entityfields/name/valueProcess.js b/entity/BulkMail_entity/entityfields/name/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..94a33c3fc3288465e8959ea57f74eb9b983d1787
--- /dev/null
+++ b/entity/BulkMail_entity/entityfields/name/valueProcess.js
@@ -0,0 +1,14 @@
+import("system.result");
+import("Document_lib");
+import("system.neon");
+import("system.vars");
+
+if (vars.get("$this.value") == null && vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+{
+    var upload = vars.get("$field.bindata");
+    if (upload)
+    {
+        var filename = DocumentUtil.getFilenameFromUpload(upload);
+        result.string(filename.split(".")[0]);
+    }
+}
\ No newline at end of file
diff --git a/entity/BulkMail_entity/entityfields/subject/valueProcess.js b/entity/BulkMail_entity/entityfields/subject/valueProcess.js
index 792f903d2c5b5063b787295c49e860af90110a06..fb6c668dc4b4fdf20757a7a5db45de16f8ed8392 100644
--- a/entity/BulkMail_entity/entityfields/subject/valueProcess.js
+++ b/entity/BulkMail_entity/entityfields/subject/valueProcess.js
@@ -8,7 +8,6 @@ import("system.vars");
 if (vars.get("$this.value") == null && vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
 {
     var upload = vars.get("$field.bindata");
-    var template;
     if (upload)
     {
         var binData = DocumentUtil.getBindataFromUpload(upload);
diff --git a/entity/BulkMail_entity/recordcontainers/db/onDBDelete.js b/entity/BulkMail_entity/recordcontainers/db/onDBDelete.js
index 07aa5246ddaa79bf9f78d08a16c9da6dbfb48de5..71625c5c712cab7e76569fb406a17ae5a47fbce8 100644
--- a/entity/BulkMail_entity/recordcontainers/db/onDBDelete.js
+++ b/entity/BulkMail_entity/recordcontainers/db/onDBDelete.js
@@ -3,7 +3,7 @@ import("Sql_lib");
 import("system.db");
 
 db.deleteData("BULKMAILRECIPIENT", SqlCondition.equals("BULKMAILRECIPIENT.BULKMAIL_ID", vars.get("$local.uid"), "1=2"));
-var SYSALIAS = "_____SYSTEMALIAS";
+var SYSALIAS = SqlUtils.getSystemAlias();
 var binaryId = db.cell(SqlCondition.begin(SYSALIAS)
     .andPrepareVars("ASYS_BINARIES.ROW_ID", "$local.uid")
     .buildSql("select ID from ASYS_BINARIES"), SYSALIAS);
diff --git a/entity/BulkMail_entity/recordcontainers/db/onDBInsert.js b/entity/BulkMail_entity/recordcontainers/db/onDBInsert.js
index 7858a68158c8063c37e1149f4c6debec17a97927..124d083b49c0f9e023aaa6f730b170fc427a550f 100644
--- a/entity/BulkMail_entity/recordcontainers/db/onDBInsert.js
+++ b/entity/BulkMail_entity/recordcontainers/db/onDBInsert.js
@@ -1,3 +1,4 @@
+import("Sql_lib");
 import("DocumentTemplate_lib");
 import("system.result");
 import("system.vars");
@@ -20,7 +21,7 @@ if (template.content)
     if (!template.filename)
         template.filename = bulkMailName + ".html";
     db.insertBinary("BULKMAIL", "DOCUMENT", bulkMailId, 
-        "", template.content, template.filename, "", "", "_____SYSTEMALIAS");
+        "", template.content, template.filename, "", "", SqlUtils.getSystemAlias());
 }
 
 var contactIds = JSON.parse(vars.getString("$param.PresetRecipients_param"));
diff --git a/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js b/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js
index 0a211da13f41be1fdb027c0a3437b3b2350b412a..e6ca5e9a03194feedeba892cd5c5359deb0e9894 100644
--- a/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js
+++ b/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js
@@ -20,7 +20,7 @@ if (template.content)
 {
     if (!template.filename)
         template.filename = bulkMailName + ".html";
-    let sysAlias = "_____SYSTEMALIAS";
+    let sysAlias = SqlUtils.getSystemAlias();
     var binaryId = db.cell(SqlCondition.begin(sysAlias)
         .andPrepareVars("ASYS_BINARIES.ROW_ID", "$local.uid")
         .buildSql("select ID from ASYS_BINARIES", "1=2"), sysAlias);
diff --git a/entity/DocumentTemplateLink_entity/entityfields/opendocument/onActionProcess.js b/entity/DocumentTemplateLink_entity/entityfields/opendocument/onActionProcess.js
index 1b78adcb2e7eaa40ed24326ae14f942607602bf2..c943e852369cd24ce58978071cd6917b197414e1 100644
--- a/entity/DocumentTemplateLink_entity/entityfields/opendocument/onActionProcess.js
+++ b/entity/DocumentTemplateLink_entity/entityfields/opendocument/onActionProcess.js
@@ -1,15 +1,15 @@
-import("Sql_lib");
-import("system.db");
-import("system.vars");
-import("system.neon");
-
-var fileInformation = db.array(db.ROW, SqlCondition.begin()
-    .andPrepareVars("ASYS_BINARIES.ROW_ID", "$field.DOCUMENTTEMPLATE_ID_CHILD")
-    .buildSql("select ID, FILENAME from ASYS_BINARIES"), "_____SYSTEMALIAS");
-var data = db.getBinaryContent(fileInformation[0], "_____SYSTEMALIAS");
-neon.download(data, fileInformation[1]);
-
-
-
-
-
+import("Sql_lib");
+import("system.db");
+import("system.vars");
+import("system.neon");
+
+var fileInformation = db.array(db.ROW, SqlCondition.begin()
+    .andPrepareVars("ASYS_BINARIES.ROW_ID", "$field.DOCUMENTTEMPLATE_ID_CHILD")
+    .buildSql("select ID, FILENAME from ASYS_BINARIES"), SqlUtils.getSystemAlias());
+var data = db.getBinaryContent(fileInformation[0], SqlUtils.getSystemAlias());
+neon.download(data, fileInformation[1]);
+
+
+
+
+
diff --git a/entity/DocumentTemplate_entity/entityfields/textext/valueProcess.js b/entity/DocumentTemplate_entity/entityfields/textext/valueProcess.js
index 7c52687169d59f207a63f94857ffd60137a12fba..4723c73981af398bf75eb3cb0f4b5a440bc2ef03 100644
--- a/entity/DocumentTemplate_entity/entityfields/textext/valueProcess.js
+++ b/entity/DocumentTemplate_entity/entityfields/textext/valueProcess.js
@@ -1,20 +1,20 @@
-import("Sql_lib");
-import("Document_lib");
-import("system.util");
-import("system.db");
-import("system.neon");
-import("system.result");
-import("system.vars");
-
-if(vars.get("$field.KIND").trim() == "TEX" && vars.get("$field.BINDATA") == "" && vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_EDIT)
-{
-    var fileExtension = DocumentUtil.getFileExtensionFromUpload(vars.get("$field.NAME"));
-    
-    if(fileExtension == "txt")
-    {
-        var binaryId      = db.cell(SqlCondition.begin()
-            .andPrepareVars("ASYS_BINARIES.ROW_ID", "$field.DOCUMENTTEMPLATEID")
-            .buildSql("select ID from ASYS_BINARIES"), "_____SYSTEMALIAS");
-        result.string(util.decodeBase64String(db.getBinaryContent(binaryId, "_____SYSTEMALIAS")));
-    }
+import("Sql_lib");
+import("Document_lib");
+import("system.util");
+import("system.db");
+import("system.neon");
+import("system.result");
+import("system.vars");
+
+if(vars.get("$field.KIND").trim() == "TEX" && vars.get("$field.BINDATA") == "" && vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_EDIT)
+{
+    var fileExtension = DocumentUtil.getFileExtensionFromUpload(vars.get("$field.NAME"));
+    
+    if(fileExtension == "txt")
+    {
+        var binaryId      = db.cell(SqlCondition.begin()
+            .andPrepareVars("ASYS_BINARIES.ROW_ID", "$field.DOCUMENTTEMPLATEID")
+            .buildSql("select ID from ASYS_BINARIES"), SqlUtils.getSystemAlias());
+        result.string(util.decodeBase64String(db.getBinaryContent(binaryId, SqlUtils.getSystemAlias())));
+    }
 }
\ No newline at end of file
diff --git a/entity/DocumentTemplate_entity/recordcontainers/db/onDBDelete.js b/entity/DocumentTemplate_entity/recordcontainers/db/onDBDelete.js
index 8936ad8b3e3fe2373001a1fab17e4833577efcef..95cf3f9a66491bacbe72d4968928043043d2445c 100644
--- a/entity/DocumentTemplate_entity/recordcontainers/db/onDBDelete.js
+++ b/entity/DocumentTemplate_entity/recordcontainers/db/onDBDelete.js
@@ -4,6 +4,6 @@ import("system.db");
 
 var binaryId      = db.cell(SqlCondition.begin()
             .andPrepareVars("ASYS_BINARIES.ROW_ID", "$field.DOCUMENTTEMPLATEID")
-            .buildSql("select ID from ASYS_BINARIES"), "_____SYSTEMALIAS");
+            .buildSql("select ID from ASYS_BINARIES"), SqlUtils.getSystemAlias());
 
-db.deleteBinary(binaryId, "_____SYSTEMALIAS");
\ No newline at end of file
+db.deleteBinary(binaryId, SqlUtils.getSystemAlias());
\ No newline at end of file
diff --git a/entity/Offer_entity/entityfields/footer/valueProcess.js b/entity/Offer_entity/entityfields/footer/valueProcess.js
index e064cf46a63fdb4be84aff8ec90abb3a711874a7..002d6afd22e0e8bede80b076b0c08bda6d3cb458 100644
--- a/entity/Offer_entity/entityfields/footer/valueProcess.js
+++ b/entity/Offer_entity/entityfields/footer/valueProcess.js
@@ -1,20 +1,21 @@
-import("system.result");
-import("system.vars");
-import("system.neon");
-import("system.db");
-import("system.util");
-
-if(vars.exists("$param.OfferFooter_param") && vars.get("$param.OfferFooter_param")) 
-    result.string(vars.get("$param.OfferFooter_param"));
-
-else if(vars.get("$this.value"))
-    result.string(vars.get("$this.value"));
-
-else if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
-    result.string("Vielen Dank!")
-
-if (vars.get("$field.ChoosenTEXFooter") != "")
-{
-    var binaryId      = db.cell("select ID from ASYS_BINARIES where ROW_ID = '" + vars.get("$field.ChoosenTEXFooter") + "'", "_____SYSTEMALIAS");
-    result.string(util.decodeBase64String(db.getBinaryContent(binaryId, "_____SYSTEMALIAS")));
+import("Sql_lib");
+import("system.result");
+import("system.vars");
+import("system.neon");
+import("system.db");
+import("system.util");
+
+if(vars.exists("$param.OfferFooter_param") && vars.get("$param.OfferFooter_param")) 
+    result.string(vars.get("$param.OfferFooter_param"));
+
+else if(vars.get("$this.value"))
+    result.string(vars.get("$this.value"));
+
+else if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+    result.string("Vielen Dank!")
+
+if (vars.get("$field.ChoosenTEXFooter") != "")
+{
+    var binaryId      = db.cell("select ID from ASYS_BINARIES where ROW_ID = '" + vars.get("$field.ChoosenTEXFooter") + "'", SqlUtils.getSystemAlias());
+    result.string(util.decodeBase64String(db.getBinaryContent(binaryId, SqlUtils.getSystemAlias())));
 }
\ No newline at end of file
diff --git a/entity/PermissionAction_entity/onValidation.js b/entity/PermissionAction_entity/onValidation.js
index e97725af0b6c953e10fdabce940ff4bead540594..47c7fa03520ed8ae10eccbaa3c1002bf4609384e 100644
--- a/entity/PermissionAction_entity/onValidation.js
+++ b/entity/PermissionAction_entity/onValidation.js
@@ -1,10 +1,11 @@
+import("Sql_lib");
 import("system.db");
 import("system.vars");
 import("system.translate");
 import("system.result");
 import("Permission_lib");
 
-var alias = "_____SYSTEMALIAS";
+var alias = SqlUtils.getSystemAlias();
 var actionTitle = vars.get("$field.ACTION");
 var permissionId = vars.get("$param.PermissionId_param");
 var permCondInput = vars.get("$param.PermissionCondition_param");
diff --git a/entity/PermissionAction_entity/recordcontainers/jdito/onInsert.js b/entity/PermissionAction_entity/recordcontainers/jdito/onInsert.js
index 3acce8156ad069e0f495f8793e8e202a86b8c4db..eaa90d0cb8597dc1a3374c82d563eb6ada68ab90 100644
--- a/entity/PermissionAction_entity/recordcontainers/jdito/onInsert.js
+++ b/entity/PermissionAction_entity/recordcontainers/jdito/onInsert.js
@@ -1,10 +1,11 @@
+import("Sql_lib");
 import("system.db");
 import("system.tools");
 import("system.result");
 import("system.vars");
 import("Permission_lib");
 
-var alias = "_____SYSTEMALIAS";
+var alias = SqlUtils.getSystemAlias();
 var permissionId = vars.exists("$param.PermissionId_param") && vars.get("$param.PermissionId_param");
 
 if (permissionId) {
diff --git a/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js b/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js
index 44edb699f0c0827f4ce776f34e4f3875a478b061..aecbbceea0b47cf2a04eab49eb7815db75ea3efa 100644
--- a/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js
@@ -1,146 +1,147 @@
-import("system.util");
-import("system.vars");
-import("system.db");
-import("system.result");
-import("Permission_lib");
-import("system.project");
-
-var selectedPermission = vars.get("$local.idvalues");
-var sqlStr;
-var whereCond = " where";
-var alias = "_____SYSTEMALIAS";
-var entitiesMetaData = project.getDataModels(project.DATAMODEL_KIND_ENTITY);
-var entityStructure;
-var entitiesUsePermFlagSet = [];
-var fieldsUsePermFlagSet = [];
-
-// gets all names of the entites which have the 'usePermission'-flag set (positive list)
-// gets all names of the fields which have the 'usePermission'-flag set (positive list)
-for each (let entityMetaData in entitiesMetaData) {
-    if (entityMetaData[6] == "true") {
-        entitiesUsePermFlagSet.push(entityMetaData[0])
-        entityStructure = project.getEntityStructure(entityMetaData[0]);
-        for (fieldname in entityStructure.fields) {
-            field = entityStructure.fields[fieldname];
-            if (field.usePermissions == true) {
-                fieldsUsePermFlagSet.push(field.name);
-            }
-        }
-    }   
-}
-
-whereCond += " ENTITY_ID in ('" + entitiesUsePermFlagSet.join("','") + "')";
-whereCond += " and (FIELD_ID in ('" + fieldsUsePermFlagSet.join("','") + "') or FIELD_ID is NULL)";
-
-if (vars.exists("$param.RoleTitle_param") && vars.get("$param.RoleTitle_param")) {
-    whereCond += " and ASYS_PERMISSIONSET.ROLE_ID = '" + vars.getString("$param.RoleTitle_param") + "'";
-} else if (vars.exists("$param.EntityTitle_param") && vars.get("$param.EntityTitle_param")) {
-    whereCond += " and ASYS_PERMISSIONSET.ENTITY_ID = '" + vars.getString("$param.EntityTitle_param") + "'";
-}
-
-sqlStr = 
-    "select ASYS_PERMISSION.ASYS_PERMISSIONID, ASYS_PERMISSIONSET.ENTITY_ID, ASYS_PERMISSIONSET.ROLE_ID,"
-    + " ASYS_PERMISSIONSET.FIELD_ID, ASYS_PERMISSION.COND, ASYS_PERMISSIONACTION.ACTION, ASYS_PERMISSIONSET.ACCESSTYPE, ASYS_PERMISSION.CONDTYPE from ASYS_PERMISSIONSET"
-    + " join ASYS_PERMISSION on ASYS_PERMISSION.ASYS_PERMISSIONSET_ID = ASYS_PERMISSIONSET.ASYS_PERMISSIONSETID"
-    + " join ASYS_PERMISSIONACTION on ASYS_PERMISSIONACTION.ASYS_PERMISSION_ID = ASYS_PERMISSION.ASYS_PERMISSIONID"
-    + whereCond
-    + " order by ASYS_PERMISSION.ASYS_PERMISSIONID";
-
-var sqlRes = db.table(sqlStr, alias);
-var permissionTable = PermissionUtil.convertArrToObj(sqlRes);
-
-// group all permissions by permissionid and condition, concat actions
-var groupedPermissionTable = [], concatAction;
-for (let i = 0; i < permissionTable.length - 1; i++) {
-    for (let j = i + 1; j < permissionTable.length; j++) {
-        if (permissionTable[i].permissionid == permissionTable[j].permissionid && permissionTable[i].cond == permissionTable[j].cond) {
-            var currPermId = permissionTable[i].permissionid;
-            var indexCurrPermGrouped = PermissionUtil.indexOfPermId(groupedPermissionTable, currPermId);
-            if (indexCurrPermGrouped > -1) {
-                // permissionset got already grouped before
-                // concat current action with the actions which got already grouped
-                concatAction = groupedPermissionTable[indexCurrPermGrouped].action + "," + permissionTable[j].action;
-                groupedPermissionTable[indexCurrPermGrouped].action = concatAction;
-                break;
-            } else {
-                concatAction = permissionTable[i].action + "," + permissionTable[j].action;
-                groupedPermissionTable.push(permissionTable[i]);
-                groupedPermissionTable[groupedPermissionTable.length-1].action = concatAction;
-                break;
-            }
-        }
-    }
-}
-
-var res = [];
-var permissionTableOrigin = PermissionUtil.convertArrToObj(sqlRes);
-
-// no permission selected, return all permission entrys
-if (selectedPermission == null) {
-    for each (let entry in groupedPermissionTable) {
-        res = prepareResultArray(entry, res);
-    }
-
-    for each (let entry in permissionTableOrigin) {
-        if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
-            res = prepareResultArray(entry, res);
-        }
-    }
-} else { // permission selected, return only the selected permission entry
-    for each (let entry in groupedPermissionTable) {
-        if (selectedPermission == entry.permissionid) {
-            res = prepareResultArray(entry, res);
-            break;
-        }
-    }
-
-    for each (let entry in permissionTableOrigin) {
-        if (selectedPermission == entry.permissionid) {
-            if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
-                res = prepareResultArray(entry, res);
-                break;
-            }
-        }
-    }
-}
-
-result.object(res.sort(sortFunction));
-
-function prepareResultArray(pEntry, pRes) {
-    var rootPermission = "";
-    if (pEntry.accesstype != "E") {
-        if (pEntry.accesstype == "F" && pEntry.cond != "") {
-            rootPermission = PermissionUtil.getRootFieldPermission(pEntry.permissionid);
-            if (rootPermission == "") {
-                rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);
-            }
-        } else 
-            rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);        
-    }
-    pRes.push([pEntry.permissionid, pEntry.entity, pEntry.role, pEntry.field, pEntry.cond, pEntry.action, pEntry.accesstype, pEntry.condtype, rootPermission]);
-    return pRes;
-}
-
-// used to sort result array: Entity -> Records -> Fields
-function sortFunction(a, b) {
-    if (a[6] == b[6] && a[6] != "F" && a[6] != "R")
-        return 0;
-    else if (a[6] == "E")
-        return -1;
-    else if (b[6] == "E")
-        return 1;
-    else if (a[6] == "R" && b[6] == "F")
-        return -1;
-    else if (a[6] == "F" && b[6] == "R")
-        return 1;
-    else if (a[6] == "R" && b[6] == "R" && a[4] == "")
-        return -1;
-    else if (a[6] == "R" && b[6] == "R" && b[4] == "")
-        return 1;
-    else if (a[6] == "F" && b[6] == "F" && a[4] == "")
-        return -1;
-    else if (a[6] == "F" && b[6] == "F" && b[4] == "")
-        return 1;
-    else
-        return 0;
+import("Sql_lib");
+import("system.util");
+import("system.vars");
+import("system.db");
+import("system.result");
+import("Permission_lib");
+import("system.project");
+
+var selectedPermission = vars.get("$local.idvalues");
+var sqlStr;
+var whereCond = " where";
+var alias = SqlUtils.getSystemAlias();
+var entitiesMetaData = project.getDataModels(project.DATAMODEL_KIND_ENTITY);
+var entityStructure;
+var entitiesUsePermFlagSet = [];
+var fieldsUsePermFlagSet = [];
+
+// gets all names of the entites which have the 'usePermission'-flag set (positive list)
+// gets all names of the fields which have the 'usePermission'-flag set (positive list)
+for each (let entityMetaData in entitiesMetaData) {
+    if (entityMetaData[6] == "true") {
+        entitiesUsePermFlagSet.push(entityMetaData[0])
+        entityStructure = project.getEntityStructure(entityMetaData[0]);
+        for (fieldname in entityStructure.fields) {
+            field = entityStructure.fields[fieldname];
+            if (field.usePermissions == true) {
+                fieldsUsePermFlagSet.push(field.name);
+            }
+        }
+    }   
+}
+
+whereCond += " ENTITY_ID in ('" + entitiesUsePermFlagSet.join("','") + "')";
+whereCond += " and (FIELD_ID in ('" + fieldsUsePermFlagSet.join("','") + "') or FIELD_ID is NULL)";
+
+if (vars.exists("$param.RoleTitle_param") && vars.get("$param.RoleTitle_param")) {
+    whereCond += " and ASYS_PERMISSIONSET.ROLE_ID = '" + vars.getString("$param.RoleTitle_param") + "'";
+} else if (vars.exists("$param.EntityTitle_param") && vars.get("$param.EntityTitle_param")) {
+    whereCond += " and ASYS_PERMISSIONSET.ENTITY_ID = '" + vars.getString("$param.EntityTitle_param") + "'";
+}
+
+sqlStr = 
+    "select ASYS_PERMISSION.ASYS_PERMISSIONID, ASYS_PERMISSIONSET.ENTITY_ID, ASYS_PERMISSIONSET.ROLE_ID,"
+    + " ASYS_PERMISSIONSET.FIELD_ID, ASYS_PERMISSION.COND, ASYS_PERMISSIONACTION.ACTION, ASYS_PERMISSIONSET.ACCESSTYPE, ASYS_PERMISSION.CONDTYPE from ASYS_PERMISSIONSET"
+    + " join ASYS_PERMISSION on ASYS_PERMISSION.ASYS_PERMISSIONSET_ID = ASYS_PERMISSIONSET.ASYS_PERMISSIONSETID"
+    + " join ASYS_PERMISSIONACTION on ASYS_PERMISSIONACTION.ASYS_PERMISSION_ID = ASYS_PERMISSION.ASYS_PERMISSIONID"
+    + whereCond
+    + " order by ASYS_PERMISSION.ASYS_PERMISSIONID";
+
+var sqlRes = db.table(sqlStr, alias);
+var permissionTable = PermissionUtil.convertArrToObj(sqlRes);
+
+// group all permissions by permissionid and condition, concat actions
+var groupedPermissionTable = [], concatAction;
+for (let i = 0; i < permissionTable.length - 1; i++) {
+    for (let j = i + 1; j < permissionTable.length; j++) {
+        if (permissionTable[i].permissionid == permissionTable[j].permissionid && permissionTable[i].cond == permissionTable[j].cond) {
+            var currPermId = permissionTable[i].permissionid;
+            var indexCurrPermGrouped = PermissionUtil.indexOfPermId(groupedPermissionTable, currPermId);
+            if (indexCurrPermGrouped > -1) {
+                // permissionset got already grouped before
+                // concat current action with the actions which got already grouped
+                concatAction = groupedPermissionTable[indexCurrPermGrouped].action + "," + permissionTable[j].action;
+                groupedPermissionTable[indexCurrPermGrouped].action = concatAction;
+                break;
+            } else {
+                concatAction = permissionTable[i].action + "," + permissionTable[j].action;
+                groupedPermissionTable.push(permissionTable[i]);
+                groupedPermissionTable[groupedPermissionTable.length-1].action = concatAction;
+                break;
+            }
+        }
+    }
+}
+
+var res = [];
+var permissionTableOrigin = PermissionUtil.convertArrToObj(sqlRes);
+
+// no permission selected, return all permission entrys
+if (selectedPermission == null) {
+    for each (let entry in groupedPermissionTable) {
+        res = prepareResultArray(entry, res);
+    }
+
+    for each (let entry in permissionTableOrigin) {
+        if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
+            res = prepareResultArray(entry, res);
+        }
+    }
+} else { // permission selected, return only the selected permission entry
+    for each (let entry in groupedPermissionTable) {
+        if (selectedPermission == entry.permissionid) {
+            res = prepareResultArray(entry, res);
+            break;
+        }
+    }
+
+    for each (let entry in permissionTableOrigin) {
+        if (selectedPermission == entry.permissionid) {
+            if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
+                res = prepareResultArray(entry, res);
+                break;
+            }
+        }
+    }
+}
+
+result.object(res.sort(sortFunction));
+
+function prepareResultArray(pEntry, pRes) {
+    var rootPermission = "";
+    if (pEntry.accesstype != "E") {
+        if (pEntry.accesstype == "F" && pEntry.cond != "") {
+            rootPermission = PermissionUtil.getRootFieldPermission(pEntry.permissionid);
+            if (rootPermission == "") {
+                rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);
+            }
+        } else 
+            rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);        
+    }
+    pRes.push([pEntry.permissionid, pEntry.entity, pEntry.role, pEntry.field, pEntry.cond, pEntry.action, pEntry.accesstype, pEntry.condtype, rootPermission]);
+    return pRes;
+}
+
+// used to sort result array: Entity -> Records -> Fields
+function sortFunction(a, b) {
+    if (a[6] == b[6] && a[6] != "F" && a[6] != "R")
+        return 0;
+    else if (a[6] == "E")
+        return -1;
+    else if (b[6] == "E")
+        return 1;
+    else if (a[6] == "R" && b[6] == "F")
+        return -1;
+    else if (a[6] == "F" && b[6] == "R")
+        return 1;
+    else if (a[6] == "R" && b[6] == "R" && a[4] == "")
+        return -1;
+    else if (a[6] == "R" && b[6] == "R" && b[4] == "")
+        return 1;
+    else if (a[6] == "F" && b[6] == "F" && a[4] == "")
+        return -1;
+    else if (a[6] == "F" && b[6] == "F" && b[4] == "")
+        return 1;
+    else
+        return 0;
 }
\ No newline at end of file
diff --git a/entity/PermissionDetail_entity/recordcontainers/jdito/onDelete.js b/entity/PermissionDetail_entity/recordcontainers/jdito/onDelete.js
index a53b540b0027aaf55679b569d9433362fb35c4d2..03032f4d74eaf6b7a7e38ddc8c66c55e8fcfd3c1 100644
--- a/entity/PermissionDetail_entity/recordcontainers/jdito/onDelete.js
+++ b/entity/PermissionDetail_entity/recordcontainers/jdito/onDelete.js
@@ -9,7 +9,7 @@ var permId = vars.get("$field.UID");
 var accessType = vars.get("$field.ACCESSTYPE");
 var parentPermSetId = PermissionUtil.getParentPermissionSet(permId);
 var linkedActions = PermissionUtil.getAllChildPermissionActions(permId);
-var alias = "_____SYSTEMALIAS";
+var alias = SqlUtils.getSystemAlias();
 
 var sqlCondDelAction = SqlCondition.begin()
 .and("ASYS_PERMISSIONACTION.ASYS_PERMISSIONACTIONID in ('" + linkedActions.join("','") + "')")
diff --git a/entity/PermissionDetail_entity/recordcontainers/jdito/onInsert.js b/entity/PermissionDetail_entity/recordcontainers/jdito/onInsert.js
index 00b1e28b28b5c0d5810eea6ab9c4330f9a43d434..d74cc3ec7c6b9b91038fb9cf8c52c7793098061a 100644
--- a/entity/PermissionDetail_entity/recordcontainers/jdito/onInsert.js
+++ b/entity/PermissionDetail_entity/recordcontainers/jdito/onInsert.js
@@ -1,3 +1,4 @@
+import("Sql_lib");
 import("Permission_lib");
 import("system.neon");
 import("system.util");
@@ -7,7 +8,7 @@ import("system.tools");
 import("Permission_lib");
 import("Entity_lib");
 
-var alias = "_____SYSTEMALIAS";
+var alias = SqlUtils.getSystemAlias();
 var tablename = "ASYS_PERMISSION";
 var sqlExt = "";
 var permissionid = vars.get("$field.UID");
diff --git a/entity/PermissionDetail_entity/recordcontainers/jdito/onUpdate.js b/entity/PermissionDetail_entity/recordcontainers/jdito/onUpdate.js
index 0bac6c05f928b15b692f05e9d7d286da7979301e..d45ba525a2cd7a43f2ca2710b0038ba6f70b388d 100644
--- a/entity/PermissionDetail_entity/recordcontainers/jdito/onUpdate.js
+++ b/entity/PermissionDetail_entity/recordcontainers/jdito/onUpdate.js
@@ -8,7 +8,7 @@ import("Permission_lib");
 
 var table, cols, vals, cond;
 
-var alias = "_____SYSTEMALIAS";
+var alias = SqlUtils.getSystemAlias();
 var permissionid = vars.get("$field.UID");
 var actionNew = vars.get("$field.ACTION").split(",");
 var entityNew = vars.get("$field.ENTITY");
diff --git a/entity/SerialLetter_entity/SerialLetter_entity.aod b/entity/SerialLetter_entity/SerialLetter_entity.aod
index 37cbad61dc3b8475cb02d5d8f4b5a5a9672b465c..dd4c7fd86fed7fb40f753c34fa643f5b2dad3db8 100644
--- a/entity/SerialLetter_entity/SerialLetter_entity.aod
+++ b/entity/SerialLetter_entity/SerialLetter_entity.aod
@@ -66,6 +66,7 @@
       <name>TITLE</name>
       <title>Title</title>
       <mandatory v="true" />
+      <valueProcess>%aditoprj%/entity/SerialLetter_entity/entityfields/title/valueProcess.js</valueProcess>
     </entityField>
     <entityField>
       <name>DESCRIPTION</name>
@@ -97,6 +98,17 @@
       <name>bindata</name>
       <contentType>FILE</contentType>
     </entityField>
+    <entityField>
+      <name>STATUS</name>
+      <title>Status</title>
+    </entityField>
+    <entityActionField>
+      <name>downloadTemplate</name>
+      <title>Download template</title>
+      <onActionProcess>%aditoprj%/entity/SerialLetter_entity/entityfields/downloadtemplate/onActionProcess.js</onActionProcess>
+      <iconId>VAADIN:FILE_FONT</iconId>
+      <stateProcess>%aditoprj%/entity/SerialLetter_entity/entityfields/downloadtemplate/stateProcess.js</stateProcess>
+    </entityActionField>
   </entityFields>
   <recordContainers>
     <dbRecordContainer>
@@ -146,6 +158,10 @@
           <recordfield>SERIALLETTER.DESCRIPTION</recordfield>
           <isFilterable v="true" />
         </dbRecordFieldMapping>
+        <dbRecordFieldMapping>
+          <name>STATUS.value</name>
+          <recordfield>SERIALLETTER.STATUS</recordfield>
+        </dbRecordFieldMapping>
       </recordFieldMappings>
     </dbRecordContainer>
   </recordContainers>
diff --git a/entity/SerialLetter_entity/entityfields/downloadtemplate/onActionProcess.js b/entity/SerialLetter_entity/entityfields/downloadtemplate/onActionProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..08f18726798d5059a50a4559b66dce881dd40b4b
--- /dev/null
+++ b/entity/SerialLetter_entity/entityfields/downloadtemplate/onActionProcess.js
@@ -0,0 +1,7 @@
+import("system.neon");
+import("system.vars");
+import("DocumentTemplate_lib");
+
+var template = DocumentTemplate.loadTemplate(vars.get("$field.DOCUMENTTEMPLATE_ID"));
+if (template.type)
+    neon.download(template.content, template.filename);
\ No newline at end of file
diff --git a/entity/SerialLetter_entity/entityfields/downloadtemplate/stateProcess.js b/entity/SerialLetter_entity/entityfields/downloadtemplate/stateProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d14adc883f77e859201d92eeded2a5de21a8d97
--- /dev/null
+++ b/entity/SerialLetter_entity/entityfields/downloadtemplate/stateProcess.js
@@ -0,0 +1,8 @@
+import("system.neon");
+import("system.vars");
+import("system.result");
+
+if (vars.get("$field.DOCUMENTTEMPLATE_ID"))
+    result.string(neon.COMPONENTSTATE_EDITABLE);
+else
+    result.string(neon.COMPONENTSTATE_DISABLED);
\ No newline at end of file
diff --git a/entity/SerialLetter_entity/entityfields/title/valueProcess.js b/entity/SerialLetter_entity/entityfields/title/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..94a33c3fc3288465e8959ea57f74eb9b983d1787
--- /dev/null
+++ b/entity/SerialLetter_entity/entityfields/title/valueProcess.js
@@ -0,0 +1,14 @@
+import("system.result");
+import("Document_lib");
+import("system.neon");
+import("system.vars");
+
+if (vars.get("$this.value") == null && vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+{
+    var upload = vars.get("$field.bindata");
+    if (upload)
+    {
+        var filename = DocumentUtil.getFilenameFromUpload(upload);
+        result.string(filename.split(".")[0]);
+    }
+}
\ No newline at end of file
diff --git a/entity/SerialLetter_entity/recordcontainers/db/onDBDelete.js b/entity/SerialLetter_entity/recordcontainers/db/onDBDelete.js
index 34979b2969fbf69d73958e88c10b9d7c378fba3e..55085a49c3ce5eaca03b0a37dac814b5cf0e706c 100644
--- a/entity/SerialLetter_entity/recordcontainers/db/onDBDelete.js
+++ b/entity/SerialLetter_entity/recordcontainers/db/onDBDelete.js
@@ -3,7 +3,7 @@ import("Sql_lib");
 import("system.db");
 
 db.deleteData("LETTERRECIPIENT", SqlCondition.equals("LETTERRECIPIENT.SERIALLETTER_ID", vars.get("$local.uid"), "1=2"));
-var SYSALIAS = "_____SYSTEMALIAS";
+var SYSALIAS = SqlUtils.getSystemAlias();
 var binaryId = db.cell(SqlCondition.begin(SYSALIAS)
     .andPrepareVars("ASYS_BINARIES.ROW_ID", "$local.uid")
     .buildSql("select ID from ASYS_BINARIES"), SYSALIAS);
diff --git a/entity/SerialLetter_entity/recordcontainers/db/onDBInsert.js b/entity/SerialLetter_entity/recordcontainers/db/onDBInsert.js
index 1224558a36e2187d2432748cb0fd69cc1955f923..9b692c8c7d8c4c8588788f61f16a1e048c351481 100644
--- a/entity/SerialLetter_entity/recordcontainers/db/onDBInsert.js
+++ b/entity/SerialLetter_entity/recordcontainers/db/onDBInsert.js
@@ -1,3 +1,4 @@
+import("Sql_lib");
 import("DocumentTemplate_lib");
 import("system.result");
 import("system.vars");
@@ -14,5 +15,5 @@ var template = DocumentTemplate.fromUpload(bindata);
 if (template.content)
 {
     db.insertBinary("SERIALLETTER", "DOCUMENT", letterId, 
-        "", template.content, template.filename, "", "", "_____SYSTEMALIAS");
+        "", template.content, template.filename, "", "", SqlUtils.getSystemAlias());
 }
\ No newline at end of file
diff --git a/entity/SerialLetter_entity/recordcontainers/db/onDBUpdate.js b/entity/SerialLetter_entity/recordcontainers/db/onDBUpdate.js
index 5f84c9de4f1bce2553c114c6c07230ed97c39c67..8f8d362bc8c35d28fc77c9d00a5b192e016c411e 100644
--- a/entity/SerialLetter_entity/recordcontainers/db/onDBUpdate.js
+++ b/entity/SerialLetter_entity/recordcontainers/db/onDBUpdate.js
@@ -15,9 +15,7 @@ var template = DocumentTemplate.fromUpload(bindata);
 
 if (template.content)
 {
-    if (!template.filename)
-        template.filename = bulkMailName + ".html";
-    let sysAlias = "_____SYSTEMALIAS";
+    let sysAlias = SqlUtils.getSystemAlias();
     var binaryId = db.cell(SqlCondition.begin(sysAlias)
         .andPrepareVars("ASYS_BINARIES.ROW_ID", "$local.uid")
         .buildSql("select ID from ASYS_BINARIES", "1=2"), sysAlias);
@@ -26,6 +24,6 @@ if (template.content)
         db.updateBinary(binaryId, "", template.content, template.filename, "", "", sysAlias);
     else
         db.insertBinary("SERIALLETTER", "DOCUMENT", letterId, 
-            "", template.content, template.filename, "", "", "_____SYSTEMALIAS");
+            "", template.content, template.filename, "", "", sysAlias);
     neon.refresh(["$field.content"]);
 }
diff --git a/entity/SupportTicket_entity/recordcontainers/db/onDBInsert.js b/entity/SupportTicket_entity/recordcontainers/db/onDBInsert.js
index d03f1ac83c31d9c93fe323c495011bfc9406a7b8..2d6454962cde27463f1b856741dbbc508adbe9ca 100644
--- a/entity/SupportTicket_entity/recordcontainers/db/onDBInsert.js
+++ b/entity/SupportTicket_entity/recordcontainers/db/onDBInsert.js
@@ -1,6 +1,6 @@
-import("Util_lib");
-import("system.vars");
-import("system.db");
-import("ActivityTask_lib")
-
-CodeUtils.setCode(vars.get("$local.uid"), "TICKET", "TICKETID", "CODE");
\ No newline at end of file
+import("Util_lib");
+import("system.vars");
+import("system.db");
+import("ActivityTask_lib")
+
+ConsecutiveCodeUtils.setCode(vars.get("$local.uid"), "TICKET", "TICKETID", "CODE");
\ No newline at end of file
diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
index 26fe539ab9a1e570435dca64c7c27d1b8ec5bef0..a01ee589caea77a5589eb33ffbe27037134b2083 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -1831,7 +1831,7 @@
     </entry>
     <entry>
       <key>New receipt version</key>
-      <value>Neue Belegsversion</value>
+      <value>Neue Quittungsversion</value>
     </entry>
     <entry>
       <key>Orderitems</key>
diff --git a/neonView/BulkMailEdit_view/BulkMailEdit_view.aod b/neonView/BulkMailEdit_view/BulkMailEdit_view.aod
index bd325868668ac981312ce976c7c53f0bc7e1d945..6673a4671bd569e5f51055fce543bea35d3ca5b7 100644
--- a/neonView/BulkMailEdit_view/BulkMailEdit_view.aod
+++ b/neonView/BulkMailEdit_view/BulkMailEdit_view.aod
@@ -25,10 +25,6 @@
           <name>e363bda2-d8bf-456e-bcae-d1870408022a</name>
           <entityField>NAME</entityField>
         </entityFieldLink>
-        <entityFieldLink>
-          <name>06f08869-5a81-41cb-8c7e-51be6a7041a7</name>
-          <entityField>DESCRIPTION</entityField>
-        </entityFieldLink>
         <entityFieldLink>
           <name>c73d0fb7-b740-48ac-8f3e-fd4199f169da</name>
           <entityField>SUBJECT</entityField>
@@ -41,6 +37,10 @@
           <name>d9b91083-948e-4a0f-a29c-4962ddd78b41</name>
           <entityField>CREATEACTIVITIES</entityField>
         </entityFieldLink>
+        <entityFieldLink>
+          <name>06f08869-5a81-41cb-8c7e-51be6a7041a7</name>
+          <entityField>DESCRIPTION</entityField>
+        </entityFieldLink>
         <entityFieldLink>
           <name>0a67f430-dbcd-4605-b626-ee6d715ab248</name>
           <entityField>content</entityField>
diff --git a/neonView/BulkMailPreview_view/BulkMailPreview_view.aod b/neonView/BulkMailPreview_view/BulkMailPreview_view.aod
index a9bdee7f245a104ebe54b6a5fd5d47fbaa9f6189..a635308c73c5387c3fe6c607f3f664749b7a26fc 100644
--- a/neonView/BulkMailPreview_view/BulkMailPreview_view.aod
+++ b/neonView/BulkMailPreview_view/BulkMailPreview_view.aod
@@ -1,68 +1,68 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.2">
-  <name>BulkMailPreview_view</name>
-  <majorModelMode>DISTRIBUTED</majorModelMode>
-  <layout>
-    <headerFooterLayout>
-      <name>layout</name>
-      <header>Card</header>
-    </headerFooterLayout>
-  </layout>
-  <children>
-    <cardViewTemplate>
-      <name>Card</name>
-      <iconField>ICON</iconField>
-      <titleField>NAME</titleField>
-      <descriptionField>STATUS</descriptionField>
-      <favoriteAction1>sendMail</favoriteAction1>
-      <entityField>#ENTITY</entityField>
-    </cardViewTemplate>
-    <genericViewTemplate>
-      <name>Generic</name>
-      <showDrawer v="true" />
-      <entityField>#ENTITY</entityField>
-      <fields>
-        <entityFieldLink>
-          <name>524c32f8-99d8-453d-afe5-b72a322a4555</name>
-          <entityField>SUBJECT</entityField>
-        </entityFieldLink>
-        <entityFieldLink>
-          <name>9ef1ecd4-063a-4c18-9def-d395f59a64dc</name>
-          <entityField>SENDER</entityField>
-        </entityFieldLink>
-        <entityFieldLink>
-          <name>28c1e091-0fea-4324-8fb8-1893388354b7</name>
-          <entityField>STATUS</entityField>
-        </entityFieldLink>
-        <entityFieldLink>
-          <name>edbeea4d-019f-4661-b0d7-c07468e747cc</name>
-          <entityField>DOCUMENTTEMPLATE_ID</entityField>
-        </entityFieldLink>
-        <entityFieldLink>
-          <name>8bb72d39-3348-4bd6-b57d-f7f5ae573e73</name>
-          <entityField>DESCRIPTION</entityField>
-        </entityFieldLink>
-        <entityFieldLink>
-          <name>514d0861-ad00-4d32-9c56-6be0443e03e3</name>
-          <entityField>CREATEACTIVITIES</entityField>
-        </entityFieldLink>
-      </fields>
-    </genericViewTemplate>
-    <genericViewTemplate>
-      <name>Testing</name>
-      <showDrawer v="true" />
-      <drawerCaption>Testing</drawerCaption>
-      <entityField>#ENTITY</entityField>
-      <fields>
-        <entityFieldLink>
-          <name>ea406803-6189-4de0-aa41-f09f7e1b2336</name>
-          <entityField>TESTING_CONTACT_ID</entityField>
-        </entityFieldLink>
-        <entityFieldLink>
-          <name>f8963199-c89b-48e5-a92c-8f655c05acab</name>
-          <entityField>TESTING_EMAIL_ADDRESS</entityField>
-        </entityFieldLink>
-      </fields>
-    </genericViewTemplate>
-  </children>
-</neonView>
+<?xml version="1.0" encoding="UTF-8"?>
+<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.2">
+  <name>BulkMailPreview_view</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <layout>
+    <headerFooterLayout>
+      <name>layout</name>
+      <header>Card</header>
+    </headerFooterLayout>
+  </layout>
+  <children>
+    <cardViewTemplate>
+      <name>Card</name>
+      <iconField>ICON</iconField>
+      <titleField>NAME</titleField>
+      <descriptionField>STATUS</descriptionField>
+      <favoriteAction1>sendMail</favoriteAction1>
+      <entityField>#ENTITY</entityField>
+    </cardViewTemplate>
+    <genericViewTemplate>
+      <name>Generic</name>
+      <showDrawer v="true" />
+      <entityField>#ENTITY</entityField>
+      <fields>
+        <entityFieldLink>
+          <name>524c32f8-99d8-453d-afe5-b72a322a4555</name>
+          <entityField>SUBJECT</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>9ef1ecd4-063a-4c18-9def-d395f59a64dc</name>
+          <entityField>SENDER</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>28c1e091-0fea-4324-8fb8-1893388354b7</name>
+          <entityField>STATUS</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>edbeea4d-019f-4661-b0d7-c07468e747cc</name>
+          <entityField>DOCUMENTTEMPLATE_ID</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>514d0861-ad00-4d32-9c56-6be0443e03e3</name>
+          <entityField>CREATEACTIVITIES</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>8bb72d39-3348-4bd6-b57d-f7f5ae573e73</name>
+          <entityField>DESCRIPTION</entityField>
+        </entityFieldLink>
+      </fields>
+    </genericViewTemplate>
+    <genericViewTemplate>
+      <name>Testing</name>
+      <showDrawer v="true" />
+      <drawerCaption>Testing</drawerCaption>
+      <entityField>#ENTITY</entityField>
+      <fields>
+        <entityFieldLink>
+          <name>ea406803-6189-4de0-aa41-f09f7e1b2336</name>
+          <entityField>TESTING_CONTACT_ID</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>f8963199-c89b-48e5-a92c-8f655c05acab</name>
+          <entityField>TESTING_EMAIL_ADDRESS</entityField>
+        </entityFieldLink>
+      </fields>
+    </genericViewTemplate>
+  </children>
+</neonView>
diff --git a/neonView/SerialLetterEdit_view/SerialLetterEdit_view.aod b/neonView/SerialLetterEdit_view/SerialLetterEdit_view.aod
index 587a9aeb2e2d76da75d8f38527ecc0f64a46e9a0..a7414e5f2776ac45060fb932612b6b6407723d8e 100644
--- a/neonView/SerialLetterEdit_view/SerialLetterEdit_view.aod
+++ b/neonView/SerialLetterEdit_view/SerialLetterEdit_view.aod
@@ -13,10 +13,6 @@
       <editMode v="true" />
       <entityField>#ENTITY</entityField>
       <fields>
-        <entityFieldLink>
-          <name>b33a8886-75b0-4644-890d-97a8133079ea</name>
-          <entityField>TITLE</entityField>
-        </entityFieldLink>
         <entityFieldLink>
           <name>98b1c32e-8f7e-45f9-bbc8-048851133048</name>
           <entityField>DOCUMENTTEMPLATE_ID</entityField>
@@ -25,6 +21,10 @@
           <name>057f064d-53fd-4d80-a43e-20a4a40d4ffb</name>
           <entityField>bindata</entityField>
         </entityFieldLink>
+        <entityFieldLink>
+          <name>b33a8886-75b0-4644-890d-97a8133079ea</name>
+          <entityField>TITLE</entityField>
+        </entityFieldLink>
         <entityFieldLink>
           <name>1845ae78-7132-4efc-aba0-737dc4d7fc9b</name>
           <entityField>DESCRIPTION</entityField>
diff --git a/process/Calendar_lib/process.js b/process/Calendar_lib/process.js
index 38539e6ee4e3d85bbc6110a9453e60414fbbccfc..fddc0663aa181607e49b22260d9d67d504705686 100644
--- a/process/Calendar_lib/process.js
+++ b/process/Calendar_lib/process.js
@@ -1,1935 +1,1935 @@
-import("system.neon");
-import("system.vars");
-import("system.db");
-import("system.translate");
-import("system.datetime");
-import("system.swing");
-import("system.eMath");
-import("system.calendars");
-import("system.logging");
-import("system.tools");
-import("system.text");
-import("system.question");
-import("system.SQLTYPES");
-import("system.result");
-import("Util_lib");
-import("system.util")
-
-
-/**
- *  @class
- **/
-function CalendarUtil(){}
-
-
-/*
- * Erzeugt und öffnet ein neues Aufgabenobjekt (mit einem Link).
- *
- * @param {String} pSummary opt die Zusammenfassung
- * @param {String} pDescription opt die Beschreibung
- * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
- * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
- *                                   pWithLink[1]: ID des angezeigten Datensatzes
- *                                   pWithLink[2]: Verknüpfungstitel
- * @param {String} pUser opt der Benutzer ( Login )
- * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
- * @param {date} pStart opt Beginn der Aufgabe
- * @param {date} pDuration opt Dauer
- * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
- * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
- * @param {Array{[]} pComps4Refresh opt die zu aktualisierenden Komponenten
- *
- * @return {void}
- */
-CalendarUtil.newTodo = function(pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh)
-{
-    var todo = createEntry( calendars.VTODO, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus );
-    var prompts = [];
-    prompts["comp4refresh"] = [];
-
-    if (pComps4Refresh == undefined)
-        pComps4Refresh = ["$comp.Aufgabe", "$comp.tbl_Aufgabe"];
-
-    for (var i = 0; i < pComps4Refresh.length; i++)
-    {
-        if ( vars.exists(pComps4Refresh[i]))    prompts["comp4refresh"].push(pComps4Refresh[i]);
-    }
-    if(vars.getString("$sys.scope") == "vaadin")
-        neon.openCalendarEntry([todo], null, neon.OPERATINGSTATE_NEW, null)
-    else
-    {
-        if (vars.exists("$sys.currentwindow"))
-            prompts["window"] = vars.getString("$sys.currentwindow");
-        if (vars.exists("$sys.currentimage"))
-            prompts["image"] = vars.getString("$sys.currentimage");
-
-        swing.openCalendarEntry([todo], null, false, prompts);
-    }
-}
-
-
-/**
- * Finds the effective calendarId of an user in the same attribute order like the ADITO core, which is exchangeEmail -> calendarID -> email
- * DO NOT CHANGE THIS ORDER
- *
- * @param {String} user to check
- * @return effective calendar id
- */
-CalendarUtil.getEffectiveCalendarIdFromUser = function(pUser)
-{
-    var userParams = pUser["params"];
-    
-    var resolvedCurrentUser;
-    var exchangeEmail = userParams["exchangeEMail"];
-    var calendarId = userParams["calendarID"];
-    var email = userParams["email"];
-    
-    if(exchangeEmail)
-        return exhangeEmail;
-    else if(calendarId)
-        return calendarId;
-    else if(email)
-        return email;
-    else
-        return "";
-}
-
-/*
- * Erzeugt eine neue Aufgabe (mit einem Link).
- *
- * @param {String} pSummary opt die Zusammenfassung
- * @param {String} pDescription opt die Beschreibung
- * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
- * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
- *                		     pWithLink[1]: ID des angezeigten Datensatzes
- *               		     pWithLink[2]: Verknüpfungstitel
- * @param {String} pUser opt der Benutzer ( Login )
- * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
- * @param {date} pStart opt Beginn der Aufagebe
- * @param {integer} pGroupType opt ( calendars.GROUP_SINGLE , calendars.GROUP_MULTI )
- * @param {date} pDuration opt Dauer
- * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
- * @param {String} pStatus opt Status der Aufgabe ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
- * @param {String} pPriority opt Priorität der Aufgabe
- * @param {String} pReminder opt Erinnerung der Aufgabe
- *
- * @return {void}
- */
-
-CalendarUtil.newSilentTodo = function(pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pGroupType, pCategory, pStatus, pPriority, pReminder)
-{
-    if ( pGroupType == undefined ) pGroupType = calendars.GROUP_SINGLE;
-    var todo = createEntry( calendars.VTODO, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pPriority, pReminder );
-    
-    return calendars.insert([todo],calendars.GROUP_SINGLE);
-}
-
-/*
- * Erzeugt und öffnet ein neues Terminnobjekt mit einem Link.
- *
- * @param {String} pSummary opt die Zusammenfassung
- * @param {String} pDescription opt die Beschreibung
- * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
- * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
- *                		     pWithLink[1]: ID des angezeigten Datensatzes
- *               		     pWithLink[2]: Verknüpfungstitel
- * @param {String} pUser opt der Benutzer ( Login )
- * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
- * @param {date} pStart opt Beginn der Aufagebe
- * @param {date} pDuration opt Dauer
- * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
- * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
- * @param {Array{[]} pComps4Refresh opt die zu aktualisierenden Komponenten
- * 
- * @return {void}
- */
-CalendarUtil.newEvent = function( pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh, pWorklistId)
-{
-    var event = createEntry( calendars.VEVENT, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus );
-
-    var prompts = [];
-    prompts["comp4refresh"] = [];
-    if (pComps4Refresh == undefined)
-        pComps4Refresh = ["$comp.Aufgabe", "$comp.tbl_Termine"];
-    for (i = 0; i < pComps4Refresh.length; i++)
-    {
-        if ( vars.exists(pComps4Refresh[i]))    prompts["comp4refresh"].push(pComps4Refresh[i]);
-    }
-
-    if(vars.getString("$sys.scope") == "vaadin")
-        neon.openCalendarEntry([event],"", neon.OPERATINGSTATE_NEW, null)
-    else
-    {
-        prompts["window"] = vars.getString("$sys.currentwindow");
-        prompts["image"] = vars.getString("$sys.currentimage");
-        if (pWorklistId != undefined)
-            prompts["worklistId"]   = pWorklistId;
-        swing.openCalendarEntry([event], null, false, prompts);
-    }
-}
-
-
-/*
- * Erzeugt einen neuen Termineintrag (mit einem Link).
- *
- * @param {String} pSummary opt die Zusammenfassung
- * @param {String} pDescription opt die Beschreibung
- * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
- * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
- *                		     pWithLink[1]: ID des angezeigten Datensatzes
- *                		     pWithLink[2]: Verknüpfungstitel
- * @param {String} pUser opt der Benutzer ( Login )
- * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
- * @param {date} pStart opt Beginn des Termins
- * @param {date} pDuration opt Dauer
- * @param {integer} pGroupType opt ( calendars.GROUP_SINGLE , calendars.GROUP_MULTI )
- * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
- * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
- * @param {String} pReminder opt Erinnerung des Termins
- *
- * @return {void}
- */
-CalendarUtil.newSilentEvent = function( pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pGroupType, pCategory, pStatus, pReminder)
-{
-    if ( pGroupType == undefined ) pGroupType = calendars.GROUP_SINGLE;
-    var event = createEntry( calendars.VEVENT, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, undefined, pReminder );
-    return calendars.insert( [event] , pGroupType );
-}
-
-/*
- * Erzeugt ein neues Aufgaben- / Termin-Objekt (mit einem Link).
- *
- * @param {date} pType req  Augabe oder Termin ( calendars.VTODO, calendars.VEVENT )
- * @param {String} pSummary opt die Zusammenfassung
- * @param {String} pDescription opt die Beschreibung
- * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
- * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
- *                		     pWithLink[1]: ID des angezeigten Datensatzes
- *                		     pWithLink[2]: Verknüpfungstitel
- * @param {String} pUser opt der Benutzer ( Login )
- * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( [ Login ] )
- * @param {date} pStart opt Beginn
- * @param {date} pDuration opt Dauer
- * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
- * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
- * @param {String} pPriority opt Priorität
- * @param {String} pReminder opt Erinnerung
- *
-@return {Object} das EntryObjekt
- */
-CalendarUtil.createEntry = function( pType, pSummary, pDescription, pWithLink, pAppLinkContext, pAppLinkId, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pPriority, pReminder )
-{
-    var Entry = {};
-    var framename;
-    var framdata;
-    var dbid;
-    var linktitle;
-    if ( pSummary == undefined || pSummary == null  ) pSummary = "";
-    if ( pDescription == undefined || pDescription == null )
-    {
-        if(vars.getString("$sys.scope") == "vaadin")
-            pDescription = neon.getImageContent(vars.getString("$sys.currententityname"));
-        else
-            pDescription = swing.getImageContent();
-    }
-    if ( pUser == undefined || pUser == null ) pUser = vars.getString("$sys.user");
-    //kein translate.key hier, weil es sich um einen rein technischen Wert handelt:
-    if ( pStart == undefined ) pStart = datetime.toLong(datetime.toDate(parseInt(vars.getString("$sys.date")) + datetime.ONE_HOUR, "dd.MM.yyyy HH:00"), "dd.MM.yyyy HH:mm");
-    if ( pCategory == undefined || pCategory == null  ) pCategory = "";
-
-    if (pAffectedUsers == null || pAffectedUsers == undefined )
-    {
-        Entry[calendars.AFFECTEDUSERS] = "";
-    }
-    else
-    {
-        Entry[calendars.AFFECTEDUSERS] = text.encodeMS(calendars.getCalendarUsers(pAffectedUsers));
-    }
-    Entry[calendars.TYPE] = pType;
-    Entry[calendars.DTSTART] = pStart;
-    if ( pType == calendars.VEVENT )
-    {
-        if ( pDuration == undefined )
-            pDuration = datetime.ONE_HOUR;
-
-        Entry[calendars.DTEND] = String ( eMath.addInt( pStart, pDuration) );
-
-        if ( pStatus == undefined )
-            pStatus = calendars.STATUS_CONFIRMED;
-
-        pStatus = mapCalendarStatus(pStatus, calendars.getBackendType() );
-    }
-    else if ( pType == calendars.VTODO )
-    {
-        //kein translate.key hier, weil es sich um einen rein technischen Wert handelt:
-        if ( pDuration != undefined )
-            Entry[calendars.DUE] = String ( eMath.addInt( pStart, pDuration) );
-        else
-            Entry[calendars.DUE] = datetime.toLong(datetime.toDate(pStart, "dd.MM.yyyy 23:59")
-                ,"dd.MM.yyyy HH:mm");
-
-        if ( pStatus == undefined )
-            pStatus = calendars.STATUS_NEEDSACTION;
-
-        pStatus = mapCalendarStatus(pStatus, calendars.getBackendTypeTasks() );
-        
-    }
-
-    Entry[calendars.USER] = calendars.getCalendarUser(pUser);
-    Entry[calendars.DESCRIPTION] = pDescription;
-    Entry[calendars.SUMMARY] = pSummary;
-    Entry[calendars.STATUS] = pStatus;
-    Entry[calendars.CLASSIFICATION] = calendars.CLASSIFICATION_PUBLIC;
-    Entry[calendars.CATEGORIES] = pCategory;
-    Entry[calendars.TRANSPARENCY] =  "OPAQUE";
-   
-
-    if( pPriority != undefined )
-    {
-        Entry[calendars.PRIORITY] = pPriority;
-    }
-
-    if( pReminder != undefined)
-    {
-        Entry[calendars.HASREMINDER] = "true";
-        Entry[calendars.REMINDER_DURATION] = pReminder;
-    }
-    else
-        Entry[calendars.HASREMINDER] = "false";
-   
-
-    if (pWithLink == false)
-    {
-        Entry[calendars.LINKS] = "0";
-    }
-    else
-    {
-        var fd = new FrameData();
-        if ( typeof(pWithLink) == "object" )
-        {
-            for ( var li = 0; li < pWithLink.length; li++ )
-            {
-                framename = pWithLink[li][0];
-                framdata = fd.getData("name", framename, ["table","idcolumn","title"])[0];
-                dbid = pWithLink[li][1];
-                linktitle = framdata[2] + " - " + pWithLink[li][2];
-
-                Entry["LINK_ALIAS_" + ( li + 1 )] = vars.getString("$sys.dbalias");
-                Entry["LINK_TABLE_" + ( li + 1 )] = framdata[0];
-                Entry["LINK_IDCOLUMN_" + ( li + 1 )] = framdata[1];
-                Entry["LINK_DBID_" + ( li + 1 )] = dbid;
-                Entry["LINK_FRAME_" + ( li + 1 )] = "comp." + framename;
-                Entry["LINK_TITLE_" + ( li + 1 )] = linktitle;
-            }
-            Entry[calendars.LINKS] = pWithLink.length.toString();
-        }
-        else
-        {
-            if ( pWithLink == true || pWithLink == undefined )
-            {
-                framename = vars.getString("$sys.currentimagename");
-                framdata = fd.getData("name", framename, ["table","idcolumn","title"])[0];
-                dbid = vars.getString("$comp.idcolumn");
-                linktitle = framdata[2] + " - " + swing.getImageContent();
-            }
-            Entry[calendars.LINKS] = "1";
-            Entry["LINK_ALIAS_1"] = vars.getString("$sys.dbalias");
-            Entry["LINK_TABLE_1"] = framdata[0];
-            Entry["LINK_IDCOLUMN_1"] = framdata[1];
-            Entry["LINK_DBID_1"] = dbid;
-            Entry["LINK_FRAME_1"] = "comp." + framename;
-            Entry["LINK_TITLE_1"] = linktitle;
-        }
-    }
-    
-
-if(pAppLinkContext && pAppLinkId)
-{
-    Entry["AppLinkContext"] = pAppLinkContext;
-    Entry["AppLinkId"] = pAppLinkId;
-}
-    return Entry;
-}
-
-/*
- * Liefert den CalendarStatus übersetzt zurück.
- * @param {String} pStatus req Status
- * @param {String} pLanguage opt Sprache
- * @param {String} pKind opt ToDo oder Event
- *
- * @return {String} übersetzte Status
- */
-function getCalendarStatus( pStatus, pLanguage, pKind)
-{
-    //kein mappen des Status, da wirklich verschiedene Dinge angezeigt werden sollen
-    switch ( pStatus )
-    {
-        case calendars.STATUS_BUSY: 
-            return translate.text("Gebucht", pLanguage);
-        case calendars.STATUS_CANCELLED:
-            if(pKind == "ToDo" && pKind != undefined) return translate.text("Zurückgestellt", pLanguage)
-            return translate.text("Abgesagt", pLanguage);
-        case calendars.STATUS_COMPLETED:
-            return translate.text("Erledigt", pLanguage);
-        case calendars.STATUS_CONFIRMED:
-            return translate.text("Bestätigt", pLanguage);
-        case calendars.STATUS_FREE:
-            return translate.text("frei", pLanguage);
-        case calendars.STATUS_INPROCESS:
-            return translate.text("In Bearbeitung", pLanguage);
-        case calendars.STATUS_NEEDSACTION:
-            return translate.text("Nicht begonnen", pLanguage);
-        case calendars.STATUS_OOF:
-            return translate.text("Außer Haus", pLanguage);
-        case calendars.STATUS_TENTATIVE:
-            return translate.text("Vorläufig", pLanguage);
-        default:
-            return "";
-    }
-}
-
-/*
- * Zu einer übergebenen Priorität wird ihre Bedeutung übersetzt und zurückgegeben.
- *
- * @param {String} pPriority req Priorität
- * @param {String} pLanguage opt Sprache
- *
- * @return (String) übersetzte Bedeutung einer Priorität
- */
-function getCalendarPriority(pPriority, pLanguage)
-{
-    switch(pPriority)
-    {
-        case "9":
-            return translate.text("niedrig", pLanguage);
-            break;
-        case "5":
-            return translate.text("normal", pLanguage);
-            break;
-        case "1":
-            return translate.text("hoch", pLanguage);
-            break;
-        default:
-            return translate.text("keine", pLanguage);
-            break;
-    }
-}
-
-/*
- * Liefert zum Objekt verknüpfte Aufgaben aus dem Kalender.
- *
- * @param {String} pFrame req Name des Frames
- * @param {String} pDBID req ID des verknüpften Datensatzes
- * @param {String} pAlias opt
- * @param {String} pLanguage opt Sprache
- *
- * @return {[]} mit Aufgaben aus Kalender
- */
-function getLinkedToDos (pFrame, pDBID, pAlias, pLanguage )
-{
-    if (pAlias == undefined ) pAlias = vars.getString("$sys.dbalias");
-    var tab = [];
-    var status = " and STATUS in ('NEEDS-ACTION', 'IN-PROCESS')";
-    var exists = [];
-    var zustaendig = "";
-    var today = getDate(vars.getString("$sys.date"));
-    var filtervalues = ["", "false"];
-    if ( vars.exists("$image.FilterValuesT") )
-    {
-        filtervalues = vars.get("$image.FilterValuesT");
-        zustaendig = filtervalues[0]
-        if (filtervalues[1] == "true") status = " and STATUS in ('COMPLETED', 'CANCELLED')";
-    }
-
-    var entryids = db.table("select ENTRYID, OWNER from ASYS_CALENDARLINK "
-        + "join ASYS_CALENDARBACKEND on ELEMENTUID = ENTRYID where FRAME = 'comp." + pFrame + "' "
-        + "and ENTRYID is not null "
-        + "and ENTRYTYPE = " + calendars.VTODO + " "
-        + "and DBID = '" + pDBID + "' "
-        + status, pAlias);
-
-    for (var i = 0; i < entryids.length; i++)
-    {
-        if ( exists.indexOf(entryids[i][0]) == -1)
-        {
-            try
-                {
-                    var entry = calendars.getEntry(entryids[i][0], null, getTitleCalenderUser( entryids[i][1] ), calendars.VTODO);
-                    var entr = new Array;
-                    status = "";
-                    var user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
-                    if(user == null) user = entry[calendars.USER2]["cn"];
-                     else user = user[tools.TITLE]
-                    if ((user == zustaendig || zustaendig == ""))
-                    {
-                        entr[0] = text.encodeMS([entry[calendars.ID], user]);
-                        var due = getDate(entry[calendars.DUE]);
-                        if (due < today ) entr[1]	= "-1769402";
-                        else  if (due > today) entr[1]	= "-16777216"; else entr[1]	= "-16744020";
-                        entr[2] = "-1";  // Hintergrundfarbe
-                        entr[3] = text.decodeMS(entry[calendars.AFFECTEDUSERS]).length;
-                        entr[4] = entry[calendars.DUE]
-                        entr[5] = getCalendarStatus( entry[calendars.STATUS], pLanguage, "ToDo");
-                        entr[6] = entry[calendars.SUMMARY]
-                        entr[7] = getRealName([entry[calendars.ORGANIZER2]]);
-                        entr[8] = getRealName(entry[calendars.ATTENDEES]);
-                        entr[9] = entry[calendars.DESCRIPTION];
-                        entr[10] = entry[calendars.PRIORITY];
-                        tab.push(entr);
-                        exists.push(entryids[i][0]);
-                    }
-            }
-            catch (ex)
-            {
-                logging.log(ex);
-            }
-        }
-    }
-    array_mDimSort(tab, 4, false); //Sortierung nach Fälligkeitsdatum
-    return tab;
-}
-
-/*
- * Anzeige des Aufgaben-Filter
- *
- * @param {Object} pFilter req
- *
- * @return string Anzeige
- */
-function show_filterLinkedToDos(pFilter)
-{
-    var retstring = "";
-    if (pFilter[0] != "")
-    {
-        var userp = tools.getUser( pFilter[0] )[tools.PARAMS];
-        retstring = translate.text("Aufgaben von") + " " + userp[tools.FIRSTNAME] + " " + userp[tools.LASTNAME];
-    }
-    if (pFilter[1] == "true") retstring += ", " + translate.text("erledigt / zurückgestellt");
-
-    return retstring
-}
-
-/*
- * Liefert zum Objekt verknüpfte Events aus dem Kalender.
- *
- * @param {String} pFrame req Name des Frames
- * @param {String} pDBID req ID des verknüpften Datensatzes
- * @param {Object} pFilter opt
- * @param {String} pAlias opt
- * @param {String} pUser opt Benutzer
- *
- * @return {[]} mit Events aus Kalender
- */
-function getLinkedEvents (pFrame, pDBID, pFilter, pAlias, pUser )
-{
-    if ( pFilter == "" || pFilter == undefined)
-        pFilter = reset_filterEvent();
-
-    var tab = [];
-    var exists = [];
-    var conditions = [];
-    var today = getDate(vars.getString("$sys.date"));
-    var conditioncount = 0;
-    var stati = [];
-
-    if ( pFilter.tentative == "true" )
-        stati.push(mapCalendarStatus(calendars.STATUS_TENTATIVE, calendars.getBackendType() ));
-
-    if ( pFilter.cancelled == "true" )
-        stati.push(mapCalendarStatus(calendars.STATUS_CANCELLED, calendars.getBackendType() ));
-
-    if ( pFilter.confirmed == "true" )
-        stati.push(mapCalendarStatus(calendars.STATUS_CONFIRMED, calendars.getBackendType() ));
-
-    if (calendars.getBackendType() == calendars.BACKEND_EXCHANGEWS)
-        stati.push(calendars.STATUS_FREE);
-
-    if (pAlias == undefined ) pAlias = vars.getString("$sys.dbalias");
-
-    var entryids = db.table(["select ENTRYID, OWNER "
-        + "from ASYS_CALENDARLINK "
-        + "join ASYS_CALENDARBACKEND on ELEMENTUID = ENTRYID where FRAME = 'comp."
-        + pFrame + "' and ENTRYID is not null and ENTRYTYPE = " + calendars.VEVENT
-        + " and DBID = '" + pDBID + "' and DTSTART >= ?",
-        [ [ String(pFilter.datefrom - datetime.ONE_WEEK), SQLTYPES.DATE ]]], pAlias );
-
-    /*
-     * Check for rights before constructing condition otherwise you'll get an error by opening linked Data
-     */
-    var userToRead = []
-    if(pUser != undefined)
-    {
-        userToRead = getCalendarUsers( calendars.RIGHT_READ_APPOINTMENT, pUser );
-    }
-    else
-    {
-        userToRead = calendars.getDisplayCalendarUsers(calendars.RIGHT_READ_APPOINTMENT);
-    }
-    var userMap = {};
-    for (let i = 0; i < userToRead.length; i++)
-    {
-        userMap[userToRead[i][1]] = true;
-    }
-
-    for ( var i = 0;i < entryids.length; i++)
-    {
-        var user = getTitleCalenderUser(entryids[i][1]);
-        if(userMap[user])
-        {
-            for ( var z = 0; z < stati.length; z++ )
-                _addEntryCondition(conditions, String(++conditioncount),
-                {
-                    TYPE: calendars.VEVENT,
-                    START: pFilter.datefrom,
-                    END: pFilter.dateto,
-                    USER: user,
-                    STATUS: stati[z],
-                    UID: entryids[i][0]
-                });
-        } else continue
-    }
-    conditions["COUNT"] = String(conditioncount);
-
-    var entries = calendars.getExpandedEntries(conditions, pFilter.datefrom, pFilter.dateto);
-    for ( let i = 0; i < entries.length; i++)
-    {
-        for (var j = 0; j < entries[i].length; j++)
-        {
-            var entry = entries[i][j];
-            if( exists.indexOf(entry[calendars.ID]) == -1 && ( pFilter.category == "" || text.decodeMS(entry[calendars.CATEGORIES]).indexOf(pFilter.category) > 0))
-            {
-                var entr = new Array;
-                var start = getDate(entry[calendars.DTSTART]);
-                var end = getDate(entry[calendars.DTEND]);
-                user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
-                if(user == null) user = entry[calendars.USER2]["cn"];
-                 else user = user[tools.TITLE]
-                entr[0] = text.encodeMS([entry[calendars.ID], user , entry[calendars.RECURRENCEID]]);
-                if (end < today) entr[1]	= "-6710887" ;
-                else if (start <= today && end >= today )   entr[1] = "-16744020" ;
-                else entr[1] = "-16777216";
-                entr[2] = "-1"
-                entr[3] = text.decodeMS(entry[calendars.AFFECTEDUSERS]).length;
-                entr[4] = entry[calendars.DTSTART]
-                entr[5] = entry[calendars.DTEND]
-                entr[6] = entry[calendars.SUMMARY]
-                entr[7] = getRealName([entry[calendars.ORGANIZER2]]);
-                entr[8] = getRealName(entry[calendars.ATTENDEES]);
-                entr[9] = entry[calendars.DESCRIPTION]
-                tab.push(entr);
-                exists.push(entry[calendars.ID]);
-            }
-        }
-    }
-    array_mDimSort(tab, 4, false);
-    return tab;
-}
-
-/*
- * Liefert Aufgaben aus dem Kalender.
- *
- * @param {Object} pFilter req
- * @param {String} pLanguage opt
- * @param {bool} pShortForm opt wenn true wird nur die kurzversion geladen
- *
- * @return {[]} mit allen aufgaben aus dem Kalender
- */
-function getTodos( pFilter, pLanguage, pShortForm )
-{
-    if ( pFilter == "" )    pFilter =  reset_filterToDo();
-    var tab = [];
-    var today = getDate (vars.getString("$sys.date"));
-    var conditions = [];
-    var conditioncount = 0;
-    var user = pFilter.user;
-    var stati = [];
-    var status = [];
-    var exists = [];
-    var entries = [];
-
-    if ( pFilter.needs_action == "true" )
-    {
-        stati.push(mapCalendarStatus(calendars.STATUS_NEEDSACTION, calendars.getBackendTypeTasks() ));
-        status.push("NEEDS-ACTION");
-    }
-    if ( pFilter.in_process == "true" )
-    {
-        stati.push(mapCalendarStatus(calendars.STATUS_INPROCESS, calendars.getBackendTypeTasks() ));
-        status.push("IN-PROCESS");
-    }
-    if ( pFilter.completed == "true" )
-    {
-        stati.push(mapCalendarStatus(calendars.STATUS_COMPLETED, calendars.getBackendTypeTasks() ));
-        status.push("COMPLETED");
-    }
-    if ( pFilter.cancelled == "true" )
-    {
-        stati.push(mapCalendarStatus(calendars.STATUS_CANCELLED, calendars.getBackendTypeTasks() ));
-        status.push("CANCELLED");
-    }
-    if (pFilter.delegated == "true" )
-    {
-        var from = [pFilter.datefrom, SQLTYPES.TIMESTAMP];
-        var to = [pFilter.dateto, SQLTYPES.TIMESTAMP];
-        setAllCalendarGrant();
-        user = calendars.getCalendarUser(user);
-        user = db.table(["select ELEMENTUID, OWNER from ASYS_CALENDARBACKEND where ENTRYTYPE = 2 and OWNER != '" + user + "' and ORGANIZER = '"
-            + user + "' and STATUS in ('" + status.join("', '") + "') and (( STARTTIME >= ? and STARTTIME <= ?) or "
-            + "( ENDTIME >= ? and ENDTIME <= ? ) or ( STARTTIME >= ? and ENDTIME <= ? ))", [from, to, from, to, from, to]] );
-
-        for (let i = 0; i < user.length; i++ )
-        {
-            try
-            {
-                entries.push([calendars.getEntry(user[i][0], null, getTitleCalenderUser(user[i][1]), calendars.VTODO)]);
-            }
-            catch(err){
-                logging.log(err);
-            }
-        }
-        setCalendarGrant();
-    }
-    else
-    {
-        if ( typeof( pFilter.user ) != "object" )  user = [user.trim()];
-        for (let i = 0; i < user.length; i++ )
-        for ( var z = 0; z < stati.length; z++ )
-            _addEntryCondition(conditions, ++conditioncount,
-            {
-                TYPE: calendars.VTODO,
-                START: pFilter.datefrom,
-                END: pFilter.dateto,
-                USER: user[i],
-                STATUS: stati[z]
-            });
-
-        conditions["COUNT"] = String(conditioncount);
-        entries = calendars.getEntries(conditions);
-    }
-
-    for (i = 0; i < entries.length; i++)
-    {
-        var entry = entries[i][0];
-        user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
-        if (user == null)  user = entry[calendars.USER2]["cn"];
-         else user = user[tools.TITLE]
-        if ( !(user != vars.getString("$sys.user") && entry[calendars.CLASSIFICATION] == calendars.CLASSIFICATION_PRIVATE) // no privat
-            && !( pFilter.delegated == "true" && ( isAffectedUser( entry, pFilter.user) || exists.indexOf(entry[calendars.ID]) > -1  ) ) // no duplicate
-            && ( pFilter.category == "" || text.decodeMS(entry[calendars.CATEGORIES]).indexOf( pFilter.category ) > -1 ) ) // Filter category
-            {
-            var entr = [];
-            var links =  entry[calendars.LINKS];
-            var due = entry[calendars.DUE] != "" ? getDate(entry[calendars.DUE]) : "";
-            entr[0] = text.encodeMS([entry[calendars.ID], user]);
-            if (due == today )      entr[1] = "-16744020" ;
-            else if(due == "")      entr[1] = "-13395712";
-            else if (due > today )  entr[1] = "-16777216";
-            else                    entr[1]	= "-1769402";
-            if (entry[calendars.PRIORITY] == "1") entr[2] = "-100";
-            entr[3] = entry[calendars.ATTENDEES].length;
-            entr[4] = entry[calendars.DUE];
-
-            if (pShortForm)
-            {
-                entr[5] = entry[calendars.SUMMARY];
-                entr[6] = entry[calendars.DESCRIPTION];
-            }
-            else
-            {
-                entr[5] = getCalendarStatus( entry[calendars.STATUS], pLanguage, "ToDo");
-                entr[6] = getCalendarPriority( entry[calendars.PRIORITY], pLanguage);
-                entr[7] = entry[calendars.SUMMARY];
-                entr[8] = getRealName( [ entry[calendars.ORGANIZER2] ] );
-                entr[9] = getRealName( entry[calendars.ATTENDEES] );
-                if (links == undefined) entr[10] = "";	else entr[10] = links;
-                entr[11] = entry[calendars.DESCRIPTION];
-                entr[12] = entry[calendars.CREATED];
-            }
-
-            tab.push(entr);
-            if ( pFilter.delegated == "true" )  exists.push(entry[calendars.ID]);
-        }
-    }
-
-    if (pShortForm)
-        sortArray(tab, -1, 4, 1, 5);
-    else
-        sortArray(tab, -1, 4, 1, 12 );
-
-    return tab;
-}
-
-/*
- * Fügt eine Condition hinzu
- *
- * @param {[]} pConditions req die Conditions
- * @param {Integer} pIndex req Index der Condition
- * @param {Object} pValues req
- *
- * @return {void}
- */
-function _addEntryCondition(pConditions, pIndex, pValues)
-{
-    var params = ["TYPE", "START", "END", "USER", "STATUS", "UID"];
-
-    for (var i = 0; i < params.length; i++)
-        if (pValues[params[i]] != undefined)    pConditions[params[i] + "_" + pIndex] = pValues[params[i]];
-}
-CalendarUtil.addEntryCondition = function(pConditions, pIndex, pValues)
-{
-    _addEntryCondition(pConditions, pIndex, pValues);
-} 
-
-/*
- * Liefert Events zu bestimmten Usern/Daten in einem Array.
- *
- * @param {Object} pFilter req
- * @param {String} pLanguage opt
- * @param {bool} pShortForm opt wenn true wird nur die kurzversion geladen
- *
- * @return {[]}
- *				[0] ID
- *				[1] Vordergrundfarbe
- *				[2] Hintergrundfarbe
- *				[3] Start
- *				[4] Ende
- *				[5] Betreff
- *				[6] Inhalt
- *				[7] User
- *				[8] Anzahl Verknüpfungen
- *				[9] Klassifikation (privat/öffentlich)
- */
-function getEvents( pFilter, pLanguage, pShortForm )
-{
-    if ( pFilter == "" )  pFilter = reset_filterEvent();
-    var tab = [];
-    var conditions = [];
-    var today = getDate(vars.getString("$sys.date"));
-    var conditioncount = 0;
-    var stati = [];
-    var user = undefined;
-    if ( pFilter.tentative == "true" )    stati.push(calendars.STATUS_TENTATIVE);
-    if ( pFilter.cancelled == "true" )    stati.push(calendars.STATUS_CANCELLED);
-    if ( pFilter.confirmed == "true" )
-        stati.push(mapCalendarStatus(calendars.STATUS_CONFIRMED, calendars.getBackendType() ));
-
-    if (getCalendarSystemType(calendars.VEVENT) == calendars.BACKEND_EXCHANGEWS && pFilter.free == "true")
-        stati.push(calendars.STATUS_FREE);
-
-    if ( pFilter.user != "" )	user = (pFilter.user).trim();
-
-    for ( var z = 0; z < stati.length; z++ )
-        _addEntryCondition(conditions, String(++conditioncount),
-        {
-            TYPE: calendars.VEVENT,
-            START: pFilter.datefrom,
-            END: pFilter.dateto,
-            USER: user,
-            STATUS: stati[z]
-        });
-
-    conditions["COUNT"] = String(conditioncount);
-
-    var entries = calendars.getExpandedEntries(conditions, pFilter.datefrom, pFilter.dateto);
-    for ( var i = 0;i < entries.length; i++)
-    {
-        for (var j = 0; j < entries[i].length; j++)
-        {
-            var entry = entries[i][j];
-            if( pFilter.category == "" || text.decodeMS(entry[calendars.CATEGORIES]).indexOf(pFilter.category) > -1 )
-            {
-                var entr = new Array;
-                var start = getDate(entry[calendars.DTSTART]);
-                var end = getDate(entry[calendars.DTEND]);
-
-                user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
-                if(user == null) user = entry[calendars.USER2]["cn"];
-                else user = user[tools.TITLE]
-                entr[0] = text.encodeMS([ entry[calendars.ID], user, entry[calendars.RECURRENCEID]]);
-                if (end < today ) entr[1]	="-6710887" ;
-                else if (start > today) entr[1]	= "-16777216";
-                else entr[1]	= "-16744020" ;
-                entr[2] = "-1"
-                entr[3] = entry[calendars.ATTENDEES].length;
-                entr[4] = entry[calendars.DTSTART];
-                entr[5] = entry[calendars.DTEND];
-                entr[6] = entry[calendars.SUMMARY];
-
-                if (!pShortForm)
-                {
-                    entr[7] = getRealName([entry[calendars.ORGANIZER2]]);
-                    entr[8] = getRealName(entry[calendars.ATTENDEES]);
-                    entr[9] = getCalendarStatus( entry[calendars.STATUS], pLanguage );
-                    if (entry[calendars.LINKS] == undefined) entr[10] = "";
-                    else entr[10] = entry[calendars.LINKS];
-                    entr[11] = entry[calendars.DESCRIPTION];
-                }
-                tab.push( entr );
-            }
-        }
-    }
-    sortArray(tab, -1, 4, 1, 6 );
-    return tab;
-}
-
-/*
- * Liefert den echten Namen anhand des Logins zurück
- *
- * @param {Array}[]} pUserMap req pUserMap
- *
- * @return String
- */
-function getRealName(pUserMap)
-{
-    var resultName = [];
-    var RealNames = getRealNameObject(pUserMap);
-
-    for ( var realname in RealNames )   resultName.push(RealNames[realname]);
-    return resultName.join(", \n");
-}
-
-/*
- * Liefert den echten Namen anhand des Logins zurück
- *
- * @param {Array}[]} pUserMap req pUserMap
- *
- * @return Object
- */
-function getRealNameObject(pUserMap)
-{
-    var resultObject = {};
-    var realname = "";
-
-    for ( let i = 0; i < pUserMap.length; i++ )
-    {
-        let user = tools.getUserByAttribute(tools.CALENDARID, [pUserMap[i]["paramvalue"].substr("mailto:".length)])
-        if ( user != null )
-        {
-            if(vars.exists("$global.firstLastName") && vars.get("$global.firstLastName"))
-                realname = user[tools.PARAMS][tools.LASTNAME] + " " + user[tools.PARAMS][tools.FIRSTNAME];
-            else realname = user[tools.PARAMS][tools.FIRSTNAME] + " " + user[tools.PARAMS][tools.LASTNAME];
-        }
-        else //Der User existiert nicht im System
-        {
-            realname = pUserMap[i]["cn"] + " " + pUserMap[i]["paramvalue"];
-        }
-        resultObject[pUserMap[i]["cn"]] = realname;
-    }
-    return resultObject;
-}
-
-
-/*
- * Gibt an ob der User im Calendarobject vorhanden ist
- *
- * @param {Object} pEntry req Calendarobject
- * @param {String} pUser req Title
- *
- * @return Object
- */
-function isAffectedUser( pEntry, pUser)
-{
-    var usermap = pEntry[calendars.ATTENDEES];
-
-    for ( var i = 0; i < usermap.length; i++ )
-        if( usermap[i]["cn"] == pUser )
-            return true;
-    return false;
-}
-
-/*
- * Liefert das Datum ohne Urzeit zurück
- *
- * @param {String} datetimeIn req DatumZeit
- *
- * @return {date}
- */
-function getDate( datetimeIn )
-{
-    if ( datetimeIn != "")
-        return datetime.clearTime(datetimeIn);
-    else return "";
-}
-
-/*
- * Setzt den Aufgaben-Filter
- *
- * @param {Object} pFilter req
- *
- * @return {image}
- */
-function filterToDo( pFilter )
-{
-    var error = true;
-    var von = pFilter.datefrom;
-    var bis = pFilter.dateto;
-    if ( pFilter == "" )	pFilter =  reset_filterToDo();
-    do
-    {
-        vars.set("$local.relation_id", pFilter.user);
-        vars.set("$local.edt_von", pFilter.datefrom);
-        vars.set("$local.edt_bis", pFilter.dateto);
-        vars.set("$local.category", pFilter.category);
-        vars.set("$local.delegated", pFilter.delegated);
-        vars.set("$local.needs_action", pFilter.needs_action);
-        vars.set("$local.in_process", pFilter.in_process);
-        vars.set("$local.completed", pFilter.completed);
-        vars.set("$local.cancelled", pFilter.cancelled);
-        var res = swing.askUserQuestion(translate.text("Bitte Filterbedingungen setzen"), "DLG_TASK_FILTER");
-        if( res != null )
-        {
-            pFilter.user =      res["DLG_TASK_FILTER.relation_id"];
-            pFilter.datefrom =  res["DLG_TASK_FILTER.edt_von"];
-            pFilter.dateto =    res["DLG_TASK_FILTER.edt_bis"];
-            pFilter.category =  res["DLG_TASK_FILTER.category"];
-            pFilter.delegated = res["DLG_TASK_FILTER.delegated"];
-            pFilter.needs_action = res["DLG_TASK_FILTER.needs_action"];
-            pFilter.in_process = res["DLG_TASK_FILTER.in_process"];
-            pFilter.completed = res["DLG_TASK_FILTER.completed"];
-            pFilter.cancelled = res["DLG_TASK_FILTER.cancelled"];
-            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
-            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
-        }
-        else
-        {
-            pFilter.datefrom = von;
-            pFilter.dateto = bis;
-            error = false;
-        }
-    }
-    while ( error )
-    return pFilter;
-}
-
-/*
- * Setzt den Aufgaben-Filter
- *
- * @param {Object} pFilter req
- *
- * @return {image}
- */
-function filterToDo_Neon( pFilter )
-{
-    var error = true;
-    var von = pFilter.datefrom;
-    var bis = pFilter.dateto;
-    if ( pFilter == "" )	pFilter =  reset_filterToDo();
-    do
-    {
-        var prompts = {
-            FILTER_TEXT: translate.text("Bitte Filterbedingungen setzen"),
-            RESPONSIBLE: pFilter.user,
-            DATE_FROM: pFilter.datefrom,
-            DATE_TO: pFilter.dateto,
-            CATEGORY_TODO: pFilter.category,
-            DELEGATED: pFilter.delegated,
-            NEEDS_ACTION: pFilter.needs_action,
-            IN_PROCESS: pFilter.in_process,
-            COMPLETED: pFilter.completed,
-            CANCELLED: pFilter.cancelled
-        }
-
-        var buttons = {
-            "ok" : translate.text("OK"),
-            "": translate.text("Abbrechen")
-            };
-        var defaultButton = "ok";
-
-        var res = question.openDialog("DLG_FILTER_TODO_Neon", prompts, buttons, defaultButton);
-
-        if( res.button != null )
-        {
-            pFilter.user =      res.RESPONSIBLE;
-            pFilter.datefrom =  res.DATE_FROM;
-            pFilter.dateto =    res.DATE_TO;
-            pFilter.category =  res.CATEGORY_TODO;
-            pFilter.delegated = res.DELEGATED;
-            pFilter.needs_action = res.NEEDS_ACTION;
-            pFilter.in_process = res.IN_PROCESS;
-            pFilter.completed = res.COMPLETED;
-            pFilter.cancelled = res.CANCELLED;
-            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
-            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
-        }
-        else
-        {
-            pFilter.datefrom = von;
-            pFilter.dateto = bis;
-            error = false;
-        }
-    }
-    while ( error )
-    return pFilter;
-}
-
-
-/*
- * Anzeige des Aufgaben-Filter
- *
- * @param {Object} pFilter req
- *
- * @return string Anzeige
- */
-function show_filterToDo(pFilter)
-{
-    var retstring = "";
-    var userp = tools.getUser( pFilter.user )[tools.PARAMS];
-    if (pFilter.user != "") retstring = (translate.text("Aufgaben von") + " " + userp[tools.FIRSTNAME] + " " + userp[tools.LASTNAME]);
-    if (pFilter.datefrom != "") retstring += ", " + datetime.toDate(pFilter.datefrom, translate.text("dd.MM.yyyy"));
-    if (pFilter.dateto != "") retstring += " - " + datetime.toDate(pFilter.dateto, translate.text("dd.MM.yyyy"));
-    if (pFilter.category != "") retstring += ", " + translate.text("Kategorie") + " " + pFilter.category;
-    if (pFilter.delegated == "true") retstring += ", " + translate.text("delegiert");
-    if (pFilter.needs_action == "true") retstring += ", " + translate.text("Nicht begonnen");
-    if (pFilter.in_process == "true") retstring += ", " + translate.text("In Bearbeitung");
-    if (pFilter.completed == "true") retstring += ", " + translate.text("Erledigt");
-    if (pFilter.cancelled == "true") retstring += ", " + translate.text("Zurückgestellt");
-
-    return retstring
-}
-
-/*
- * Setzt den Aufgaben-Filter zurück
- *
- * @return {filter}
- */
-function reset_filterToDo()
-{
-    var today = getDate (vars.getString("$sys.date"));
-
-    return pFilter =  {
-        user: vars.getString("$sys.user"),
-        datefrom: String(eMath.subInt(today, 720 * datetime.ONE_DAY)),
-        dateto: String(eMath.addInt(eMath.addInt(today, 3 * datetime.ONE_DAY)
-            ,datetime.ONE_DAY - datetime.ONE_MINUTE)),
-        category: "",
-        delegated: "",
-        needs_action: "true",
-        in_process: "true",
-        completed: "",
-        cancelled: ""
-    };
-}
-
-/*
- * Setzt den Event-Filter
- *
- * @param {Object} pFilter req
- *
- * @return {image}
- */
-function filterEvent( pFilter )
-{
-    var error = true;
-    var von = pFilter.datefrom;
-    var bis = pFilter.dateto;
-    if ( pFilter == "" )	pFilter =  reset_filterEvent();
-    do
-    {
-        vars.set("$local.relation_id", pFilter.user);
-        vars.set("$local.edt_von", pFilter.datefrom);
-        vars.set("$local.edt_bis", pFilter.dateto);
-        vars.set("$local.category", pFilter.category);
-        vars.set("$local.tentative", pFilter.tentative);
-        vars.set("$local.confirmed", pFilter.confirmed);
-        vars.set("$local.cancelled", pFilter.cancelled);
-        vars.set("$local.free", pFilter.free);
-        var res = swing.askUserQuestion(translate.text("Bitte Filterbedingungen setzen"), "DLG_EVENT_FILTER");
-        if( res != null )
-        {
-            pFilter.user =      res["DLG_EVENT_FILTER.relation_id"];
-            pFilter.datefrom =  res["DLG_EVENT_FILTER.edt_von"];
-            pFilter.dateto =    res["DLG_EVENT_FILTER.edt_bis"];
-            pFilter.category =  res["DLG_EVENT_FILTER.category"];
-            pFilter.tentative = res["DLG_EVENT_FILTER.tentative"];
-            pFilter.confirmed = res["DLG_EVENT_FILTER.confirmed"];
-            pFilter.cancelled = res["DLG_EVENT_FILTER.cancelled"];
-            pFilter.free      = res["DLG_EVENT_FILTER.free"];
-            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
-            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
-        }
-        else
-        {
-            pFilter.datefrom = von;
-            pFilter.dateto = bis;
-            error = false;
-        }
-    }
-    while ( error )
-    return pFilter;
-}
-
-/*
- * Setzt den Event-Filter
- *
- * @param {Object} pFilter req
- *
- * @return {image}
- */
-function filterEvent_Neon( pFilter )
-{
-    var error = true;
-    var von = pFilter.datefrom;
-    var bis = pFilter.dateto;
-    if ( pFilter == "" )	pFilter =  reset_filterEvent();
-    do
-    {
-        var buttons = {
-            "ok" : translate.text("OK"),
-            "": translate.text("Abbrechen")
-        };
-        var defaultButton = "ok";
-
-        var prompts = {
-            FILTER_TEXT: translate.text("Bitte Filterbedingungen setzen"),
-            RESPONSIBLE_APPOINTMENT: pFilter.user,
-            DATE_FROM: pFilter.datefrom,
-            DATE_TO: pFilter.dateto,
-            CATEGORY_APPOINTMENT: pFilter.category,
-            TENTATIVE: pFilter.tentative,
-            CONFIRMED: pFilter.confirmed,
-            CANCELLED: pFilter.cancelled
-        }
-        var res = question.openDialog("DLG_FILTER_APPOINTMENT_Neon", prompts, buttons, defaultButton);
-        if( res.button != null )
-        {
-            pFilter.user =      res.RESPONSIBLE_APPOINTMENT;
-            pFilter.datefrom =  res.DATE_FROM;
-            pFilter.dateto =    res.DATE_TO;
-            pFilter.category =  res.CATEGORY_APPOINTMENT;
-            pFilter.tentative = res.TENTATIVE;
-            pFilter.confirmed = res.CONFIRMED;
-            pFilter.cancelled = res.CANCELLED;
-            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
-            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
-        }
-        else
-        {
-            pFilter.datefrom = von;
-            pFilter.dateto = bis;
-            error = false;
-        }
-    }
-    while ( error )
-    return pFilter;
-}
-
-/*
- * Anzeige des Event-Filter
- *
- * @param {Object} pFilter req
- *
- * @return string Anzeige
- */
-function show_filterEvent(pFilter)
-{
-    var retstring = "";
-
-    var userp = tools.getUser( pFilter.user )[tools.PARAMS];
-    if (pFilter.user != "") retstring = translate.text("Termine von") + " " + userp[tools.FIRSTNAME] + " " + userp[tools.LASTNAME];
-    if (pFilter.datefrom != "") retstring += ", " + datetime.toDate(pFilter.datefrom, translate.text("dd.MM.yyyy"));
-    if (pFilter.dateto != "") retstring += " - " + datetime.toDate(pFilter.dateto, translate.text("dd.MM.yyyy"));
-    if (pFilter.category == "true") retstring += ", " + translate.text("Kategorie") + " " + pFilter.category;
-    if (pFilter.tentative == "true") retstring += ", " + translate.text("Vorläufig");
-    if (pFilter.confirmed == "true") retstring += ", " + translate.text("Bestätigt");
-    if (pFilter.cancelled == "true") retstring += ", " + translate.text("Abgesagt");
-
-    return retstring
-}
-
-/*
- * Setzt den Event-Filter zurück
- *
- * @return {filter}
- */
-function reset_filterEvent()
-{
-    var today = getDate (vars.getString("$sys.date"));
-
-    return pFilter =  {
-        user: vars.getString("$sys.user"),
-        datefrom: String(today), //nur die Termine ab heute anzeigen,
-        //die von vor einer Woche sind uninteressant
-        dateto: String(eMath.addInt(eMath.addInt(today, datetime.ONE_WEEK)
-            ,datetime.ONE_DAY - datetime.ONE_MINUTE)),
-        category: "",
-        tentative: "true",
-        confirmed: "true",
-        cancelled: "",
-        free: "true"
-    };
-}
-
-/*
- * Setzt den Aufgaben-Filter in Tab Aufgaben
- *
- * @return {image}
- */
-function filterLinkedToDo()
-{
-    var filtervalues = ["", "false"];
-    vars.set("$local.CalenderUser", getCalenderUser( calendars.RIGHT_READ_TASK ));
-
-    //Vorbelegen der Werte, wenn bereits gewählt wurde:
-    if(vars.exists("$image.FilterValuesT"))
-    {
-        filtervalues = vars.get("$image.FilterValuesT");
-        vars.set("$local.relation_id", filtervalues[0]);
-        vars.set("$local.done", filtervalues[1]);
-    }
-
-    var res = swing.askUserQuestion(translate.text("Bitte Filterbedingungen setzen"), "DLG_TASK_DATE_LINKED_FILTER");
-
-    if( res != null && res != undefined && res != "")
-    {
-        filtervalues[0] = res["DLG_TASK_DATE_LINKED_FILTER.relation_id"];
-        filtervalues[1] = res["DLG_TASK_DATE_LINKED_FILTER.done"]
-    }
-    vars.set("$image.FilterValuesT", filtervalues );
-
-    return(filtervalues);
-}
-
-/*
- * Setzt den Aufgabe-Filter zurück
- *
- * @return {image}
- */
-function resetfilterLinkedToDo()
-{
-    var filtervalues = ["", "false"];
-
-    vars.set("$image.FilterValuesT", filtervalues );
-
-}
-
-/*
- * setzt die Kalenderrechte
- *
- * @return {void}
- */
-function setCalendarGrant()
-{
-    calendars.resetCalendarUser();
-    var user_read_todo = [];
-    var user_write_todo = [];   // ["Admin"]
-    var user_read_event = [];
-    var user_write_event = [];
-    var tree = {};
-    var data = db.table("select THEMEID, THEME.THEME_ID, LOGIN, STATUS from THEME "
-        + " left join EMPLOYEE on EMPLOYEE.THEME_ID = THEMEID left join RELATION on RELATION_ID = RELATIONID and STATUS = 1"
-        +" where KIND = 2");
-    for ( let i = 0; i < data.length; i++)
-    {
-        if ( tree[data[i][0]] == undefined )    tree[data[i][0]] = {
-            pid: data[i][1],
-            isuser: false
-        };
-        if ( data[i][2] != "" && data[i][3] != "" )     tree[data[i][2]] = {
-            pid: data[i][0],
-            isuser: true
-        };
-    }
-
-    var user = vars.getString("$sys.user");
-    // Lese- und Schreibrechte auf Kalender aus Datentabelle holen
-    data = db.table("select HASRIGHTFOR, TODO_RIGHTS, EVENT_RIGHTS from AOSYS_CALENDAR_RIGHTS where LOGIN = '" + user + "'");
-    for ( var i = 0; i < data.length; i++ )
-        if(tree[data[i][0]] != undefined)
-            tree[data[i][0]].grants = data[i].slice(1);
-
-    for ( login in tree )
-    {
-        if( tree[login].isuser )
-        {
-            var grantstodo = __getGrants( login, 0 );
-            var grantsevent = __getGrants( login, 1 );
-            if ( grantstodo.length + grantsevent.length > 0 )
-            {
-                if ( grantstodo == "1" || grantstodo == "3")  user_read_todo.push(login);
-                if ( grantstodo == "2" || grantstodo == "3")  user_write_todo.push(login);
-                if ( grantsevent == "1" || grantsevent == "3")  user_read_event.push(login);
-                if ( grantsevent == "2" || grantsevent == "3")  user_write_event.push(login);
-            }
-        }
-    }
-    calendars.setCalendarUser(user_read_todo, calendars.RIGHT_READ_TASK, true, calendars.SORTSTRATEGY_NATURAL );
-    calendars.setCalendarUser(user_write_todo, calendars.RIGHT_WRITE_TASK, true, calendars.SORTSTRATEGY_NATURAL );
-    calendars.setCalendarUser(user_read_event, calendars.RIGHT_READ_APPOINTMENT, true, calendars.SORTSTRATEGY_NATURAL );
-    calendars.setCalendarUser(user_write_event, calendars.RIGHT_WRITE_APPOINTMENT, true, calendars.SORTSTRATEGY_NATURAL );
-
-    //********** Ressourcen in Kalender einfügen ************
-    var ressource = tools.getUsersWithRole("PROJECT_Ressource");
-    calendars.setCalendarUser(  ressource, calendars.RIGHT_READ_APPOINTMENT | calendars.RIGHT_WRITE_APPOINTMENT  );
-
-    //add all users from support-role since support-action-tasks (sp_supportAktionen) generates tasks for all support-role members
-    //and if you're a member you are allowed to edit these
-    if (tools.hasRole(user, "PROJECT_Support"))
-    {
-        var support = tools.getUsersWithRole("PROJECT_Support");
-        calendars.setCalendarUser(  support, calendars.RIGHT_WRITE_TASK | calendars.RIGHT_READ_TASK  );
-    }
-
-    function __getGrants( pKey, pIndex )
-    {
-        var grants = [];
-        if ( tree[pKey].grants != undefined && tree[pKey].grants[pIndex]) grants = tree[pKey].grants[pIndex];
-        else if ( tree[pKey].pid != "" )   grants = __getGrants( tree[pKey].pid, pIndex );
-        return grants;
-    }
-}
-
-/*
- * setzt Recht für alle Kalender
- *
- * @return {void}
- */
-function setAllCalendarGrant()
-{
-    calendars.resetCalendarUser();
-    var users = tools.getStoredUsers();
-    var calendar_user = [];
-    for ( var i = 0; i < users.length; i++ )    calendar_user.push( users[i][1] );
-    calendars.setCalendarUser(calendar_user, calendars.RIGHT_READ_TASK | calendars.RIGHT_WRITE_TASK, false, calendars.SORTSTRATEGY_NATURAL );
-}
-
-/*
- * gibt die Logins der user, die den übergebenen User als Attribute eingetragen haben, zurück
- *
- * @param {[]} pUsers req Logins
- * @param {String []} pAttrName req AttributeName für Employee
- * @param {String []} pFields opt
- *
- * @return {String []} Logins
- */
-function getUsersbyAttr( pUsers, pAttrName, pFields )
-{
-    if (typeof(pAttrName) == "string") pAttrName = [pAttrName]
-
-    if ( pFields == undefined )
-    {
-        pFields = ["LOGIN"];
-    }
-
-    var sqlstr = "select " + pFields.join(", ") + " from ATTRLINK join ATTR on ATTRLINK.ATTR_ID = ATTRID and OBJECT_ID = 12 "
-    + "and ATTRNAME in ('" + pAttrName.join("','") + "') "
-    + " join EMPLOYEE on EMPLOYEEID = ATTRLINK.ROW_ID join RELATION on RELATION_ID = RELATIONID join PERS on PERS_ID = PERSID"
-    + " where VALUE_ID in (select EMPLOYEEID from EMPLOYEE where LOGIN in ('" + pUsers.join("','") + "'))"
-    + "";
-
-    if(pFields.length == 1)
-        return db.array(db.COLUMN, sqlstr);
-    else
-        return db.table(sqlstr);
-}
-
-/*
- * Gibt die Anzahl der verknüpften Aufgaben und Termine zurück.
- *
- * @param {String} pID req
- * @param {String} pFrame req
- *
- * @return {String} text
- */
-function countLinkedTodoEvent(pID, pFrame)
-{
-    var today = getDate (vars.getString("$sys.date"));
-    var datefrom = String(today);
-    var dateto = String(eMath.addInt(today, datetime.ONE_WEEK));
-
-    var str = "select count(distinct ELEMENTUID) from ASYS_CALENDARLINK join ASYS_CALENDARBACKEND on ELEMENTUID = ASYS_CALENDARLINK.ENTRYID "
-    + " where FRAME = 'comp." + pFrame + "' and ELEMENTUID is not null and DBID = '" + pID + "'";
-    var rettask = db.cell(str + "and ENTRYTYPE = " + calendars.VTODO + " and STATUS in ('"
-        + mapCalendarStatus(calendars.STATUS_NEEDSACTION, calendars.getBackendTypeTasks() ) + "', '"
-        + mapCalendarStatus(calendars.STATUS_INPROCESS, calendars.getBackendTypeTasks() ) + "')");
-
-    var eventStatusList = [
-         mapCalendarStatus(calendars.STATUS_CONFIRMED, calendars.getBackendType() )
-        ,mapCalendarStatus(calendars.STATUS_TENTATIVE, calendars.getBackendType() )
-    ];
-
-    if (calendars.getBackendType() == calendars.BACKEND_EXCHANGEWS)
-        eventStatusList.push(calendars.STATUS_FREE);
-
-    var retevent = db.cell([str + " and ENTRYTYPE = " + calendars.VEVENT + " and STATUS in ('" + eventStatusList.join("', '") + "')"
-            + " and DTSTART >= ? and DTEND <= ?",
-        [ [ String(datefrom), SQLTYPES.DATE ],  [String(dateto), SQLTYPES.DATE ] ]]);
-
-    result.string(translate.withArguments("&Aufg / Term (%0/%1)", [rettask, retevent]));
-}
-
-/*
- * Gibt die Anzahl der verknüpften Aufgaben zurück.
- *
- * @param {String} pID req
- * @param {String} pFrame req
- *
- * @return {String} text
- */
-function countLinkedTodo(pID, pFrame)
-{
-    var str = "select count(distinct ELEMENTUID) "
-    + "from ASYS_CALENDARLINK "
-    + "join ASYS_CALENDARBACKEND on ELEMENTUID = ASYS_CALENDARLINK.ENTRYID "
-    + " where FRAME = 'comp." + pFrame + "' "
-    + "and ELEMENTUID is not null and DBID = '" + pID + "'";
-
-    var rettask = db.cell(str + "and ENTRYTYPE = " + calendars.VTODO
-        + " and  STATUS in ('"
-        + mapCalendarStatus(calendars.STATUS_NEEDSACTION, calendars.getBackendTypeTasks() ) + "', '"
-        + mapCalendarStatus(calendars.STATUS_INPROCESS, calendars.getBackendTypeTasks() ) + "')"
-        + "");
-
-    result.string(translate.withArguments("&Aufgaben (%0)", [rettask]));
-}
-
-/*
- * Gibt die Überschneidungen von Termine zurück.
- *
- * @param {Date} pStart req
- * @param {Date} pEnd req
- * @param {Array} pUsers req
- *
- * @return {String Array} Termine
- */
-function getOverlappingEvents(pStart, pEnd, pUsers  )
-{
-    var resultEvents = new Array();
-    var users = pUsers;
-    if (calendars.getBackendType() == calendars.BACKEND_DB && pStart != "" && pEnd != "" && users.length > 0)
-    {
-        calendars.clearCache();
-        for (var u = 0; u < users.length; u++)
-        {
-            var localuid = vars.get("$image.entry")[calendars.ID]
-            var condition = new Array();
-            condition["COUNT"] = "1";
-            condition["TYPE_1"] = calendars.VFREEBUSY;
-            condition["USER_1"] = users[u][0];
-            condition["START_1"] = pStart;
-            condition["END_1"] = pEnd;
-
-            var fbsall = calendars.getEntries(condition);
-            for (var j = 0; j < fbsall.length; j++)
-            {
-                var fbs = fbsall[j];
-                for (var i = 0; i < fbs.length; i++)
-                {
-                    var next = fbs[i];
-                    var uid = next[calendars.ID];
-                    var sum = next[calendars.SUMMARY];
-                    if (uid != localuid)
-                    {
-                        var freebusy = next[calendars.FREEBUSY];
-                        var freebusyDec = text.decodeMS(freebusy);
-                        var match = false;
-                        for (var k = 0; k < freebusyDec.length; k++)
-                        {
-                            var freebusyInstance = text.decodeMS(freebusyDec[k]);
-                            if (!freebusyInstance[0].equals("FREE"))
-                            {
-                                match = true;
-                            }
-                        }
-                        if (match)  resultEvents.push([users[u][2], sum != null && sum.length > 0 ? sum : "?", uid]);
-                    }
-                }
-            }
-        }
-    }
-    return resultEvents;
-}
-
-/*
- * Überprüft den Eintrag, wenn eine neue Aufgaben angelegt wird darauf, ob private Aufgaben für andere erstellt werden
- *
- * @param {String[]} pUser die Liste der User, denen die Aufgabe zugewiesen werden soll
- * @param {String} pClassification die Klassifizierung der Aufgabe - "PRIVATE"
- *
- * @return {Boolean} ob der Eintrag in der Konstellation möglich ist, wenn nicht wird eine Meldung ausgegeben
- */
-function checkEntry(pUser, pClassification )
-{
-    if( pClassification == "PRIVATE" &&
-        ( pUser.length > 1 || text.decodeMS(pUser[0][0])[1] != "CN:" + vars.getString("$sys.user")))
-        {
-        question.showMessage(translate.text("Eine private Aufgabe kann nicht jemand anderem zugewiesen werden."));
-        return false;
-    }
-    else
-    {
-        return true;
-    }
-}
-
-/*
- * Verschiebt ein Calendareintrag
- *
- * @param {String[]} pIDs req die zu verschiebenden Ids mit den Calenderinformationen (Multistring)
- * @param {String} pTblCompName  opt die Komponente die aktualisiert werden soll
- *
- * @return {void}
- */
-function shiftEntry(pIDs, pTblCompName)
-{
-    var datenew = swing.askUserQuestion(translate.text("Verschieben auf Datum?"), "DLG_DATE");
-    if (datenew != null )
-    {
-        var grantedUsers = calendars.getDisplayCalendarUsers(calendars.RIGHT_WRITE_TASK);
-        var usermap = {};
-        var granted = true;
-        for (let i = 0; i < grantedUsers.length; i++)
-        {
-            usermap[grantedUsers[i][1]] = true;
-        }
-
-        datenew = datetime.clearTime(datenew["DLG_DATE.Edit_date"]);
-        if (datenew <= vars.getString("$sys.today"))
-            question.showMessage(translate.text("nur Verschiebung in die Zukunft erlaubt!"));
-        else
-        {
-            for (var i = 0, j = pIDs.length; i < j; i++)
-            {
-                var id = text.decodeMS(pIDs[i]);
-                var entry = calendars.getEntry(id[0], null, id [1], calendars.VTODO);
-                var affectedUsers = entry[calendars.ATTENDEES];
-
-                granted =  hasGrantForEntryByObject(affectedUsers, usermap);
-                if(granted)
-                {
-                    //Zeitdifferenz von Aufgabenstart und -ende
-                    var dateDiff  = eMath.subInt(entry[calendars.DUE], entry[calendars.DTSTART]);
-
-                    //Startzeit der Aufgabe
-                    var startTime = eMath.subInt(entry[calendars.DTSTART]
-                        , datetime.clearTime(entry[calendars.DTSTART]));
-
-                    entry[calendars.DTSTART] = eMath.addInt(datenew, startTime);
-                    entry[calendars.DUE] = eMath.addInt(entry[calendars.DTSTART], dateDiff);
-                    calendars.update( [ entry ] );
-                }
-                else
-                    question.showMessage(translate.text("Keine Berechtigung zum Verschieben der Aufgabe"));
-            }
-        }
-    }
-}
-
-/*
- * Gibt eine Aufgabe weiter
- *
- * @param {String} pIDs die ID der Aufgabe, welche weitergegeben werden soll
- * @param {String} pTblCompName die Komponente der Aufgaben, die aktualisiert werden soll
- *
- * @return {void}
- */
-function handOverToDo(pIDs, pTblCompName)
-{
-    var calendar_user = "";
-    var users = getCalenderUser( calendars.RIGHT_WRITE_TASK );
-    var publicCount = 0;
-    var privateCount = 0;
-    var notGrantedCount = 0;
-
-    var grantedUsers = calendars.getDisplayCalendarUsers(calendars.RIGHT_WRITE_TASK);
-    var userMap = {};
-    var granted = true;
-    for (let i = 0; i < grantedUsers.length; i++)
-    {
-        userMap[grantedUsers[i][1]] = true;
-    }
-
-    if (pIDs.length == 1)  // only one todo and private
-    {
-        var id = text.decodeMS( pIDs[0] );
-        var entry = calendars.getEntry( id[0], null, id[1] );
-        if(entry[calendars.CLASSIFICATION] == calendars.CLASSIFICATION_PRIVATE)
-        {
-            question.showMessage(translate.text("Kein Weitergeben von privaten Aufgaben möglich!"));
-            return;
-        }
-    }
-
-    for ( let i = 0; i < users.length; i++)  if (pIDs.length > 0 || users[i][0] != id[1])   calendar_user += "|" + users[i][1];
-    var selection = swing.askQuestion(translate.text("Benutzer auswählen"), swing.QUESTION_COMBOBOX, calendar_user);
-    if (selection != null)
-    {
-        for ( let i = 0; i < users.length; i++)
-        {
-            if ( selection == users[i][1] )
-            {
-                selection = users[i];
-                break;
-            }
-        }
-        for( let i = 0; i < pIDs.length; i++)
-        {
-            let id = text.decodeMS( pIDs[i] );
-            let entry = calendars.getEntry( id[0], null, id[1] );
-            if(entry[calendars.CLASSIFICATION] == calendars.CLASSIFICATION_PRIVATE)  privateCount++;
-            else //Nur öffentliche Aufgabe kann weiter gegeben werden.
-            {
-                // User austauschen
-                var usermap = entry[calendars.ATTENDEES];
-                var affectedusers = [];
-
-                granted =  hasGrantForEntryByObject(usermap, userMap);
-
-                for (var ui = 0; ui < usermap.length; ui++)
-                {
-                    var login_cn = usermap[ui]["cn"];
-                    var user = tools.getUserByAttribute(tools.CALENDARID, usermap[ui]["paramvalue"].substr("mailto:".length))
-                    if (user == null)  user = login_cn;
-                    else user = user[tools.TITLE]
-
-                    if ( user == id[1] )   affectedusers.push(selection[0]);
-                    else  if ( user != selection[0] )  affectedusers.push(user);
-                }
-                if(granted)
-                {
-                    entry[calendars.AFFECTEDUSERS] = text.encodeMS(calendars.getCalendarUsers(affectedusers));
-                    calendars.update([entry]);
-                    publicCount++;
-                }
-                else notGrantedCount++
-            }
-        }
-        question.showMessage(translate.withArguments("%0 Aufgabe(n) erfolgreich weitergegeben an: %1"
-            + (privateCount == 0 ? "" : "\n%2 private Aufgabe(n) können nicht weitergegeben werden.")
-            +(notGrantedCount == 0 ? "" : "\n%3 Aufgabe(n) können aufgrund fehlender Berechtigung nicht weitergegeben werden."),
-            [publicCount, selection[1], privateCount, notGrantedCount]));
-
-    }
-}
-
-// ToDo prüfen !!
-/*
- * Verschiebt die Kalendereinträge bei Login-Änderung
- *
- * @param {String} pNewlogin req
- * @param {String} pOldlogin req
- * @param {String} pNewCalendarID req
- * @param {String} pOldCalendarID req
- *
- * @return {void}
- */
-function moveCalendarData ( pNewlogin, pOldlogin, pNewCalendarID, pOldCalendarID )
-{
-    var newcaluser = text.encodeMS(["mailto:" + pNewCalendarID, "CN:" + pNewlogin]);
-    var oldcaluser = text.encodeMS(["mailto:" + pOldCalendarID, "CN:" + pOldlogin]);
-    db.runStatement("update ASYS_CALENDARBACKEND set OWNER = '" + newcaluser + "' where OWNER = '" + oldcaluser + "'");
-    db.runStatement("update ASYS_CALENDARBACKEND set ORGANIZER = '" + newcaluser + "' where ORGANIZER = '" + oldcaluser + "'");
-
-    if(pNewCalendarID != pOldCalendarID) //Nur wenn sich die E-Mailadresse geändert hat
-    {
-        //Messenger-Historien
-        db.runStatement("update ASYS_XMPP_HISTORY set JID_FROM = '" + pNewCalendarID +"' where JID_FROM = '" + pOldCalendarID +"'", "_____SYSTEMALIAS");
-        db.runStatement("update ASYS_XMPP_HISTORY set JID_TO = '" + pNewCalendarID +"' where JID_TO = '" + pOldCalendarID +"'", "_____SYSTEMALIAS");
-    }
-}
-
-/*
- * Löst den CalenderUser in lesbarer From auf
- *
- * @param {String} pValue req
- *
- * @return {String} übersetzten Wert
- */
-function getCalUser( pValue )
-{
-    var realname = pValue;
-    var user = tools.getUser( text.decodeMS(pValue)[1].split(":")[1] );
-    if ( user != null )
-    {
-        realname = user[tools.PARAMS][tools.FIRSTNAME] + " " + user[tools.PARAMS][tools.LASTNAME];
-    }
-    return realname;
-}
-
-/*
- * Gibt den Login eines CalenderUser zurück
- *
- * @param {String} pCalendarUser req
- *
- * @return {String} Title
- */
-function getTitleCalenderUser( pCalendarUser )
-{
-    var data = text.decodeMS(pCalendarUser)
-    for ( var i = 0; i < data.length; i++ )
-    {
-        //if login changes we have to check calendarid
-        if ( data[i].substr(0, "mailto:".length).toUpperCase() == "MAILTO:" )
-        {
-            var user = tools.getUserByAttribute(tools.CALENDARID, [data[i].substr("mailto:".length)]);
-            if (user != null )
-                return user[tools.TITLE];
-        }
-
-        if ( data[i].substr(0, 3).toUpperCase() == "CN:" )
-            return data[i].substr(3);
-    }
-    return "";
-}
-
-
-/*
- * Gibt den richtigen Status zum Prüfen je nach Backend zurück
- *
- *
- * @param {String} pStatus req die konstante für den zu prüfenden status,
- *                             z.B. calendars.STATUS_INPROCESS
- *
- * @param {String} pCalendarType req die konstante für den typen des Termin- oder Aufgabenbackends,
- *                             z.B. calendars.BACKEND_DB
- *
- * @return {String} Konstanten für den Kalender (Backend-Typen), gibt es den status im backend nicht
- *                  wird null geliefert
- */
-function mapCalendarStatus(pStatus, pCalendarType)
-{
-    switch (pCalendarType)
-    {
-        //case calendars.BACKEND_EXCHANGE:
-        case calendars.BACKEND_EXCHANGEWS:
-            if (pStatus == calendars.STATUS_CONFIRMED)
-                return calendars.STATUS_BUSY;
-            else
-                return pStatus;
-        default:
-            if (pStatus == calendars.STATUS_OOF)//nur bei exchange
-                return null;
-            else
-                return pStatus;
-    }
-}
-
-/*
- * Sets the imagevariable with affectedusers
- *
- * @param {Object} pEntry req the Entry of Tasks or Appointments
- *
- * @return {void}
- *
- */
-function setAffectedUsersImage(pEntry)
-{
-    var affectedUsers = [];
-    var usermap = pEntry[calendars.ATTENDEES];
-    var realnames = getRealNameObject(usermap);
-
-    for ( var i = 0; i < usermap.length; i++ )
-    {
-        affectedUsers.push([ text.encodeMS( [usermap[i]["paramvalue"], "CN:" + usermap[i]["cn"]] ), realnames[usermap[i]["cn"]] ]);
-    }
-
-    vars.set("$image.affectedusers", affectedUsers);
-    return affectedUsers;
-}
-
-/*
- * Opens calendar links
- *
- * @param {String} pEntryID req the ID of the link
- *
- * @return {void}
- *
- */
-function openCalendarLinks(pEntryID)
-{
-    var openFramesObj = {};
-    if (typeof(pEntryID) == "object")
-        openFramesObj = pEntryID;
-    else
-    {
-        var links = db.table("SELECT FRAME, DBIDCOLUMN, DBID FROM ASYS_CALENDARLINK WHERE ENTRYID = '" + pEntryID + "'");
-        for (var i = 0; i < links.length; i++)
-        {
-            var frame = links[i][0].substr( 5 );//remove comp. so the frame can be opened with openFrame
-            if ( openFramesObj[frame] == undefined ) openFramesObj[frame] = {
-                IDField: links[i][1],
-                IDs: []
-            };
-            openFramesObj[frame].IDs.push(links[i][2]);
-        }
-    }
-
-    for ( frame in openFramesObj)
-    {
-        var condition = openFramesObj[frame].IDField + " in ('" + openFramesObj[frame].IDs.join("', '") + "')";
-        var framemode = openFramesObj[frame].IDs.length > 1 ? swing.FRAMEMODE_TABLE_SELECTION : swing.FRAMEMODE_SHOW;
-
-        if ( vars.getString("$global.upwardLink") == "link")
-        {
-            swing.openLinkedFrame(frame, condition, swing.WINDOW_CURRENT, framemode, "", null, false, {
-                autoclose: true
-            } );
-        }
-        else
-        {
-            swing.openFrame(frame, condition, swing.WINDOW_CURRENT, framemode, null, false);
-        }
-    }
-}
-
-/**
- * Returns the "real" calendar system/backend type (BackendType & SyncBackendType)
- *
- * @param {Number} pScope - The needed scope (e.g. "calendars.VEVENT", "calendars.VTODO")
- * @return {Number} - The backend type (calendars.BACKEND_*)
- */
-function getCalendarSystemType (pScope)
-{
-    // Check sync backend type
-    if (calendars.getSyncBackendType() != calendars.BACKEND_NONE && calendars.getSyncBackendType() != 3)
-    {
-        var scope = calendars.getSyncBackendTypeScope();
-        if (scope.length == 1 && scope[0] == pScope) // Scope.length = 1 -> VEVENT *OR* VTODO
-            return calendars.getSyncBackendType();
-        else if (scope.length == 2) // Scope.length = 2 -> Both
-            return calendars.getSyncBackendType();
-       // Scope.length = 0 -> Nothing selected -> Skip this block
-    }
-
-    // Fallback to backend type (event)
-    if (calendars.getBackendType() != calendars.BACKEND_NONE && pScope === calendars.VEVENT)
-        return calendars.getBackendType();
-
-    // Second fallback to backend type (todo)
-    if (calendars.getBackendTypeTasks() != calendars.BACKEND_NONE && pScope === calendars.VTODO)
-        return calendars.getBackendTypeTasks();
-
-    // Everything is none
-    return calendars.BACKEND_NONE;
-}
-
-function hasGrantForEntryByMS(pAffectedUsers, pUserMap)
-{
-    for (let i = 0; i < pAffectedUsers.length; i++)
-    {
-        var calUser = getTitleCalenderUser( pAffectedUsers[i][0]);
-        if(pUserMap[calUser] == undefined)
-        {
-            return false;
-            break;
-        }
-        else
-            return true;
-    }
-}
-
-function hasGrantForEntryByObject(pAffectedUsers, pUserMap)
-{
-    for (let i = 0; i < pAffectedUsers.length; i++)
-    {
-        calUser = tools.getUserByAttribute(tools.CALENDARID, pAffectedUsers[i]["paramvalue"].substr("mailto:".length))
-        if (calUser == null)  calUser = pAffectedUsers[i]["cn"];
-        else calUser = calUser[tools.TITLE]
-        if(pUserMap[calUser] == undefined)
-        {
-            return false;
-            break;
-        }
-        else
-            return true;
-    }
-}
+import("system.neon");
+import("system.vars");
+import("system.db");
+import("system.translate");
+import("system.datetime");
+import("system.swing");
+import("system.eMath");
+import("system.calendars");
+import("system.logging");
+import("system.tools");
+import("system.text");
+import("system.question");
+import("system.SQLTYPES");
+import("system.result");
+import("Util_lib");
+import("system.util")
+
+
+/**
+ *  @class
+ **/
+function CalendarUtil(){}
+
+
+/*
+ * Erzeugt und öffnet ein neues Aufgabenobjekt (mit einem Link).
+ *
+ * @param {String} pSummary opt die Zusammenfassung
+ * @param {String} pDescription opt die Beschreibung
+ * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
+ * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
+ *                                   pWithLink[1]: ID des angezeigten Datensatzes
+ *                                   pWithLink[2]: Verknüpfungstitel
+ * @param {String} pUser opt der Benutzer ( Login )
+ * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
+ * @param {date} pStart opt Beginn der Aufgabe
+ * @param {date} pDuration opt Dauer
+ * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
+ * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
+ * @param {Array{[]} pComps4Refresh opt die zu aktualisierenden Komponenten
+ *
+ * @return {void}
+ */
+CalendarUtil.newTodo = function(pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh)
+{
+    var todo = createEntry( calendars.VTODO, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus );
+    var prompts = [];
+    prompts["comp4refresh"] = [];
+
+    if (pComps4Refresh == undefined)
+        pComps4Refresh = ["$comp.Aufgabe", "$comp.tbl_Aufgabe"];
+
+    for (var i = 0; i < pComps4Refresh.length; i++)
+    {
+        if ( vars.exists(pComps4Refresh[i]))    prompts["comp4refresh"].push(pComps4Refresh[i]);
+    }
+    if(vars.getString("$sys.scope") == "vaadin")
+        neon.openCalendarEntry([todo], null, neon.OPERATINGSTATE_NEW, null)
+    else
+    {
+        if (vars.exists("$sys.currentwindow"))
+            prompts["window"] = vars.getString("$sys.currentwindow");
+        if (vars.exists("$sys.currentimage"))
+            prompts["image"] = vars.getString("$sys.currentimage");
+
+        swing.openCalendarEntry([todo], null, false, prompts);
+    }
+}
+
+
+/**
+ * Finds the effective calendarId of an user in the same attribute order like the ADITO core, which is exchangeEmail -> calendarID -> email
+ * DO NOT CHANGE THIS ORDER
+ *
+ * @param {String} user to check
+ * @return effective calendar id
+ */
+CalendarUtil.getEffectiveCalendarIdFromUser = function(pUser)
+{
+    var userParams = pUser["params"];
+    
+    var resolvedCurrentUser;
+    var exchangeEmail = userParams["exchangeEMail"];
+    var calendarId = userParams["calendarID"];
+    var email = userParams["email"];
+    
+    if(exchangeEmail)
+        return exhangeEmail;
+    else if(calendarId)
+        return calendarId;
+    else if(email)
+        return email;
+    else
+        return "";
+}
+
+/*
+ * Erzeugt eine neue Aufgabe (mit einem Link).
+ *
+ * @param {String} pSummary opt die Zusammenfassung
+ * @param {String} pDescription opt die Beschreibung
+ * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
+ * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
+ *                		     pWithLink[1]: ID des angezeigten Datensatzes
+ *               		     pWithLink[2]: Verknüpfungstitel
+ * @param {String} pUser opt der Benutzer ( Login )
+ * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
+ * @param {date} pStart opt Beginn der Aufagebe
+ * @param {integer} pGroupType opt ( calendars.GROUP_SINGLE , calendars.GROUP_MULTI )
+ * @param {date} pDuration opt Dauer
+ * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
+ * @param {String} pStatus opt Status der Aufgabe ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
+ * @param {String} pPriority opt Priorität der Aufgabe
+ * @param {String} pReminder opt Erinnerung der Aufgabe
+ *
+ * @return {void}
+ */
+
+CalendarUtil.newSilentTodo = function(pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pGroupType, pCategory, pStatus, pPriority, pReminder)
+{
+    if ( pGroupType == undefined ) pGroupType = calendars.GROUP_SINGLE;
+    var todo = createEntry( calendars.VTODO, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pPriority, pReminder );
+    
+    return calendars.insert([todo],calendars.GROUP_SINGLE);
+}
+
+/*
+ * Erzeugt und öffnet ein neues Terminnobjekt mit einem Link.
+ *
+ * @param {String} pSummary opt die Zusammenfassung
+ * @param {String} pDescription opt die Beschreibung
+ * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
+ * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
+ *                		     pWithLink[1]: ID des angezeigten Datensatzes
+ *               		     pWithLink[2]: Verknüpfungstitel
+ * @param {String} pUser opt der Benutzer ( Login )
+ * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
+ * @param {date} pStart opt Beginn der Aufagebe
+ * @param {date} pDuration opt Dauer
+ * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
+ * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
+ * @param {Array{[]} pComps4Refresh opt die zu aktualisierenden Komponenten
+ * 
+ * @return {void}
+ */
+CalendarUtil.newEvent = function( pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh, pWorklistId)
+{
+    var event = createEntry( calendars.VEVENT, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus );
+
+    var prompts = [];
+    prompts["comp4refresh"] = [];
+    if (pComps4Refresh == undefined)
+        pComps4Refresh = ["$comp.Aufgabe", "$comp.tbl_Termine"];
+    for (i = 0; i < pComps4Refresh.length; i++)
+    {
+        if ( vars.exists(pComps4Refresh[i]))    prompts["comp4refresh"].push(pComps4Refresh[i]);
+    }
+
+    if(vars.getString("$sys.scope") == "vaadin")
+        neon.openCalendarEntry([event],"", neon.OPERATINGSTATE_NEW, null)
+    else
+    {
+        prompts["window"] = vars.getString("$sys.currentwindow");
+        prompts["image"] = vars.getString("$sys.currentimage");
+        if (pWorklistId != undefined)
+            prompts["worklistId"]   = pWorklistId;
+        swing.openCalendarEntry([event], null, false, prompts);
+    }
+}
+
+
+/*
+ * Erzeugt einen neuen Termineintrag (mit einem Link).
+ *
+ * @param {String} pSummary opt die Zusammenfassung
+ * @param {String} pDescription opt die Beschreibung
+ * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
+ * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
+ *                		     pWithLink[1]: ID des angezeigten Datensatzes
+ *                		     pWithLink[2]: Verknüpfungstitel
+ * @param {String} pUser opt der Benutzer ( Login )
+ * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( Login )
+ * @param {date} pStart opt Beginn des Termins
+ * @param {date} pDuration opt Dauer
+ * @param {integer} pGroupType opt ( calendars.GROUP_SINGLE , calendars.GROUP_MULTI )
+ * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
+ * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
+ * @param {String} pReminder opt Erinnerung des Termins
+ *
+ * @return {void}
+ */
+CalendarUtil.newSilentEvent = function( pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pGroupType, pCategory, pStatus, pReminder)
+{
+    if ( pGroupType == undefined ) pGroupType = calendars.GROUP_SINGLE;
+    var event = createEntry( calendars.VEVENT, pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, undefined, pReminder );
+    return calendars.insert( [event] , pGroupType );
+}
+
+/*
+ * Erzeugt ein neues Aufgaben- / Termin-Objekt (mit einem Link).
+ *
+ * @param {date} pType req  Augabe oder Termin ( calendars.VTODO, calendars.VEVENT )
+ * @param {String} pSummary opt die Zusammenfassung
+ * @param {String} pDescription opt die Beschreibung
+ * @param {Boolean} pWithLink opt TRUE legt eine Verknüpfung zu $image.frametable
+ * @param {String[][]} pWithLink opt pWithLink[0]: Name des Frames
+ *                		     pWithLink[1]: ID des angezeigten Datensatzes
+ *                		     pWithLink[2]: Verknüpfungstitel
+ * @param {String} pUser opt der Benutzer ( Login )
+ * @param {[]} pAffectedUsers opt die betroffenen Benutzer ( [ Login ] )
+ * @param {date} pStart opt Beginn
+ * @param {date} pDuration opt Dauer
+ * @param {integer} pCategory opt ( calendars.CATEGORIES , encoded(String) z.B.: text.encodeMS(["Service"]) )
+ * @param {String} pStatus opt Status des Termins ( calendars.STATUS_TENTATIVE, calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED )
+ * @param {String} pPriority opt Priorität
+ * @param {String} pReminder opt Erinnerung
+ *
+@return {Object} das EntryObjekt
+ */
+CalendarUtil.createEntry = function( pType, pSummary, pDescription, pWithLink, pAppLinkContext, pAppLinkId, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pPriority, pReminder )
+{
+    var Entry = {};
+    var framename;
+    var framdata;
+    var dbid;
+    var linktitle;
+    if ( pSummary == undefined || pSummary == null  ) pSummary = "";
+    if ( pDescription == undefined || pDescription == null )
+    {
+        if(vars.getString("$sys.scope") == "vaadin")
+            pDescription = neon.getImageContent(vars.getString("$sys.currententityname"));
+        else
+            pDescription = swing.getImageContent();
+    }
+    if ( pUser == undefined || pUser == null ) pUser = vars.getString("$sys.user");
+    //kein translate.key hier, weil es sich um einen rein technischen Wert handelt:
+    if ( pStart == undefined ) pStart = datetime.toLong(datetime.toDate(parseInt(vars.getString("$sys.date")) + datetime.ONE_HOUR, "dd.MM.yyyy HH:00"), "dd.MM.yyyy HH:mm");
+    if ( pCategory == undefined || pCategory == null  ) pCategory = "";
+
+    if (pAffectedUsers == null || pAffectedUsers == undefined )
+    {
+        Entry[calendars.AFFECTEDUSERS] = "";
+    }
+    else
+    {
+        Entry[calendars.AFFECTEDUSERS] = text.encodeMS(calendars.getCalendarUsers(pAffectedUsers));
+    }
+    Entry[calendars.TYPE] = pType;
+    Entry[calendars.DTSTART] = pStart;
+    if ( pType == calendars.VEVENT )
+    {
+        if ( pDuration == undefined )
+            pDuration = datetime.ONE_HOUR;
+
+        Entry[calendars.DTEND] = String ( eMath.addInt( pStart, pDuration) );
+
+        if ( pStatus == undefined )
+            pStatus = calendars.STATUS_CONFIRMED;
+
+        pStatus = mapCalendarStatus(pStatus, calendars.getBackendType() );
+    }
+    else if ( pType == calendars.VTODO )
+    {
+        //kein translate.key hier, weil es sich um einen rein technischen Wert handelt:
+        if ( pDuration != undefined )
+            Entry[calendars.DUE] = String ( eMath.addInt( pStart, pDuration) );
+        else
+            Entry[calendars.DUE] = datetime.toLong(datetime.toDate(pStart, "dd.MM.yyyy 23:59")
+                ,"dd.MM.yyyy HH:mm");
+
+        if ( pStatus == undefined )
+            pStatus = calendars.STATUS_NEEDSACTION;
+
+        pStatus = mapCalendarStatus(pStatus, calendars.getBackendTypeTasks() );
+        
+    }
+
+    Entry[calendars.USER] = calendars.getCalendarUser(pUser);
+    Entry[calendars.DESCRIPTION] = pDescription;
+    Entry[calendars.SUMMARY] = pSummary;
+    Entry[calendars.STATUS] = pStatus;
+    Entry[calendars.CLASSIFICATION] = calendars.CLASSIFICATION_PUBLIC;
+    Entry[calendars.CATEGORIES] = pCategory;
+    Entry[calendars.TRANSPARENCY] =  "OPAQUE";
+   
+
+    if( pPriority != undefined )
+    {
+        Entry[calendars.PRIORITY] = pPriority;
+    }
+
+    if( pReminder != undefined)
+    {
+        Entry[calendars.HASREMINDER] = "true";
+        Entry[calendars.REMINDER_DURATION] = pReminder;
+    }
+    else
+        Entry[calendars.HASREMINDER] = "false";
+   
+
+    if (pWithLink == false)
+    {
+        Entry[calendars.LINKS] = "0";
+    }
+    else
+    {
+        var fd = new FrameData();
+        if ( typeof(pWithLink) == "object" )
+        {
+            for ( var li = 0; li < pWithLink.length; li++ )
+            {
+                framename = pWithLink[li][0];
+                framdata = fd.getData("name", framename, ["table","idcolumn","title"])[0];
+                dbid = pWithLink[li][1];
+                linktitle = framdata[2] + " - " + pWithLink[li][2];
+
+                Entry["LINK_ALIAS_" + ( li + 1 )] = vars.getString("$sys.dbalias");
+                Entry["LINK_TABLE_" + ( li + 1 )] = framdata[0];
+                Entry["LINK_IDCOLUMN_" + ( li + 1 )] = framdata[1];
+                Entry["LINK_DBID_" + ( li + 1 )] = dbid;
+                Entry["LINK_FRAME_" + ( li + 1 )] = "comp." + framename;
+                Entry["LINK_TITLE_" + ( li + 1 )] = linktitle;
+            }
+            Entry[calendars.LINKS] = pWithLink.length.toString();
+        }
+        else
+        {
+            if ( pWithLink == true || pWithLink == undefined )
+            {
+                framename = vars.getString("$sys.currentimagename");
+                framdata = fd.getData("name", framename, ["table","idcolumn","title"])[0];
+                dbid = vars.getString("$comp.idcolumn");
+                linktitle = framdata[2] + " - " + swing.getImageContent();
+            }
+            Entry[calendars.LINKS] = "1";
+            Entry["LINK_ALIAS_1"] = vars.getString("$sys.dbalias");
+            Entry["LINK_TABLE_1"] = framdata[0];
+            Entry["LINK_IDCOLUMN_1"] = framdata[1];
+            Entry["LINK_DBID_1"] = dbid;
+            Entry["LINK_FRAME_1"] = "comp." + framename;
+            Entry["LINK_TITLE_1"] = linktitle;
+        }
+    }
+    
+
+if(pAppLinkContext && pAppLinkId)
+{
+    Entry["AppLinkContext"] = pAppLinkContext;
+    Entry["AppLinkId"] = pAppLinkId;
+}
+    return Entry;
+}
+
+/*
+ * Liefert den CalendarStatus übersetzt zurück.
+ * @param {String} pStatus req Status
+ * @param {String} pLanguage opt Sprache
+ * @param {String} pKind opt ToDo oder Event
+ *
+ * @return {String} übersetzte Status
+ */
+function getCalendarStatus( pStatus, pLanguage, pKind)
+{
+    //kein mappen des Status, da wirklich verschiedene Dinge angezeigt werden sollen
+    switch ( pStatus )
+    {
+        case calendars.STATUS_BUSY: 
+            return translate.text("Gebucht", pLanguage);
+        case calendars.STATUS_CANCELLED:
+            if(pKind == "ToDo" && pKind != undefined) return translate.text("Zurückgestellt", pLanguage)
+            return translate.text("Abgesagt", pLanguage);
+        case calendars.STATUS_COMPLETED:
+            return translate.text("Erledigt", pLanguage);
+        case calendars.STATUS_CONFIRMED:
+            return translate.text("Bestätigt", pLanguage);
+        case calendars.STATUS_FREE:
+            return translate.text("frei", pLanguage);
+        case calendars.STATUS_INPROCESS:
+            return translate.text("In Bearbeitung", pLanguage);
+        case calendars.STATUS_NEEDSACTION:
+            return translate.text("Nicht begonnen", pLanguage);
+        case calendars.STATUS_OOF:
+            return translate.text("Außer Haus", pLanguage);
+        case calendars.STATUS_TENTATIVE:
+            return translate.text("Vorläufig", pLanguage);
+        default:
+            return "";
+    }
+}
+
+/*
+ * Zu einer übergebenen Priorität wird ihre Bedeutung übersetzt und zurückgegeben.
+ *
+ * @param {String} pPriority req Priorität
+ * @param {String} pLanguage opt Sprache
+ *
+ * @return (String) übersetzte Bedeutung einer Priorität
+ */
+function getCalendarPriority(pPriority, pLanguage)
+{
+    switch(pPriority)
+    {
+        case "9":
+            return translate.text("niedrig", pLanguage);
+            break;
+        case "5":
+            return translate.text("normal", pLanguage);
+            break;
+        case "1":
+            return translate.text("hoch", pLanguage);
+            break;
+        default:
+            return translate.text("keine", pLanguage);
+            break;
+    }
+}
+
+/*
+ * Liefert zum Objekt verknüpfte Aufgaben aus dem Kalender.
+ *
+ * @param {String} pFrame req Name des Frames
+ * @param {String} pDBID req ID des verknüpften Datensatzes
+ * @param {String} pAlias opt
+ * @param {String} pLanguage opt Sprache
+ *
+ * @return {[]} mit Aufgaben aus Kalender
+ */
+function getLinkedToDos (pFrame, pDBID, pAlias, pLanguage )
+{
+    if (pAlias == undefined ) pAlias = vars.getString("$sys.dbalias");
+    var tab = [];
+    var status = " and STATUS in ('NEEDS-ACTION', 'IN-PROCESS')";
+    var exists = [];
+    var zustaendig = "";
+    var today = getDate(vars.getString("$sys.date"));
+    var filtervalues = ["", "false"];
+    if ( vars.exists("$image.FilterValuesT") )
+    {
+        filtervalues = vars.get("$image.FilterValuesT");
+        zustaendig = filtervalues[0]
+        if (filtervalues[1] == "true") status = " and STATUS in ('COMPLETED', 'CANCELLED')";
+    }
+
+    var entryids = db.table("select ENTRYID, OWNER from ASYS_CALENDARLINK "
+        + "join ASYS_CALENDARBACKEND on ELEMENTUID = ENTRYID where FRAME = 'comp." + pFrame + "' "
+        + "and ENTRYID is not null "
+        + "and ENTRYTYPE = " + calendars.VTODO + " "
+        + "and DBID = '" + pDBID + "' "
+        + status, pAlias);
+
+    for (var i = 0; i < entryids.length; i++)
+    {
+        if ( exists.indexOf(entryids[i][0]) == -1)
+        {
+            try
+                {
+                    var entry = calendars.getEntry(entryids[i][0], null, getTitleCalenderUser( entryids[i][1] ), calendars.VTODO);
+                    var entr = new Array;
+                    status = "";
+                    var user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
+                    if(user == null) user = entry[calendars.USER2]["cn"];
+                     else user = user[tools.TITLE]
+                    if ((user == zustaendig || zustaendig == ""))
+                    {
+                        entr[0] = text.encodeMS([entry[calendars.ID], user]);
+                        var due = getDate(entry[calendars.DUE]);
+                        if (due < today ) entr[1]	= "-1769402";
+                        else  if (due > today) entr[1]	= "-16777216"; else entr[1]	= "-16744020";
+                        entr[2] = "-1";  // Hintergrundfarbe
+                        entr[3] = text.decodeMS(entry[calendars.AFFECTEDUSERS]).length;
+                        entr[4] = entry[calendars.DUE]
+                        entr[5] = getCalendarStatus( entry[calendars.STATUS], pLanguage, "ToDo");
+                        entr[6] = entry[calendars.SUMMARY]
+                        entr[7] = getRealName([entry[calendars.ORGANIZER2]]);
+                        entr[8] = getRealName(entry[calendars.ATTENDEES]);
+                        entr[9] = entry[calendars.DESCRIPTION];
+                        entr[10] = entry[calendars.PRIORITY];
+                        tab.push(entr);
+                        exists.push(entryids[i][0]);
+                    }
+            }
+            catch (ex)
+            {
+                logging.log(ex);
+            }
+        }
+    }
+    array_mDimSort(tab, 4, false); //Sortierung nach Fälligkeitsdatum
+    return tab;
+}
+
+/*
+ * Anzeige des Aufgaben-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return string Anzeige
+ */
+function show_filterLinkedToDos(pFilter)
+{
+    var retstring = "";
+    if (pFilter[0] != "")
+    {
+        var userp = tools.getUser( pFilter[0] )[tools.PARAMS];
+        retstring = translate.text("Aufgaben von") + " " + userp[tools.FIRSTNAME] + " " + userp[tools.LASTNAME];
+    }
+    if (pFilter[1] == "true") retstring += ", " + translate.text("erledigt / zurückgestellt");
+
+    return retstring
+}
+
+/*
+ * Liefert zum Objekt verknüpfte Events aus dem Kalender.
+ *
+ * @param {String} pFrame req Name des Frames
+ * @param {String} pDBID req ID des verknüpften Datensatzes
+ * @param {Object} pFilter opt
+ * @param {String} pAlias opt
+ * @param {String} pUser opt Benutzer
+ *
+ * @return {[]} mit Events aus Kalender
+ */
+function getLinkedEvents (pFrame, pDBID, pFilter, pAlias, pUser )
+{
+    if ( pFilter == "" || pFilter == undefined)
+        pFilter = reset_filterEvent();
+
+    var tab = [];
+    var exists = [];
+    var conditions = [];
+    var today = getDate(vars.getString("$sys.date"));
+    var conditioncount = 0;
+    var stati = [];
+
+    if ( pFilter.tentative == "true" )
+        stati.push(mapCalendarStatus(calendars.STATUS_TENTATIVE, calendars.getBackendType() ));
+
+    if ( pFilter.cancelled == "true" )
+        stati.push(mapCalendarStatus(calendars.STATUS_CANCELLED, calendars.getBackendType() ));
+
+    if ( pFilter.confirmed == "true" )
+        stati.push(mapCalendarStatus(calendars.STATUS_CONFIRMED, calendars.getBackendType() ));
+
+    if (calendars.getBackendType() == calendars.BACKEND_EXCHANGEWS)
+        stati.push(calendars.STATUS_FREE);
+
+    if (pAlias == undefined ) pAlias = vars.getString("$sys.dbalias");
+
+    var entryids = db.table(["select ENTRYID, OWNER "
+        + "from ASYS_CALENDARLINK "
+        + "join ASYS_CALENDARBACKEND on ELEMENTUID = ENTRYID where FRAME = 'comp."
+        + pFrame + "' and ENTRYID is not null and ENTRYTYPE = " + calendars.VEVENT
+        + " and DBID = '" + pDBID + "' and DTSTART >= ?",
+        [ [ String(pFilter.datefrom - datetime.ONE_WEEK), SQLTYPES.DATE ]]], pAlias );
+
+    /*
+     * Check for rights before constructing condition otherwise you'll get an error by opening linked Data
+     */
+    var userToRead = []
+    if(pUser != undefined)
+    {
+        userToRead = getCalendarUsers( calendars.RIGHT_READ_APPOINTMENT, pUser );
+    }
+    else
+    {
+        userToRead = calendars.getDisplayCalendarUsers(calendars.RIGHT_READ_APPOINTMENT);
+    }
+    var userMap = {};
+    for (let i = 0; i < userToRead.length; i++)
+    {
+        userMap[userToRead[i][1]] = true;
+    }
+
+    for ( var i = 0;i < entryids.length; i++)
+    {
+        var user = getTitleCalenderUser(entryids[i][1]);
+        if(userMap[user])
+        {
+            for ( var z = 0; z < stati.length; z++ )
+                _addEntryCondition(conditions, String(++conditioncount),
+                {
+                    TYPE: calendars.VEVENT,
+                    START: pFilter.datefrom,
+                    END: pFilter.dateto,
+                    USER: user,
+                    STATUS: stati[z],
+                    UID: entryids[i][0]
+                });
+        } else continue
+    }
+    conditions["COUNT"] = String(conditioncount);
+
+    var entries = calendars.getExpandedEntries(conditions, pFilter.datefrom, pFilter.dateto);
+    for ( let i = 0; i < entries.length; i++)
+    {
+        for (var j = 0; j < entries[i].length; j++)
+        {
+            var entry = entries[i][j];
+            if( exists.indexOf(entry[calendars.ID]) == -1 && ( pFilter.category == "" || text.decodeMS(entry[calendars.CATEGORIES]).indexOf(pFilter.category) > 0))
+            {
+                var entr = new Array;
+                var start = getDate(entry[calendars.DTSTART]);
+                var end = getDate(entry[calendars.DTEND]);
+                user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
+                if(user == null) user = entry[calendars.USER2]["cn"];
+                 else user = user[tools.TITLE]
+                entr[0] = text.encodeMS([entry[calendars.ID], user , entry[calendars.RECURRENCEID]]);
+                if (end < today) entr[1]	= "-6710887" ;
+                else if (start <= today && end >= today )   entr[1] = "-16744020" ;
+                else entr[1] = "-16777216";
+                entr[2] = "-1"
+                entr[3] = text.decodeMS(entry[calendars.AFFECTEDUSERS]).length;
+                entr[4] = entry[calendars.DTSTART]
+                entr[5] = entry[calendars.DTEND]
+                entr[6] = entry[calendars.SUMMARY]
+                entr[7] = getRealName([entry[calendars.ORGANIZER2]]);
+                entr[8] = getRealName(entry[calendars.ATTENDEES]);
+                entr[9] = entry[calendars.DESCRIPTION]
+                tab.push(entr);
+                exists.push(entry[calendars.ID]);
+            }
+        }
+    }
+    array_mDimSort(tab, 4, false);
+    return tab;
+}
+
+/*
+ * Liefert Aufgaben aus dem Kalender.
+ *
+ * @param {Object} pFilter req
+ * @param {String} pLanguage opt
+ * @param {bool} pShortForm opt wenn true wird nur die kurzversion geladen
+ *
+ * @return {[]} mit allen aufgaben aus dem Kalender
+ */
+function getTodos( pFilter, pLanguage, pShortForm )
+{
+    if ( pFilter == "" )    pFilter =  reset_filterToDo();
+    var tab = [];
+    var today = getDate (vars.getString("$sys.date"));
+    var conditions = [];
+    var conditioncount = 0;
+    var user = pFilter.user;
+    var stati = [];
+    var status = [];
+    var exists = [];
+    var entries = [];
+
+    if ( pFilter.needs_action == "true" )
+    {
+        stati.push(mapCalendarStatus(calendars.STATUS_NEEDSACTION, calendars.getBackendTypeTasks() ));
+        status.push("NEEDS-ACTION");
+    }
+    if ( pFilter.in_process == "true" )
+    {
+        stati.push(mapCalendarStatus(calendars.STATUS_INPROCESS, calendars.getBackendTypeTasks() ));
+        status.push("IN-PROCESS");
+    }
+    if ( pFilter.completed == "true" )
+    {
+        stati.push(mapCalendarStatus(calendars.STATUS_COMPLETED, calendars.getBackendTypeTasks() ));
+        status.push("COMPLETED");
+    }
+    if ( pFilter.cancelled == "true" )
+    {
+        stati.push(mapCalendarStatus(calendars.STATUS_CANCELLED, calendars.getBackendTypeTasks() ));
+        status.push("CANCELLED");
+    }
+    if (pFilter.delegated == "true" )
+    {
+        var from = [pFilter.datefrom, SQLTYPES.TIMESTAMP];
+        var to = [pFilter.dateto, SQLTYPES.TIMESTAMP];
+        setAllCalendarGrant();
+        user = calendars.getCalendarUser(user);
+        user = db.table(["select ELEMENTUID, OWNER from ASYS_CALENDARBACKEND where ENTRYTYPE = 2 and OWNER != '" + user + "' and ORGANIZER = '"
+            + user + "' and STATUS in ('" + status.join("', '") + "') and (( STARTTIME >= ? and STARTTIME <= ?) or "
+            + "( ENDTIME >= ? and ENDTIME <= ? ) or ( STARTTIME >= ? and ENDTIME <= ? ))", [from, to, from, to, from, to]] );
+
+        for (let i = 0; i < user.length; i++ )
+        {
+            try
+            {
+                entries.push([calendars.getEntry(user[i][0], null, getTitleCalenderUser(user[i][1]), calendars.VTODO)]);
+            }
+            catch(err){
+                logging.log(err);
+            }
+        }
+        setCalendarGrant();
+    }
+    else
+    {
+        if ( typeof( pFilter.user ) != "object" )  user = [user.trim()];
+        for (let i = 0; i < user.length; i++ )
+        for ( var z = 0; z < stati.length; z++ )
+            _addEntryCondition(conditions, ++conditioncount,
+            {
+                TYPE: calendars.VTODO,
+                START: pFilter.datefrom,
+                END: pFilter.dateto,
+                USER: user[i],
+                STATUS: stati[z]
+            });
+
+        conditions["COUNT"] = String(conditioncount);
+        entries = calendars.getEntries(conditions);
+    }
+
+    for (i = 0; i < entries.length; i++)
+    {
+        var entry = entries[i][0];
+        user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
+        if (user == null)  user = entry[calendars.USER2]["cn"];
+         else user = user[tools.TITLE]
+        if ( !(user != vars.getString("$sys.user") && entry[calendars.CLASSIFICATION] == calendars.CLASSIFICATION_PRIVATE) // no privat
+            && !( pFilter.delegated == "true" && ( isAffectedUser( entry, pFilter.user) || exists.indexOf(entry[calendars.ID]) > -1  ) ) // no duplicate
+            && ( pFilter.category == "" || text.decodeMS(entry[calendars.CATEGORIES]).indexOf( pFilter.category ) > -1 ) ) // Filter category
+            {
+            var entr = [];
+            var links =  entry[calendars.LINKS];
+            var due = entry[calendars.DUE] != "" ? getDate(entry[calendars.DUE]) : "";
+            entr[0] = text.encodeMS([entry[calendars.ID], user]);
+            if (due == today )      entr[1] = "-16744020" ;
+            else if(due == "")      entr[1] = "-13395712";
+            else if (due > today )  entr[1] = "-16777216";
+            else                    entr[1]	= "-1769402";
+            if (entry[calendars.PRIORITY] == "1") entr[2] = "-100";
+            entr[3] = entry[calendars.ATTENDEES].length;
+            entr[4] = entry[calendars.DUE];
+
+            if (pShortForm)
+            {
+                entr[5] = entry[calendars.SUMMARY];
+                entr[6] = entry[calendars.DESCRIPTION];
+            }
+            else
+            {
+                entr[5] = getCalendarStatus( entry[calendars.STATUS], pLanguage, "ToDo");
+                entr[6] = getCalendarPriority( entry[calendars.PRIORITY], pLanguage);
+                entr[7] = entry[calendars.SUMMARY];
+                entr[8] = getRealName( [ entry[calendars.ORGANIZER2] ] );
+                entr[9] = getRealName( entry[calendars.ATTENDEES] );
+                if (links == undefined) entr[10] = "";	else entr[10] = links;
+                entr[11] = entry[calendars.DESCRIPTION];
+                entr[12] = entry[calendars.CREATED];
+            }
+
+            tab.push(entr);
+            if ( pFilter.delegated == "true" )  exists.push(entry[calendars.ID]);
+        }
+    }
+
+    if (pShortForm)
+        sortArray(tab, -1, 4, 1, 5);
+    else
+        sortArray(tab, -1, 4, 1, 12 );
+
+    return tab;
+}
+
+/*
+ * Fügt eine Condition hinzu
+ *
+ * @param {[]} pConditions req die Conditions
+ * @param {Integer} pIndex req Index der Condition
+ * @param {Object} pValues req
+ *
+ * @return {void}
+ */
+function _addEntryCondition(pConditions, pIndex, pValues)
+{
+    var params = ["TYPE", "START", "END", "USER", "STATUS", "UID"];
+
+    for (var i = 0; i < params.length; i++)
+        if (pValues[params[i]] != undefined)    pConditions[params[i] + "_" + pIndex] = pValues[params[i]];
+}
+CalendarUtil.addEntryCondition = function(pConditions, pIndex, pValues)
+{
+    _addEntryCondition(pConditions, pIndex, pValues);
+} 
+
+/*
+ * Liefert Events zu bestimmten Usern/Daten in einem Array.
+ *
+ * @param {Object} pFilter req
+ * @param {String} pLanguage opt
+ * @param {bool} pShortForm opt wenn true wird nur die kurzversion geladen
+ *
+ * @return {[]}
+ *				[0] ID
+ *				[1] Vordergrundfarbe
+ *				[2] Hintergrundfarbe
+ *				[3] Start
+ *				[4] Ende
+ *				[5] Betreff
+ *				[6] Inhalt
+ *				[7] User
+ *				[8] Anzahl Verknüpfungen
+ *				[9] Klassifikation (privat/öffentlich)
+ */
+function getEvents( pFilter, pLanguage, pShortForm )
+{
+    if ( pFilter == "" )  pFilter = reset_filterEvent();
+    var tab = [];
+    var conditions = [];
+    var today = getDate(vars.getString("$sys.date"));
+    var conditioncount = 0;
+    var stati = [];
+    var user = undefined;
+    if ( pFilter.tentative == "true" )    stati.push(calendars.STATUS_TENTATIVE);
+    if ( pFilter.cancelled == "true" )    stati.push(calendars.STATUS_CANCELLED);
+    if ( pFilter.confirmed == "true" )
+        stati.push(mapCalendarStatus(calendars.STATUS_CONFIRMED, calendars.getBackendType() ));
+
+    if (getCalendarSystemType(calendars.VEVENT) == calendars.BACKEND_EXCHANGEWS && pFilter.free == "true")
+        stati.push(calendars.STATUS_FREE);
+
+    if ( pFilter.user != "" )	user = (pFilter.user).trim();
+
+    for ( var z = 0; z < stati.length; z++ )
+        _addEntryCondition(conditions, String(++conditioncount),
+        {
+            TYPE: calendars.VEVENT,
+            START: pFilter.datefrom,
+            END: pFilter.dateto,
+            USER: user,
+            STATUS: stati[z]
+        });
+
+    conditions["COUNT"] = String(conditioncount);
+
+    var entries = calendars.getExpandedEntries(conditions, pFilter.datefrom, pFilter.dateto);
+    for ( var i = 0;i < entries.length; i++)
+    {
+        for (var j = 0; j < entries[i].length; j++)
+        {
+            var entry = entries[i][j];
+            if( pFilter.category == "" || text.decodeMS(entry[calendars.CATEGORIES]).indexOf(pFilter.category) > -1 )
+            {
+                var entr = new Array;
+                var start = getDate(entry[calendars.DTSTART]);
+                var end = getDate(entry[calendars.DTEND]);
+
+                user = tools.getUserByAttribute(tools.CALENDARID, entry[calendars.USER2]["paramvalue"].substr("mailto:".length))
+                if(user == null) user = entry[calendars.USER2]["cn"];
+                else user = user[tools.TITLE]
+                entr[0] = text.encodeMS([ entry[calendars.ID], user, entry[calendars.RECURRENCEID]]);
+                if (end < today ) entr[1]	="-6710887" ;
+                else if (start > today) entr[1]	= "-16777216";
+                else entr[1]	= "-16744020" ;
+                entr[2] = "-1"
+                entr[3] = entry[calendars.ATTENDEES].length;
+                entr[4] = entry[calendars.DTSTART];
+                entr[5] = entry[calendars.DTEND];
+                entr[6] = entry[calendars.SUMMARY];
+
+                if (!pShortForm)
+                {
+                    entr[7] = getRealName([entry[calendars.ORGANIZER2]]);
+                    entr[8] = getRealName(entry[calendars.ATTENDEES]);
+                    entr[9] = getCalendarStatus( entry[calendars.STATUS], pLanguage );
+                    if (entry[calendars.LINKS] == undefined) entr[10] = "";
+                    else entr[10] = entry[calendars.LINKS];
+                    entr[11] = entry[calendars.DESCRIPTION];
+                }
+                tab.push( entr );
+            }
+        }
+    }
+    sortArray(tab, -1, 4, 1, 6 );
+    return tab;
+}
+
+/*
+ * Liefert den echten Namen anhand des Logins zurück
+ *
+ * @param {Array}[]} pUserMap req pUserMap
+ *
+ * @return String
+ */
+function getRealName(pUserMap)
+{
+    var resultName = [];
+    var RealNames = getRealNameObject(pUserMap);
+
+    for ( var realname in RealNames )   resultName.push(RealNames[realname]);
+    return resultName.join(", \n");
+}
+
+/*
+ * Liefert den echten Namen anhand des Logins zurück
+ *
+ * @param {Array}[]} pUserMap req pUserMap
+ *
+ * @return Object
+ */
+function getRealNameObject(pUserMap)
+{
+    var resultObject = {};
+    var realname = "";
+
+    for ( let i = 0; i < pUserMap.length; i++ )
+    {
+        let user = tools.getUserByAttribute(tools.CALENDARID, [pUserMap[i]["paramvalue"].substr("mailto:".length)])
+        if ( user != null )
+        {
+            if(vars.exists("$global.firstLastName") && vars.get("$global.firstLastName"))
+                realname = user[tools.PARAMS][tools.LASTNAME] + " " + user[tools.PARAMS][tools.FIRSTNAME];
+            else realname = user[tools.PARAMS][tools.FIRSTNAME] + " " + user[tools.PARAMS][tools.LASTNAME];
+        }
+        else //Der User existiert nicht im System
+        {
+            realname = pUserMap[i]["cn"] + " " + pUserMap[i]["paramvalue"];
+        }
+        resultObject[pUserMap[i]["cn"]] = realname;
+    }
+    return resultObject;
+}
+
+
+/*
+ * Gibt an ob der User im Calendarobject vorhanden ist
+ *
+ * @param {Object} pEntry req Calendarobject
+ * @param {String} pUser req Title
+ *
+ * @return Object
+ */
+function isAffectedUser( pEntry, pUser)
+{
+    var usermap = pEntry[calendars.ATTENDEES];
+
+    for ( var i = 0; i < usermap.length; i++ )
+        if( usermap[i]["cn"] == pUser )
+            return true;
+    return false;
+}
+
+/*
+ * Liefert das Datum ohne Urzeit zurück
+ *
+ * @param {String} datetimeIn req DatumZeit
+ *
+ * @return {date}
+ */
+function getDate( datetimeIn )
+{
+    if ( datetimeIn != "")
+        return datetime.clearTime(datetimeIn);
+    else return "";
+}
+
+/*
+ * Setzt den Aufgaben-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return {image}
+ */
+function filterToDo( pFilter )
+{
+    var error = true;
+    var von = pFilter.datefrom;
+    var bis = pFilter.dateto;
+    if ( pFilter == "" )	pFilter =  reset_filterToDo();
+    do
+    {
+        vars.set("$local.relation_id", pFilter.user);
+        vars.set("$local.edt_von", pFilter.datefrom);
+        vars.set("$local.edt_bis", pFilter.dateto);
+        vars.set("$local.category", pFilter.category);
+        vars.set("$local.delegated", pFilter.delegated);
+        vars.set("$local.needs_action", pFilter.needs_action);
+        vars.set("$local.in_process", pFilter.in_process);
+        vars.set("$local.completed", pFilter.completed);
+        vars.set("$local.cancelled", pFilter.cancelled);
+        var res = swing.askUserQuestion(translate.text("Bitte Filterbedingungen setzen"), "DLG_TASK_FILTER");
+        if( res != null )
+        {
+            pFilter.user =      res["DLG_TASK_FILTER.relation_id"];
+            pFilter.datefrom =  res["DLG_TASK_FILTER.edt_von"];
+            pFilter.dateto =    res["DLG_TASK_FILTER.edt_bis"];
+            pFilter.category =  res["DLG_TASK_FILTER.category"];
+            pFilter.delegated = res["DLG_TASK_FILTER.delegated"];
+            pFilter.needs_action = res["DLG_TASK_FILTER.needs_action"];
+            pFilter.in_process = res["DLG_TASK_FILTER.in_process"];
+            pFilter.completed = res["DLG_TASK_FILTER.completed"];
+            pFilter.cancelled = res["DLG_TASK_FILTER.cancelled"];
+            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
+            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
+        }
+        else
+        {
+            pFilter.datefrom = von;
+            pFilter.dateto = bis;
+            error = false;
+        }
+    }
+    while ( error )
+    return pFilter;
+}
+
+/*
+ * Setzt den Aufgaben-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return {image}
+ */
+function filterToDo_Neon( pFilter )
+{
+    var error = true;
+    var von = pFilter.datefrom;
+    var bis = pFilter.dateto;
+    if ( pFilter == "" )	pFilter =  reset_filterToDo();
+    do
+    {
+        var prompts = {
+            FILTER_TEXT: translate.text("Bitte Filterbedingungen setzen"),
+            RESPONSIBLE: pFilter.user,
+            DATE_FROM: pFilter.datefrom,
+            DATE_TO: pFilter.dateto,
+            CATEGORY_TODO: pFilter.category,
+            DELEGATED: pFilter.delegated,
+            NEEDS_ACTION: pFilter.needs_action,
+            IN_PROCESS: pFilter.in_process,
+            COMPLETED: pFilter.completed,
+            CANCELLED: pFilter.cancelled
+        }
+
+        var buttons = {
+            "ok" : translate.text("OK"),
+            "": translate.text("Abbrechen")
+            };
+        var defaultButton = "ok";
+
+        var res = question.openDialog("DLG_FILTER_TODO_Neon", prompts, buttons, defaultButton);
+
+        if( res.button != null )
+        {
+            pFilter.user =      res.RESPONSIBLE;
+            pFilter.datefrom =  res.DATE_FROM;
+            pFilter.dateto =    res.DATE_TO;
+            pFilter.category =  res.CATEGORY_TODO;
+            pFilter.delegated = res.DELEGATED;
+            pFilter.needs_action = res.NEEDS_ACTION;
+            pFilter.in_process = res.IN_PROCESS;
+            pFilter.completed = res.COMPLETED;
+            pFilter.cancelled = res.CANCELLED;
+            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
+            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
+        }
+        else
+        {
+            pFilter.datefrom = von;
+            pFilter.dateto = bis;
+            error = false;
+        }
+    }
+    while ( error )
+    return pFilter;
+}
+
+
+/*
+ * Anzeige des Aufgaben-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return string Anzeige
+ */
+function show_filterToDo(pFilter)
+{
+    var retstring = "";
+    var userp = tools.getUser( pFilter.user )[tools.PARAMS];
+    if (pFilter.user != "") retstring = (translate.text("Aufgaben von") + " " + userp[tools.FIRSTNAME] + " " + userp[tools.LASTNAME]);
+    if (pFilter.datefrom != "") retstring += ", " + datetime.toDate(pFilter.datefrom, translate.text("dd.MM.yyyy"));
+    if (pFilter.dateto != "") retstring += " - " + datetime.toDate(pFilter.dateto, translate.text("dd.MM.yyyy"));
+    if (pFilter.category != "") retstring += ", " + translate.text("Kategorie") + " " + pFilter.category;
+    if (pFilter.delegated == "true") retstring += ", " + translate.text("delegiert");
+    if (pFilter.needs_action == "true") retstring += ", " + translate.text("Nicht begonnen");
+    if (pFilter.in_process == "true") retstring += ", " + translate.text("In Bearbeitung");
+    if (pFilter.completed == "true") retstring += ", " + translate.text("Erledigt");
+    if (pFilter.cancelled == "true") retstring += ", " + translate.text("Zurückgestellt");
+
+    return retstring
+}
+
+/*
+ * Setzt den Aufgaben-Filter zurück
+ *
+ * @return {filter}
+ */
+function reset_filterToDo()
+{
+    var today = getDate (vars.getString("$sys.date"));
+
+    return pFilter =  {
+        user: vars.getString("$sys.user"),
+        datefrom: String(eMath.subInt(today, 720 * datetime.ONE_DAY)),
+        dateto: String(eMath.addInt(eMath.addInt(today, 3 * datetime.ONE_DAY)
+            ,datetime.ONE_DAY - datetime.ONE_MINUTE)),
+        category: "",
+        delegated: "",
+        needs_action: "true",
+        in_process: "true",
+        completed: "",
+        cancelled: ""
+    };
+}
+
+/*
+ * Setzt den Event-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return {image}
+ */
+function filterEvent( pFilter )
+{
+    var error = true;
+    var von = pFilter.datefrom;
+    var bis = pFilter.dateto;
+    if ( pFilter == "" )	pFilter =  reset_filterEvent();
+    do
+    {
+        vars.set("$local.relation_id", pFilter.user);
+        vars.set("$local.edt_von", pFilter.datefrom);
+        vars.set("$local.edt_bis", pFilter.dateto);
+        vars.set("$local.category", pFilter.category);
+        vars.set("$local.tentative", pFilter.tentative);
+        vars.set("$local.confirmed", pFilter.confirmed);
+        vars.set("$local.cancelled", pFilter.cancelled);
+        vars.set("$local.free", pFilter.free);
+        var res = swing.askUserQuestion(translate.text("Bitte Filterbedingungen setzen"), "DLG_EVENT_FILTER");
+        if( res != null )
+        {
+            pFilter.user =      res["DLG_EVENT_FILTER.relation_id"];
+            pFilter.datefrom =  res["DLG_EVENT_FILTER.edt_von"];
+            pFilter.dateto =    res["DLG_EVENT_FILTER.edt_bis"];
+            pFilter.category =  res["DLG_EVENT_FILTER.category"];
+            pFilter.tentative = res["DLG_EVENT_FILTER.tentative"];
+            pFilter.confirmed = res["DLG_EVENT_FILTER.confirmed"];
+            pFilter.cancelled = res["DLG_EVENT_FILTER.cancelled"];
+            pFilter.free      = res["DLG_EVENT_FILTER.free"];
+            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
+            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
+        }
+        else
+        {
+            pFilter.datefrom = von;
+            pFilter.dateto = bis;
+            error = false;
+        }
+    }
+    while ( error )
+    return pFilter;
+}
+
+/*
+ * Setzt den Event-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return {image}
+ */
+function filterEvent_Neon( pFilter )
+{
+    var error = true;
+    var von = pFilter.datefrom;
+    var bis = pFilter.dateto;
+    if ( pFilter == "" )	pFilter =  reset_filterEvent();
+    do
+    {
+        var buttons = {
+            "ok" : translate.text("OK"),
+            "": translate.text("Abbrechen")
+        };
+        var defaultButton = "ok";
+
+        var prompts = {
+            FILTER_TEXT: translate.text("Bitte Filterbedingungen setzen"),
+            RESPONSIBLE_APPOINTMENT: pFilter.user,
+            DATE_FROM: pFilter.datefrom,
+            DATE_TO: pFilter.dateto,
+            CATEGORY_APPOINTMENT: pFilter.category,
+            TENTATIVE: pFilter.tentative,
+            CONFIRMED: pFilter.confirmed,
+            CANCELLED: pFilter.cancelled
+        }
+        var res = question.openDialog("DLG_FILTER_APPOINTMENT_Neon", prompts, buttons, defaultButton);
+        if( res.button != null )
+        {
+            pFilter.user =      res.RESPONSIBLE_APPOINTMENT;
+            pFilter.datefrom =  res.DATE_FROM;
+            pFilter.dateto =    res.DATE_TO;
+            pFilter.category =  res.CATEGORY_APPOINTMENT;
+            pFilter.tentative = res.TENTATIVE;
+            pFilter.confirmed = res.CONFIRMED;
+            pFilter.cancelled = res.CANCELLED;
+            if (pFilter.datefrom != "" && pFilter.dateto != "" && pFilter.dateto >= pFilter.datefrom ) error = false;
+            else question.showMessage(translate.text("Bitte Datumseingabe prüfen!"))
+        }
+        else
+        {
+            pFilter.datefrom = von;
+            pFilter.dateto = bis;
+            error = false;
+        }
+    }
+    while ( error )
+    return pFilter;
+}
+
+/*
+ * Anzeige des Event-Filter
+ *
+ * @param {Object} pFilter req
+ *
+ * @return string Anzeige
+ */
+function show_filterEvent(pFilter)
+{
+    var retstring = "";
+
+    var userp = tools.getUser( pFilter.user )[tools.PARAMS];
+    if (pFilter.user != "") retstring = translate.text("Termine von") + " " + userp[tools.FIRSTNAME] + " " + userp[tools.LASTNAME];
+    if (pFilter.datefrom != "") retstring += ", " + datetime.toDate(pFilter.datefrom, translate.text("dd.MM.yyyy"));
+    if (pFilter.dateto != "") retstring += " - " + datetime.toDate(pFilter.dateto, translate.text("dd.MM.yyyy"));
+    if (pFilter.category == "true") retstring += ", " + translate.text("Kategorie") + " " + pFilter.category;
+    if (pFilter.tentative == "true") retstring += ", " + translate.text("Vorläufig");
+    if (pFilter.confirmed == "true") retstring += ", " + translate.text("Bestätigt");
+    if (pFilter.cancelled == "true") retstring += ", " + translate.text("Abgesagt");
+
+    return retstring
+}
+
+/*
+ * Setzt den Event-Filter zurück
+ *
+ * @return {filter}
+ */
+function reset_filterEvent()
+{
+    var today = getDate (vars.getString("$sys.date"));
+
+    return pFilter =  {
+        user: vars.getString("$sys.user"),
+        datefrom: String(today), //nur die Termine ab heute anzeigen,
+        //die von vor einer Woche sind uninteressant
+        dateto: String(eMath.addInt(eMath.addInt(today, datetime.ONE_WEEK)
+            ,datetime.ONE_DAY - datetime.ONE_MINUTE)),
+        category: "",
+        tentative: "true",
+        confirmed: "true",
+        cancelled: "",
+        free: "true"
+    };
+}
+
+/*
+ * Setzt den Aufgaben-Filter in Tab Aufgaben
+ *
+ * @return {image}
+ */
+function filterLinkedToDo()
+{
+    var filtervalues = ["", "false"];
+    vars.set("$local.CalenderUser", getCalenderUser( calendars.RIGHT_READ_TASK ));
+
+    //Vorbelegen der Werte, wenn bereits gewählt wurde:
+    if(vars.exists("$image.FilterValuesT"))
+    {
+        filtervalues = vars.get("$image.FilterValuesT");
+        vars.set("$local.relation_id", filtervalues[0]);
+        vars.set("$local.done", filtervalues[1]);
+    }
+
+    var res = swing.askUserQuestion(translate.text("Bitte Filterbedingungen setzen"), "DLG_TASK_DATE_LINKED_FILTER");
+
+    if( res != null && res != undefined && res != "")
+    {
+        filtervalues[0] = res["DLG_TASK_DATE_LINKED_FILTER.relation_id"];
+        filtervalues[1] = res["DLG_TASK_DATE_LINKED_FILTER.done"]
+    }
+    vars.set("$image.FilterValuesT", filtervalues );
+
+    return(filtervalues);
+}
+
+/*
+ * Setzt den Aufgabe-Filter zurück
+ *
+ * @return {image}
+ */
+function resetfilterLinkedToDo()
+{
+    var filtervalues = ["", "false"];
+
+    vars.set("$image.FilterValuesT", filtervalues );
+
+}
+
+/*
+ * setzt die Kalenderrechte
+ *
+ * @return {void}
+ */
+function setCalendarGrant()
+{
+    calendars.resetCalendarUser();
+    var user_read_todo = [];
+    var user_write_todo = [];   // ["Admin"]
+    var user_read_event = [];
+    var user_write_event = [];
+    var tree = {};
+    var data = db.table("select THEMEID, THEME.THEME_ID, LOGIN, STATUS from THEME "
+        + " left join EMPLOYEE on EMPLOYEE.THEME_ID = THEMEID left join RELATION on RELATION_ID = RELATIONID and STATUS = 1"
+        +" where KIND = 2");
+    for ( let i = 0; i < data.length; i++)
+    {
+        if ( tree[data[i][0]] == undefined )    tree[data[i][0]] = {
+            pid: data[i][1],
+            isuser: false
+        };
+        if ( data[i][2] != "" && data[i][3] != "" )     tree[data[i][2]] = {
+            pid: data[i][0],
+            isuser: true
+        };
+    }
+
+    var user = vars.getString("$sys.user");
+    // Lese- und Schreibrechte auf Kalender aus Datentabelle holen
+    data = db.table("select HASRIGHTFOR, TODO_RIGHTS, EVENT_RIGHTS from AOSYS_CALENDAR_RIGHTS where LOGIN = '" + user + "'");
+    for ( var i = 0; i < data.length; i++ )
+        if(tree[data[i][0]] != undefined)
+            tree[data[i][0]].grants = data[i].slice(1);
+
+    for ( login in tree )
+    {
+        if( tree[login].isuser )
+        {
+            var grantstodo = __getGrants( login, 0 );
+            var grantsevent = __getGrants( login, 1 );
+            if ( grantstodo.length + grantsevent.length > 0 )
+            {
+                if ( grantstodo == "1" || grantstodo == "3")  user_read_todo.push(login);
+                if ( grantstodo == "2" || grantstodo == "3")  user_write_todo.push(login);
+                if ( grantsevent == "1" || grantsevent == "3")  user_read_event.push(login);
+                if ( grantsevent == "2" || grantsevent == "3")  user_write_event.push(login);
+            }
+        }
+    }
+    calendars.setCalendarUser(user_read_todo, calendars.RIGHT_READ_TASK, true, calendars.SORTSTRATEGY_NATURAL );
+    calendars.setCalendarUser(user_write_todo, calendars.RIGHT_WRITE_TASK, true, calendars.SORTSTRATEGY_NATURAL );
+    calendars.setCalendarUser(user_read_event, calendars.RIGHT_READ_APPOINTMENT, true, calendars.SORTSTRATEGY_NATURAL );
+    calendars.setCalendarUser(user_write_event, calendars.RIGHT_WRITE_APPOINTMENT, true, calendars.SORTSTRATEGY_NATURAL );
+
+    //********** Ressourcen in Kalender einfügen ************
+    var ressource = tools.getUsersWithRole("PROJECT_Ressource");
+    calendars.setCalendarUser(  ressource, calendars.RIGHT_READ_APPOINTMENT | calendars.RIGHT_WRITE_APPOINTMENT  );
+
+    //add all users from support-role since support-action-tasks (sp_supportAktionen) generates tasks for all support-role members
+    //and if you're a member you are allowed to edit these
+    if (tools.hasRole(user, "PROJECT_Support"))
+    {
+        var support = tools.getUsersWithRole("PROJECT_Support");
+        calendars.setCalendarUser(  support, calendars.RIGHT_WRITE_TASK | calendars.RIGHT_READ_TASK  );
+    }
+
+    function __getGrants( pKey, pIndex )
+    {
+        var grants = [];
+        if ( tree[pKey].grants != undefined && tree[pKey].grants[pIndex]) grants = tree[pKey].grants[pIndex];
+        else if ( tree[pKey].pid != "" )   grants = __getGrants( tree[pKey].pid, pIndex );
+        return grants;
+    }
+}
+
+/*
+ * setzt Recht für alle Kalender
+ *
+ * @return {void}
+ */
+function setAllCalendarGrant()
+{
+    calendars.resetCalendarUser();
+    var users = tools.getStoredUsers();
+    var calendar_user = [];
+    for ( var i = 0; i < users.length; i++ )    calendar_user.push( users[i][1] );
+    calendars.setCalendarUser(calendar_user, calendars.RIGHT_READ_TASK | calendars.RIGHT_WRITE_TASK, false, calendars.SORTSTRATEGY_NATURAL );
+}
+
+/*
+ * gibt die Logins der user, die den übergebenen User als Attribute eingetragen haben, zurück
+ *
+ * @param {[]} pUsers req Logins
+ * @param {String []} pAttrName req AttributeName für Employee
+ * @param {String []} pFields opt
+ *
+ * @return {String []} Logins
+ */
+function getUsersbyAttr( pUsers, pAttrName, pFields )
+{
+    if (typeof(pAttrName) == "string") pAttrName = [pAttrName]
+
+    if ( pFields == undefined )
+    {
+        pFields = ["LOGIN"];
+    }
+
+    var sqlstr = "select " + pFields.join(", ") + " from ATTRLINK join ATTR on ATTRLINK.ATTR_ID = ATTRID and OBJECT_ID = 12 "
+    + "and ATTRNAME in ('" + pAttrName.join("','") + "') "
+    + " join EMPLOYEE on EMPLOYEEID = ATTRLINK.ROW_ID join RELATION on RELATION_ID = RELATIONID join PERS on PERS_ID = PERSID"
+    + " where VALUE_ID in (select EMPLOYEEID from EMPLOYEE where LOGIN in ('" + pUsers.join("','") + "'))"
+    + "";
+
+    if(pFields.length == 1)
+        return db.array(db.COLUMN, sqlstr);
+    else
+        return db.table(sqlstr);
+}
+
+/*
+ * Gibt die Anzahl der verknüpften Aufgaben und Termine zurück.
+ *
+ * @param {String} pID req
+ * @param {String} pFrame req
+ *
+ * @return {String} text
+ */
+function countLinkedTodoEvent(pID, pFrame)
+{
+    var today = getDate (vars.getString("$sys.date"));
+    var datefrom = String(today);
+    var dateto = String(eMath.addInt(today, datetime.ONE_WEEK));
+
+    var str = "select count(distinct ELEMENTUID) from ASYS_CALENDARLINK join ASYS_CALENDARBACKEND on ELEMENTUID = ASYS_CALENDARLINK.ENTRYID "
+    + " where FRAME = 'comp." + pFrame + "' and ELEMENTUID is not null and DBID = '" + pID + "'";
+    var rettask = db.cell(str + "and ENTRYTYPE = " + calendars.VTODO + " and STATUS in ('"
+        + mapCalendarStatus(calendars.STATUS_NEEDSACTION, calendars.getBackendTypeTasks() ) + "', '"
+        + mapCalendarStatus(calendars.STATUS_INPROCESS, calendars.getBackendTypeTasks() ) + "')");
+
+    var eventStatusList = [
+         mapCalendarStatus(calendars.STATUS_CONFIRMED, calendars.getBackendType() )
+        ,mapCalendarStatus(calendars.STATUS_TENTATIVE, calendars.getBackendType() )
+    ];
+
+    if (calendars.getBackendType() == calendars.BACKEND_EXCHANGEWS)
+        eventStatusList.push(calendars.STATUS_FREE);
+
+    var retevent = db.cell([str + " and ENTRYTYPE = " + calendars.VEVENT + " and STATUS in ('" + eventStatusList.join("', '") + "')"
+            + " and DTSTART >= ? and DTEND <= ?",
+        [ [ String(datefrom), SQLTYPES.DATE ],  [String(dateto), SQLTYPES.DATE ] ]]);
+
+    result.string(translate.withArguments("&Aufg / Term (%0/%1)", [rettask, retevent]));
+}
+
+/*
+ * Gibt die Anzahl der verknüpften Aufgaben zurück.
+ *
+ * @param {String} pID req
+ * @param {String} pFrame req
+ *
+ * @return {String} text
+ */
+function countLinkedTodo(pID, pFrame)
+{
+    var str = "select count(distinct ELEMENTUID) "
+    + "from ASYS_CALENDARLINK "
+    + "join ASYS_CALENDARBACKEND on ELEMENTUID = ASYS_CALENDARLINK.ENTRYID "
+    + " where FRAME = 'comp." + pFrame + "' "
+    + "and ELEMENTUID is not null and DBID = '" + pID + "'";
+
+    var rettask = db.cell(str + "and ENTRYTYPE = " + calendars.VTODO
+        + " and  STATUS in ('"
+        + mapCalendarStatus(calendars.STATUS_NEEDSACTION, calendars.getBackendTypeTasks() ) + "', '"
+        + mapCalendarStatus(calendars.STATUS_INPROCESS, calendars.getBackendTypeTasks() ) + "')"
+        + "");
+
+    result.string(translate.withArguments("&Aufgaben (%0)", [rettask]));
+}
+
+/*
+ * Gibt die Überschneidungen von Termine zurück.
+ *
+ * @param {Date} pStart req
+ * @param {Date} pEnd req
+ * @param {Array} pUsers req
+ *
+ * @return {String Array} Termine
+ */
+function getOverlappingEvents(pStart, pEnd, pUsers  )
+{
+    var resultEvents = new Array();
+    var users = pUsers;
+    if (calendars.getBackendType() == calendars.BACKEND_DB && pStart != "" && pEnd != "" && users.length > 0)
+    {
+        calendars.clearCache();
+        for (var u = 0; u < users.length; u++)
+        {
+            var localuid = vars.get("$image.entry")[calendars.ID]
+            var condition = new Array();
+            condition["COUNT"] = "1";
+            condition["TYPE_1"] = calendars.VFREEBUSY;
+            condition["USER_1"] = users[u][0];
+            condition["START_1"] = pStart;
+            condition["END_1"] = pEnd;
+
+            var fbsall = calendars.getEntries(condition);
+            for (var j = 0; j < fbsall.length; j++)
+            {
+                var fbs = fbsall[j];
+                for (var i = 0; i < fbs.length; i++)
+                {
+                    var next = fbs[i];
+                    var uid = next[calendars.ID];
+                    var sum = next[calendars.SUMMARY];
+                    if (uid != localuid)
+                    {
+                        var freebusy = next[calendars.FREEBUSY];
+                        var freebusyDec = text.decodeMS(freebusy);
+                        var match = false;
+                        for (var k = 0; k < freebusyDec.length; k++)
+                        {
+                            var freebusyInstance = text.decodeMS(freebusyDec[k]);
+                            if (!freebusyInstance[0].equals("FREE"))
+                            {
+                                match = true;
+                            }
+                        }
+                        if (match)  resultEvents.push([users[u][2], sum != null && sum.length > 0 ? sum : "?", uid]);
+                    }
+                }
+            }
+        }
+    }
+    return resultEvents;
+}
+
+/*
+ * Überprüft den Eintrag, wenn eine neue Aufgaben angelegt wird darauf, ob private Aufgaben für andere erstellt werden
+ *
+ * @param {String[]} pUser die Liste der User, denen die Aufgabe zugewiesen werden soll
+ * @param {String} pClassification die Klassifizierung der Aufgabe - "PRIVATE"
+ *
+ * @return {Boolean} ob der Eintrag in der Konstellation möglich ist, wenn nicht wird eine Meldung ausgegeben
+ */
+function checkEntry(pUser, pClassification )
+{
+    if( pClassification == "PRIVATE" &&
+        ( pUser.length > 1 || text.decodeMS(pUser[0][0])[1] != "CN:" + vars.getString("$sys.user")))
+        {
+        question.showMessage(translate.text("Eine private Aufgabe kann nicht jemand anderem zugewiesen werden."));
+        return false;
+    }
+    else
+    {
+        return true;
+    }
+}
+
+/*
+ * Verschiebt ein Calendareintrag
+ *
+ * @param {String[]} pIDs req die zu verschiebenden Ids mit den Calenderinformationen (Multistring)
+ * @param {String} pTblCompName  opt die Komponente die aktualisiert werden soll
+ *
+ * @return {void}
+ */
+function shiftEntry(pIDs, pTblCompName)
+{
+    var datenew = swing.askUserQuestion(translate.text("Verschieben auf Datum?"), "DLG_DATE");
+    if (datenew != null )
+    {
+        var grantedUsers = calendars.getDisplayCalendarUsers(calendars.RIGHT_WRITE_TASK);
+        var usermap = {};
+        var granted = true;
+        for (let i = 0; i < grantedUsers.length; i++)
+        {
+            usermap[grantedUsers[i][1]] = true;
+        }
+
+        datenew = datetime.clearTime(datenew["DLG_DATE.Edit_date"]);
+        if (datenew <= vars.getString("$sys.today"))
+            question.showMessage(translate.text("nur Verschiebung in die Zukunft erlaubt!"));
+        else
+        {
+            for (var i = 0, j = pIDs.length; i < j; i++)
+            {
+                var id = text.decodeMS(pIDs[i]);
+                var entry = calendars.getEntry(id[0], null, id [1], calendars.VTODO);
+                var affectedUsers = entry[calendars.ATTENDEES];
+
+                granted =  hasGrantForEntryByObject(affectedUsers, usermap);
+                if(granted)
+                {
+                    //Zeitdifferenz von Aufgabenstart und -ende
+                    var dateDiff  = eMath.subInt(entry[calendars.DUE], entry[calendars.DTSTART]);
+
+                    //Startzeit der Aufgabe
+                    var startTime = eMath.subInt(entry[calendars.DTSTART]
+                        , datetime.clearTime(entry[calendars.DTSTART]));
+
+                    entry[calendars.DTSTART] = eMath.addInt(datenew, startTime);
+                    entry[calendars.DUE] = eMath.addInt(entry[calendars.DTSTART], dateDiff);
+                    calendars.update( [ entry ] );
+                }
+                else
+                    question.showMessage(translate.text("Keine Berechtigung zum Verschieben der Aufgabe"));
+            }
+        }
+    }
+}
+
+/*
+ * Gibt eine Aufgabe weiter
+ *
+ * @param {String} pIDs die ID der Aufgabe, welche weitergegeben werden soll
+ * @param {String} pTblCompName die Komponente der Aufgaben, die aktualisiert werden soll
+ *
+ * @return {void}
+ */
+function handOverToDo(pIDs, pTblCompName)
+{
+    var calendar_user = "";
+    var users = getCalenderUser( calendars.RIGHT_WRITE_TASK );
+    var publicCount = 0;
+    var privateCount = 0;
+    var notGrantedCount = 0;
+
+    var grantedUsers = calendars.getDisplayCalendarUsers(calendars.RIGHT_WRITE_TASK);
+    var userMap = {};
+    var granted = true;
+    for (let i = 0; i < grantedUsers.length; i++)
+    {
+        userMap[grantedUsers[i][1]] = true;
+    }
+
+    if (pIDs.length == 1)  // only one todo and private
+    {
+        var id = text.decodeMS( pIDs[0] );
+        var entry = calendars.getEntry( id[0], null, id[1] );
+        if(entry[calendars.CLASSIFICATION] == calendars.CLASSIFICATION_PRIVATE)
+        {
+            question.showMessage(translate.text("Kein Weitergeben von privaten Aufgaben möglich!"));
+            return;
+        }
+    }
+
+    for ( let i = 0; i < users.length; i++)  if (pIDs.length > 0 || users[i][0] != id[1])   calendar_user += "|" + users[i][1];
+    var selection = swing.askQuestion(translate.text("Benutzer auswählen"), swing.QUESTION_COMBOBOX, calendar_user);
+    if (selection != null)
+    {
+        for ( let i = 0; i < users.length; i++)
+        {
+            if ( selection == users[i][1] )
+            {
+                selection = users[i];
+                break;
+            }
+        }
+        for( let i = 0; i < pIDs.length; i++)
+        {
+            let id = text.decodeMS( pIDs[i] );
+            let entry = calendars.getEntry( id[0], null, id[1] );
+            if(entry[calendars.CLASSIFICATION] == calendars.CLASSIFICATION_PRIVATE)  privateCount++;
+            else //Nur öffentliche Aufgabe kann weiter gegeben werden.
+            {
+                // User austauschen
+                var usermap = entry[calendars.ATTENDEES];
+                var affectedusers = [];
+
+                granted =  hasGrantForEntryByObject(usermap, userMap);
+
+                for (var ui = 0; ui < usermap.length; ui++)
+                {
+                    var login_cn = usermap[ui]["cn"];
+                    var user = tools.getUserByAttribute(tools.CALENDARID, usermap[ui]["paramvalue"].substr("mailto:".length))
+                    if (user == null)  user = login_cn;
+                    else user = user[tools.TITLE]
+
+                    if ( user == id[1] )   affectedusers.push(selection[0]);
+                    else  if ( user != selection[0] )  affectedusers.push(user);
+                }
+                if(granted)
+                {
+                    entry[calendars.AFFECTEDUSERS] = text.encodeMS(calendars.getCalendarUsers(affectedusers));
+                    calendars.update([entry]);
+                    publicCount++;
+                }
+                else notGrantedCount++
+            }
+        }
+        question.showMessage(translate.withArguments("%0 Aufgabe(n) erfolgreich weitergegeben an: %1"
+            + (privateCount == 0 ? "" : "\n%2 private Aufgabe(n) können nicht weitergegeben werden.")
+            +(notGrantedCount == 0 ? "" : "\n%3 Aufgabe(n) können aufgrund fehlender Berechtigung nicht weitergegeben werden."),
+            [publicCount, selection[1], privateCount, notGrantedCount]));
+
+    }
+}
+
+// ToDo prüfen !!
+/*
+ * Verschiebt die Kalendereinträge bei Login-Änderung
+ *
+ * @param {String} pNewlogin req
+ * @param {String} pOldlogin req
+ * @param {String} pNewCalendarID req
+ * @param {String} pOldCalendarID req
+ *
+ * @return {void}
+ */
+function moveCalendarData ( pNewlogin, pOldlogin, pNewCalendarID, pOldCalendarID )
+{
+    var newcaluser = text.encodeMS(["mailto:" + pNewCalendarID, "CN:" + pNewlogin]);
+    var oldcaluser = text.encodeMS(["mailto:" + pOldCalendarID, "CN:" + pOldlogin]);
+    db.runStatement("update ASYS_CALENDARBACKEND set OWNER = '" + newcaluser + "' where OWNER = '" + oldcaluser + "'");
+    db.runStatement("update ASYS_CALENDARBACKEND set ORGANIZER = '" + newcaluser + "' where ORGANIZER = '" + oldcaluser + "'");
+
+    if(pNewCalendarID != pOldCalendarID) //Nur wenn sich die E-Mailadresse geändert hat
+    {
+        //Messenger-Historien
+        db.runStatement("update ASYS_XMPP_HISTORY set JID_FROM = '" + pNewCalendarID +"' where JID_FROM = '" + pOldCalendarID +"'", SqlUtils.getSystemAlias());
+        db.runStatement("update ASYS_XMPP_HISTORY set JID_TO = '" + pNewCalendarID +"' where JID_TO = '" + pOldCalendarID +"'", SqlUtils.getSystemAlias());
+    }
+}
+
+/*
+ * Löst den CalenderUser in lesbarer From auf
+ *
+ * @param {String} pValue req
+ *
+ * @return {String} übersetzten Wert
+ */
+function getCalUser( pValue )
+{
+    var realname = pValue;
+    var user = tools.getUser( text.decodeMS(pValue)[1].split(":")[1] );
+    if ( user != null )
+    {
+        realname = user[tools.PARAMS][tools.FIRSTNAME] + " " + user[tools.PARAMS][tools.LASTNAME];
+    }
+    return realname;
+}
+
+/*
+ * Gibt den Login eines CalenderUser zurück
+ *
+ * @param {String} pCalendarUser req
+ *
+ * @return {String} Title
+ */
+function getTitleCalenderUser( pCalendarUser )
+{
+    var data = text.decodeMS(pCalendarUser)
+    for ( var i = 0; i < data.length; i++ )
+    {
+        //if login changes we have to check calendarid
+        if ( data[i].substr(0, "mailto:".length).toUpperCase() == "MAILTO:" )
+        {
+            var user = tools.getUserByAttribute(tools.CALENDARID, [data[i].substr("mailto:".length)]);
+            if (user != null )
+                return user[tools.TITLE];
+        }
+
+        if ( data[i].substr(0, 3).toUpperCase() == "CN:" )
+            return data[i].substr(3);
+    }
+    return "";
+}
+
+
+/*
+ * Gibt den richtigen Status zum Prüfen je nach Backend zurück
+ *
+ *
+ * @param {String} pStatus req die konstante für den zu prüfenden status,
+ *                             z.B. calendars.STATUS_INPROCESS
+ *
+ * @param {String} pCalendarType req die konstante für den typen des Termin- oder Aufgabenbackends,
+ *                             z.B. calendars.BACKEND_DB
+ *
+ * @return {String} Konstanten für den Kalender (Backend-Typen), gibt es den status im backend nicht
+ *                  wird null geliefert
+ */
+function mapCalendarStatus(pStatus, pCalendarType)
+{
+    switch (pCalendarType)
+    {
+        //case calendars.BACKEND_EXCHANGE:
+        case calendars.BACKEND_EXCHANGEWS:
+            if (pStatus == calendars.STATUS_CONFIRMED)
+                return calendars.STATUS_BUSY;
+            else
+                return pStatus;
+        default:
+            if (pStatus == calendars.STATUS_OOF)//nur bei exchange
+                return null;
+            else
+                return pStatus;
+    }
+}
+
+/*
+ * Sets the imagevariable with affectedusers
+ *
+ * @param {Object} pEntry req the Entry of Tasks or Appointments
+ *
+ * @return {void}
+ *
+ */
+function setAffectedUsersImage(pEntry)
+{
+    var affectedUsers = [];
+    var usermap = pEntry[calendars.ATTENDEES];
+    var realnames = getRealNameObject(usermap);
+
+    for ( var i = 0; i < usermap.length; i++ )
+    {
+        affectedUsers.push([ text.encodeMS( [usermap[i]["paramvalue"], "CN:" + usermap[i]["cn"]] ), realnames[usermap[i]["cn"]] ]);
+    }
+
+    vars.set("$image.affectedusers", affectedUsers);
+    return affectedUsers;
+}
+
+/*
+ * Opens calendar links
+ *
+ * @param {String} pEntryID req the ID of the link
+ *
+ * @return {void}
+ *
+ */
+function openCalendarLinks(pEntryID)
+{
+    var openFramesObj = {};
+    if (typeof(pEntryID) == "object")
+        openFramesObj = pEntryID;
+    else
+    {
+        var links = db.table("SELECT FRAME, DBIDCOLUMN, DBID FROM ASYS_CALENDARLINK WHERE ENTRYID = '" + pEntryID + "'");
+        for (var i = 0; i < links.length; i++)
+        {
+            var frame = links[i][0].substr( 5 );//remove comp. so the frame can be opened with openFrame
+            if ( openFramesObj[frame] == undefined ) openFramesObj[frame] = {
+                IDField: links[i][1],
+                IDs: []
+            };
+            openFramesObj[frame].IDs.push(links[i][2]);
+        }
+    }
+
+    for ( frame in openFramesObj)
+    {
+        var condition = openFramesObj[frame].IDField + " in ('" + openFramesObj[frame].IDs.join("', '") + "')";
+        var framemode = openFramesObj[frame].IDs.length > 1 ? swing.FRAMEMODE_TABLE_SELECTION : swing.FRAMEMODE_SHOW;
+
+        if ( vars.getString("$global.upwardLink") == "link")
+        {
+            swing.openLinkedFrame(frame, condition, swing.WINDOW_CURRENT, framemode, "", null, false, {
+                autoclose: true
+            } );
+        }
+        else
+        {
+            swing.openFrame(frame, condition, swing.WINDOW_CURRENT, framemode, null, false);
+        }
+    }
+}
+
+/**
+ * Returns the "real" calendar system/backend type (BackendType & SyncBackendType)
+ *
+ * @param {Number} pScope - The needed scope (e.g. "calendars.VEVENT", "calendars.VTODO")
+ * @return {Number} - The backend type (calendars.BACKEND_*)
+ */
+function getCalendarSystemType (pScope)
+{
+    // Check sync backend type
+    if (calendars.getSyncBackendType() != calendars.BACKEND_NONE && calendars.getSyncBackendType() != 3)
+    {
+        var scope = calendars.getSyncBackendTypeScope();
+        if (scope.length == 1 && scope[0] == pScope) // Scope.length = 1 -> VEVENT *OR* VTODO
+            return calendars.getSyncBackendType();
+        else if (scope.length == 2) // Scope.length = 2 -> Both
+            return calendars.getSyncBackendType();
+       // Scope.length = 0 -> Nothing selected -> Skip this block
+    }
+
+    // Fallback to backend type (event)
+    if (calendars.getBackendType() != calendars.BACKEND_NONE && pScope === calendars.VEVENT)
+        return calendars.getBackendType();
+
+    // Second fallback to backend type (todo)
+    if (calendars.getBackendTypeTasks() != calendars.BACKEND_NONE && pScope === calendars.VTODO)
+        return calendars.getBackendTypeTasks();
+
+    // Everything is none
+    return calendars.BACKEND_NONE;
+}
+
+function hasGrantForEntryByMS(pAffectedUsers, pUserMap)
+{
+    for (let i = 0; i < pAffectedUsers.length; i++)
+    {
+        var calUser = getTitleCalenderUser( pAffectedUsers[i][0]);
+        if(pUserMap[calUser] == undefined)
+        {
+            return false;
+            break;
+        }
+        else
+            return true;
+    }
+}
+
+function hasGrantForEntryByObject(pAffectedUsers, pUserMap)
+{
+    for (let i = 0; i < pAffectedUsers.length; i++)
+    {
+        calUser = tools.getUserByAttribute(tools.CALENDARID, pAffectedUsers[i]["paramvalue"].substr("mailto:".length))
+        if (calUser == null)  calUser = pAffectedUsers[i]["cn"];
+        else calUser = calUser[tools.TITLE]
+        if(pUserMap[calUser] == undefined)
+        {
+            return false;
+            break;
+        }
+        else
+            return true;
+    }
+}
diff --git a/process/DocumentTemplate_lib/process.js b/process/DocumentTemplate_lib/process.js
index beefa84e98a8831b674bc7d235abae67ecf1f185..771323e40270864ef0b87041c3291aac368e57f7 100644
--- a/process/DocumentTemplate_lib/process.js
+++ b/process/DocumentTemplate_lib/process.js
@@ -113,7 +113,7 @@ DocumentTemplate.types = {
  */
 DocumentTemplate.loadTemplate = function (pAssignmentRowId, pAssignmentTable)
 {
-    var alias = "_____SYSTEMALIAS";
+    var alias = SqlUtils.getSystemAlias();
     if (!pAssignmentTable)
         pAssignmentTable = "DOCUMENTTEMPLATE";
     var templateDocument = db.getBinaryMetadata(pAssignmentTable, "DOCUMENT", pAssignmentRowId, false, alias, null);
diff --git a/process/Permission_lib/process.js b/process/Permission_lib/process.js
index bec5de949e05a8cad27e1e644de9705220b7630b..d5b1425935b6eb7eeef19f148ddb419b8671781d 100644
--- a/process/Permission_lib/process.js
+++ b/process/Permission_lib/process.js
@@ -10,7 +10,9 @@ import("Sql_lib");
  */
 function PermissionUtil () {}
 
-var alias = "_____SYSTEMALIAS";
+{ //block where variables declared with let are available to avoid unexpected side-effects
+
+let alias = SqlUtils.getSystemAlias();
 
 /**
  * Returns the ids of all subordinated permission sets of a given parent permission set.
@@ -590,6 +592,8 @@ PermissionUtil.deletePermissionAction = function(pPermActionId) {
     return db.deleteData(table, cond, alias);
 }
 
+} //end of block
+
 
 // arrDiff calculates different elements of two arrays and returns them as array, otherwise empty array
 function arrDiff (arr1, arr2) {
diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js
index de713fa6707bd85988ac250dc3873bb6006970c4..250a95ee13ddaee794ae0aee7592e8c6a8bbff6e 100644
--- a/process/Util_lib/process.js
+++ b/process/Util_lib/process.js
@@ -1,615 +1,615 @@
-import("Sql_lib");
-import("system.neon");
-import("system.project");
-import("system.process");
-import("system.db");
-import("system.util");
-import("system.translate");
-import("system.text");
-import("system.vars");
-import("system.swing");
-import("system.question");
-import("system.eMath");
-import("system.datetime");
-import("Offer_lib");
-import("Date_lib");
-
-/**
- * Class containing static utility functions for string-actions
- * Do not create an instance of this
- * 
- * @class
- */
-function StringUtils(){}
-
-/**
- * concats severel elements by a separator; the separator is only applied if a element is not null and not an empty string "";
- *
- * @param {String} pSeparator specifies how the not empty elements shall be concatenated
- * @param {String[]} pElements elements that shall be joined by the separator 
- *
- * @return {String} concatenated string; if all elements are empty an emtpy string is returned
- * 
- */
-StringUtils.concat = function(pSeparator, pElements)
-{
-   var res = pElements.filter(function(e){
-       return e != null && e != "";
-   }).join(pSeparator);
-   return res;
-};
-
-
-/**
- * converts a string to a string of always 36 chars. Whitespaces are added at the end if needed.
- */
-StringUtils.pad36 = function(pValue) 
-{
-    return (pValue + "                                    ").slice(0, 36);
-}
-
-/**
- * Class containing static utility functions for numbers
- * Do not create an instance of this
- * 
- * @class
- */
-function NumberUtils(){}
-
-/**
- * Check iv the value is inside of the min / max values. 
- * INCLUDING min / max
- *
- * @param {Number} pValue value to check
- * @param {Number} pMin min value INCLUSIVE
- * @param {Number} pMax max value INCLUSIVE 
- * @param {Boolean} [pIgnoreNull=true] return True if pValue is null
- * 
- * @return {Boolean}
- */
-NumberUtils.isInside = function(pValue, pMin, pMax, pIgnoreNull)
-{
-    if (pIgnoreNull == undefined)
-        pIgnoreNull = true;
-    
-    return pValue >= pMin && pValue <= pMax || pIgnoreNull && (pValue == null || isNaN(pValue));
-};
-
-/**
- * For use in validationProcess. Calls result.string(...) with error message, if number is not inside of the given values
- * INCLUDING min / max.
- * 
- * @param {Number} pTitle title to display in error message. Should be the name of the field and it will be translated.
- * @param {Number} pValue value to check
- * @param {Number} pMin min value INCLUSIVE
- * @param {Number} pMax max value INCLUSIVE 
- * @param {Boolean} [pIgnoreNull=true] return True if pValue is null
- * 
- * @return {String|False} returns the error message or false 
- * 
- * @example
- *  var value = vars.get("local.value"); <br>
- *   <br>
- *  var validationResult = NumberUtils.validateIsBetweenFloat("Discount", value, 0, 100); <br>
- *   <br>
- *  if (validationResult) <br>
- *  { <br>
- *      result.string(validationResult); <br>
- *  } <br>
- */
-NumberUtils.validateIsBetweenFloat = function(pTitle, pValue, pMin, pMax, pIgnoreNull)
-{
-    if(pValue.includes(","))
-        pValue = pValue.replace(",", ".");
-    
-    var discount = parseFloat(pValue);
-    
-    if(isNaN(discount))
-        return false;
-    
-    if (!NumberUtils.isInside(discount, 0, 100, pIgnoreNull))
-    {
-        return (translate.withArguments("${MIN_MAX_ERROR} field: %0, value: %1, min: %2, max: %3", [translate.text(pTitle), discount, pMin, pMax]));
-    }
-    return false;
-}
-
-/**
- * format Numbers with currency
- */
-NumberUtils.formatWithCurrency = function(pNumber, pPattern, pCurrency)
-{
-    if (pNumber && pPattern)
-        return text.formatDouble(pNumber, pPattern + " " + pCurrency);
-    return "0 " + pCurrency;
-}
-
-/**
- * Class containing static utility functions for use with arrays
- * Do not create an instance of this!
- * 
- * @class
- */
-function ArrayUtils() {}
-
-/**
- * returns a distinct list of a two dimensional array
- *
- * @param {Array} p2dArray the Array where duplicate keys shall be removed
- *
- * @return 2D-Array that only contains unique entries
- */
-ArrayUtils.distinct2d = function(p2dArray)
-{
-    var tempKeys = {};    
-    return p2dArray.filter(function(row){
-        var key = row.join("-");
-        return !(tempKeys[key] = ++tempKeys[key]|0)
-    });
-};
-
-/**
- * sorts a two dimensional array by the given index
- *
- * @param {Array} targetArray the Array to be sorted
- * @param {String} index the index of the field to sort by
- * @param {Boolean} [sortAsc=false] TRUE sorts ascending, FALSE sorts decending
- * @param {Boolean} [isNumber=false] TRUE sorts numerical, FALSE or undefined sorts alphanumerical
- *
- * @return targetArray
- *
- * @throws {RangeError} if index is outside pArray.length
- */
-ArrayUtils.sort2d = function(targetArray, index, sortAsc, isNumber) {
-    if (targetArray.length == 0)
-        return targetArray;
-    if (targetArray[0].length == 0)
-        return targetArray;
-    if (index >= targetArray[0].length)
-        throw new RangeError("Index Out Of Bounds: " + index + " >= " + targetArray[0].length);
-
-    var sortFn = function (x, y) {
-        if ( isNumber ) {
-            xx = Number(x[index]);
-            yy = Number(y[index]);
-        }
-        else {
-            xx = x[index];
-            yy = y[index];
-
-            xx = xx.toLowerCase();
-            xx = xx.replace(/ä/g,"ae");
-            xx = xx.replace(/ö/g,"oe");
-            xx = xx.replace(/ü/g,"ue");
-            xx = xx.replace(/ß/g,"ss");
-
-            yy = yy.toLowerCase();
-            yy = yy.replace(/ä/g,"ae");
-            yy = yy.replace(/ö/g,"oe");
-            yy = yy.replace(/ü/g,"ue");
-            yy = yy.replace(/ß/g,"ss");
-        }
-        if (xx == yy)
-            return 0;
-        if (xx < yy)
-            return (sortAsc ? -1 : 1);
-        return (sortAsc ? 1 : -1);
-    }
-
-    targetArray.sort(sortFn);
-    return targetArray
-}
-
-/**
-* sorts an array with columns
-*
-* @param {Array} targetArray the array with data
-* @param {Array} sortOrder array with the format [columnIndex1, sortDescending1, columnIndex2, sortDescending2, ...],
-*                           the columnIndex must be an integer, sortDescending must be boolean (true -> descending, just like db.DESCENDING)
-* @example
-* ArrayUtils.sortMulti(rows, [1, true, 2, true, 5, false]);
-*
-* @return {void}
-*/
-ArrayUtils.sortMulti = function(targetArray, sortOrder) {
-    /*
-     * @param {String} a req value 1, first compared element
-     * @param {String} b req value 2, sencond compared element
-     *
-     * @return {Integer} -1 - set a below b, 0 - equal, 1 - set b below a 
-     */
-    var sortFn = function(a, b) {
-        var stringComparison = function(a, b) {
-            a = a.toLowerCase();
-            a = a.replace(/ä/g,"ae");
-            a = a.replace(/ö/g,"oe");
-            a = a.replace(/ü/g,"ue");
-            a = a.replace(/ß/g,"ss");
-
-            b = b.toLowerCase();
-            b = b.replace(/ä/g,"ae");
-            b = b.replace(/ö/g,"oe");
-            b = b.replace(/ü/g,"ue");
-            b = b.replace(/ß/g,"ss");
-
-            return( a == b ) ? 0 : ( a > b ) ? 1 : -1;
-        }
-        
-        var swap = 0;
-        
-        for (let i = 0, l = sortOrder.length; i < l; i += 2)
-        {
-            let colIndex = sortOrder[i];
-            let sortDesc = sortOrder[i+1];
-            if (swap || colIndex == undefined || sortDesc == undefined)
-                return swap;
-            
-            if (isNaN(a[colIndex] - b[colIndex]))
-                if ((isNaN(a[colIndex])) && (isNaN(b[colIndex])))
-                    swap =  stringComparison(a[colIndex], b[colIndex]);
-                else
-                    swap = (isNaN(a[colIndex]) ? 1 : -1);
-            else
-                swap = (a[colIndex] - b[colIndex]);
-            
-            swap *= (sortDesc ? -1 : 1);
-        }
-        return swap;
-    }
-
-    targetArray.sort(sortFn);
-    return targetArray;
-}
-
-/**
-* removes an specific element from an array
-*
-* @param {Array} targetArray Array from which the element should be removed
-* @param {String} elementPos  index of the element which should be removed
-*
-* @return {Array} array containing the deleted element
-*/
-ArrayUtils.removeElement = function(targetArray, elementPos) {
-    return targetArray.splice(elementPos, 1);
-}
-    
-/**
- * concats arrays column by column;
- * see example for more details
- *
- * @param {Array} array1 you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want
- * @param {Array} arrayN you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want
- *
- * @return {Array} concatenated array
- * 
- * @example
- * var a = ["a", "b", "c"];
- * var b = ["A", "B", "C"];
- * logging.show(JSON.stringify(concatArrayColumns(a, b)));
- * //[["a","A"],["b","B"],["c","C"]]
- *
- * logging.show(JSON.stringify(a.concat(b)));
- * //["a","b","c","A","B","C"]
- */
-ArrayUtils.concatColumns = function(array1, arrayN) {
-    var res, i, ii, l, ll, inpArr;
-    res = [];
-
-    for (i = 0, l = arguments.length; i < l; i++) {//this function can handle an "unlimited" amount of functionparams
-        inpArr = arguments[i];
-        for (ii = 0, ll = inpArr.length; ii < ll; ii++) {
-            if (res[ii] == undefined)
-                res[ii] = [];
-
-            res[ii] = res[ii].concat(inpArr[ii]);
-        }
-    }
-    return res;
-}
-
-
-/**
- * returns if an element is in an array; 
- * this is needed because there is currently no support for Array.prototype.includes() and we cannot easily use a polyfill and extend the Array.prototype
- *
- * @param {Array} targetArray array where the element should be searched in 
- * @param {AnyPrimitiveType} element the element which should be looked for
- * @param {Boolean} [ignoreStringCase=false] if you've got a string array y
- *
- * @return {Boolean} true if it has the element
- */
-ArrayUtils.hasElement = function(targetArray, element, ignoreStringCase) {
-    var i, l;
-    if (ignoreStringCase)//do only once to save ressources and not for every array element
-        element = element.toString().toLowerCase();
-    for (i = 0, l = targetArray.length; i < l; i++) {
-        if (ignoreStringCase) {
-            if (targetArray[i].toString().toLowerCase() == element)	
-                return true;
-        }
-        else {
-            if (targetArray[i] == element)
-                return true;
-        }
-    }
-    return false;
-}
-
-/**
- * joins an array but skips empty elements (null, undefined, "")
- * 
- * @param {Array} pArray the array to join
- * @param {String} [pSeparator=", "] the separator
- * 
- * @return {String} the resulting string
- */
-ArrayUtils.joinNonEmptyFields = function (pArray, pSeparator)
-{
-    return pArray.filter(function (element) 
-    {
-        return element !== null && element !== undefined && element !== "";
-    }).join(pSeparator);
-}
-
-/**
- * Class containing utility functions for use with JSON
- * @class
- */
-function JSONUtils() {
-}
-
-/**
- * A custom JSON.stringify() to
- * - keep the functions as string
- * - stringify JavaArrays
- * - stringify undefined as undefined and not as null
- *
- * @param {Object} obj the object to stringify
- *
- * @return {String} the stringified object as string representation
- */
-JSONUtils.customStringify = function(obj) {
-    //stringify part from JSON polyfill: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
-    var toString = Object.prototype.toString;
-    var escMap = {
-        '"': '\\"', 
-        '\\': '\\\\', 
-        '\b': '\\b', 
-        '\f': '\\f', 
-        '\n': '\\n', 
-        '\r': '\\r', 
-        '\t': '\\t'
-    };
-    var escFunc = function (m) {
-        return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1);
-    };
-    var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
-
-    var stringify = function (value){
-        //because: "undefined == null" is true
-        if (value === undefined) {
-            return 'undefined';
-        } else if (value == null) {
-            return 'null';
-        } else if (typeof value === 'number') {
-            return isFinite(value) ? value.toString() : 'null';
-        } else if (typeof value === 'boolean') {
-            return value.toString();
-        } else if (typeof value === 'object')
-{
-            //check with "hasOwnProperty" because in JavaArrays using value.toJSON would cause an exception
-            if (toString.call(value) != '[object Map]' && value.hasOwnProperty("toJSON") && typeof value.toJSON === 'function') {
-                return value.toString();
-            } else if (toString.call(value) === '[object Array]') {
-                var res = '[';
-                for (var i = 0; i < value.length; i++)
-                    res += (i ? ', ' : '') + stringify(value[i]);
-                return res + ']';
-            } else if (toString.call(value) === '[object Object]' || toString.call(value) === '[object Map]') {
-                var tmp = [];
-                for (var k in value) {
-                    if (hasOwnProperty.call(value, k))
-                        tmp.push(stringify(k) + ': ' + stringify(value[k]));
-                }
-                return '{' + tmp.join(', ') + '}';
-            //custom addition to stringify Rhino JavaArrays
-            } else if (toString.call(value) === '[object JavaArray]') {
-                return value.toSource().toString();
-            }
-        }
-        //custom addition for function transform
-        //JSON.stringify returns null on functions (default)
-        //instead return source code of funciton for callback-functions
-        else if (typeof value === 'function')
-        {
-            return value.toString();
-        }
-
-        return '"' + value.toString().replace(escRE, escFunc) + '"';
-    }
-
-    return stringify(obj);
-}
-
-/**
- * Class containing functions for Javascript-Objects
- * Do not create an instance of this
- * 
- * @class
- */
-function ObjectUtils(){}
-
-/**
- * checks if a value exists in the object
- * mostly usefull for primitve datatypes
- * 
- * @param pObject {Object} the object where the value is searched
- * @param pValue  {Boolean|Number|String} the value that is searched
- * 
- * @return {Boolean} true if the value was found, false if not
- */
-ObjectUtils.existsValue = function(pObject, pValue)
-{
-    for (var key in pObject)
-    {
-        if (pObject[key] === pValue)
-            return true;
-    }
-    return false;
-};
-
-/**
- * Class containing functions for sequential numbers
- * Do not create an instance of this!
- * 
- * @class
- */
-function NumberSequencingUtils() {}
-    
-/**
- * Delivers the next unique number
- *
- * @param {String} pColumn database column that contains unique numbers
- * @param {String} pTable database table
- * @param {Number} [pStartNumber=1000] number to start numeration
- * @param {String} [pCondition=no condition] SQL Where Conditon
- * 
- * @result {String} next valid number
- */
-NumberSequencingUtils.getNextUniqueNumber = function(pColumn, pTable, pStartNumber, pCondition) {
-    var maxNum = NumberSequencingUtils.getMaxUniqueNumber(pColumn, pTable, pCondition);
-    if(maxNum == "0") {
-        if(pStartNumber == undefined)   pStartNumber = 1000;
-        return pStartNumber;
-    }
-    return eMath.addInt(maxNum, "1");//increment currently highest number
-}
-    
-/**
- * Checks if the passed number is valid (has to be unique)
- * 
- * @param {String} pNumber number to check
- * @param {String} pColumn req database column that contains unique numbers
- * @param {String} pTable req database table
- * @param {String} pCondition opt SQL Where Conditon
- * 
- * @result {boolean} passed number is valid
- */
-NumberSequencingUtils.validateUniqueNumber = function(pNumber, pColumn, pTable, pCondition) {
-    var maxNum = NumberSequencingUtils.getMaxUniqueNumber(pColumn, pTable, pCondition);
-    return Number(pNumber) > Number(maxNum);
-}
-    
-/**
- * Delivers the hightest number currently stored in database
- * 
- * @param {String} pColumn req database column that contains unique numbers
- * @param {String} pTable req database table
- * @param {String} pCondition opt SQL Where Conditon
- * 
- * @result {String} hightest number
- */
-NumberSequencingUtils.getMaxUniqueNumber = function(pColumn, pTable, pCondition) {
-    var condition = "";
-    if(pCondition != undefined)
-        condition += " where " + pCondition;
-    var maxNum = db.cell("select max(" + pColumn + ") from " + pTable + condition);
-    return maxNum == "" ? "0" : maxNum;
-}
-
-/**
- * functions for trees
- * Do not create an instance of this!
- * 
- * @class
- */
-function TreeUtils () {}
-
-/**
- * sorts an array in a way that a tree(-table) can be built (parents are added before children)
- * This function does not garantee that the order of the children stays the same. If you need this, use TreeUtils.treeOrderBy
- * 
- * consider the use of TreeUtils.treeOrderBy as it may be more performant, but it needs the layernumber of each row.
- * 
- * @param {Array} pArray two-dimensional array to sort
- * @param {Number} pUidIndex the index of the uid in a row
- * @param {Number} pParentIdIndex the index of the parent id in a row
- * 
- * @return {Array} the sorted array
- */
-TreeUtils.sortArrayForTree = function (pArray, pUidIndex, pParentIdIndex) 
-{
-    if (pArray.length <= 1)
-        return pArray;
-    var rows = {};
-    var allIds = {};
-    
-    pArray.forEach(function (row) {allIds[row[pUidIndex]] = true;});
-    var index = 0;
-    
-    do {
-        var oldIndex = index;
-        pArray.forEach(function (row)
-        {
-            if (!(row[pUidIndex] in this) && (row[pParentIdIndex] in this || !allIds[row[pParentIdIndex]]))
-                this[row[pUidIndex]] = {
-                    data : row,
-                    index : index++
-                };
-        }, rows);
-        
-        /* stop if no new items were added, otherwise incorrect data (for instance 
-           an item that is it's own parent) could cause an infinite loop           */
-    } while (oldIndex != index); 
-    var sortedArray = new Array(index);
-    for (let i in rows)
-        sortedArray[rows[i].index] = rows[i].data;
-    return sortedArray;
-}
-
-/**
- * like TreeUtils.sortArrayForTree, this function garantees that parents are added before children
- * But it works in a different way based on the layer number.
- * 
- * It can also sort all children based on the given orderBys. For this you can Probvide an array of Indexes and direction (pOrderByIndexes)
- *  
- * @param {Array} pData two-dimensional array to sort
- * @param {Number} pLayerIndex The index of the layernumber-Field
- * @param {Array[][]} pOrderByIndexes Array containing arrays of [field-index, direction]. The direction can be true (desc) or false (asc).
- * 
- * @return {Array} the sorted array
- */
-TreeUtils.treeOrderBy = function(pData, pLayerIndex, pOrderByIndexes)
-{
-    pOrderByIndexes = [[pLayerIndex, false]].concat(pOrderByIndexes)
-    
-    return pData.sort(function(pRow1, pRow2) 
-    {
-        for (let i = 0; i < pOrderByIndexes.length; i++) {
-            var orderBy = pOrderByIndexes[i];
-            if (pRow1[orderBy[0]] > pRow2[orderBy[0]]) return (orderBy[1] ? -1 : 1);
-            if (pRow1[orderBy[0]] < pRow2[orderBy[0]]) return (orderBy[1] ? 1 : -1);
-        }
-
-        return 0;
-    })
-}
-
-/**
- * functions for numbered codes
- * Do not create an instance of this!
- * 
- * @class
- */
-function CodeUtils () {}
-/**
- * Sets the code of the given Table to the current max-code + 1
- */
-CodeUtils.setCode = function(pId, pTable, pIdCol, pCodeCol)
-{
-    var max = db.cell("select max(" + pCodeCol + ") from " + pTable);
-    if (!max)
-        max = -1;
-    
-    db.updateData(pTable, [pCodeCol], null, [parseInt(max)+1], SqlCondition.equals(pTable + "." + pIdCol, pId, "1=2"));
+import("Sql_lib");
+import("system.neon");
+import("system.project");
+import("system.process");
+import("system.db");
+import("system.util");
+import("system.translate");
+import("system.text");
+import("system.vars");
+import("system.swing");
+import("system.question");
+import("system.eMath");
+import("system.datetime");
+import("Offer_lib");
+import("Date_lib");
+
+/**
+ * Class containing static utility functions for string-actions
+ * Do not create an instance of this
+ * 
+ * @class
+ */
+function StringUtils(){}
+
+/**
+ * concats severel elements by a separator; the separator is only applied if a element is not null and not an empty string "";
+ *
+ * @param {String} pSeparator specifies how the not empty elements shall be concatenated
+ * @param {String[]} pElements elements that shall be joined by the separator 
+ *
+ * @return {String} concatenated string; if all elements are empty an emtpy string is returned
+ * 
+ */
+StringUtils.concat = function(pSeparator, pElements)
+{
+   var res = pElements.filter(function(e){
+       return e != null && e != "";
+   }).join(pSeparator);
+   return res;
+};
+
+
+/**
+ * converts a string to a string of always 36 chars. Whitespaces are added at the end if needed.
+ */
+StringUtils.pad36 = function(pValue) 
+{
+    return (pValue + "                                    ").slice(0, 36);
+}
+
+/**
+ * Class containing static utility functions for numbers
+ * Do not create an instance of this
+ * 
+ * @class
+ */
+function NumberUtils(){}
+
+/**
+ * Check iv the value is inside of the min / max values. 
+ * INCLUDING min / max
+ *
+ * @param {Number} pValue value to check
+ * @param {Number} pMin min value INCLUSIVE
+ * @param {Number} pMax max value INCLUSIVE 
+ * @param {Boolean} [pIgnoreNull=true] return True if pValue is null
+ * 
+ * @return {Boolean}
+ */
+NumberUtils.isInside = function(pValue, pMin, pMax, pIgnoreNull)
+{
+    if (pIgnoreNull == undefined)
+        pIgnoreNull = true;
+    
+    return pValue >= pMin && pValue <= pMax || pIgnoreNull && (pValue == null || isNaN(pValue));
+};
+
+/**
+ * For use in validationProcess. Calls result.string(...) with error message, if number is not inside of the given values
+ * INCLUDING min / max.
+ * 
+ * @param {Number} pTitle title to display in error message. Should be the name of the field and it will be translated.
+ * @param {Number} pValue value to check
+ * @param {Number} pMin min value INCLUSIVE
+ * @param {Number} pMax max value INCLUSIVE 
+ * @param {Boolean} [pIgnoreNull=true] return True if pValue is null
+ * 
+ * @return {String|False} returns the error message or false 
+ * 
+ * @example
+ *  var value = vars.get("local.value"); <br>
+ *   <br>
+ *  var validationResult = NumberUtils.validateIsBetweenFloat("Discount", value, 0, 100); <br>
+ *   <br>
+ *  if (validationResult) <br>
+ *  { <br>
+ *      result.string(validationResult); <br>
+ *  } <br>
+ */
+NumberUtils.validateIsBetweenFloat = function(pTitle, pValue, pMin, pMax, pIgnoreNull)
+{
+    if(pValue.includes(","))
+        pValue = pValue.replace(",", ".");
+    
+    var discount = parseFloat(pValue);
+    
+    if(isNaN(discount))
+        return false;
+    
+    if (!NumberUtils.isInside(discount, 0, 100, pIgnoreNull))
+    {
+        return (translate.withArguments("${MIN_MAX_ERROR} field: %0, value: %1, min: %2, max: %3", [translate.text(pTitle), discount, pMin, pMax]));
+    }
+    return false;
+}
+
+/**
+ * format Numbers with currency
+ */
+NumberUtils.formatWithCurrency = function(pNumber, pPattern, pCurrency)
+{
+    if (pNumber && pPattern)
+        return text.formatDouble(pNumber, pPattern + " " + pCurrency);
+    return "0 " + pCurrency;
+}
+
+/**
+ * Class containing static utility functions for use with arrays
+ * Do not create an instance of this!
+ * 
+ * @class
+ */
+function ArrayUtils() {}
+
+/**
+ * returns a distinct list of a two dimensional array
+ *
+ * @param {Array} p2dArray the Array where duplicate keys shall be removed
+ *
+ * @return 2D-Array that only contains unique entries
+ */
+ArrayUtils.distinct2d = function(p2dArray)
+{
+    var tempKeys = {};    
+    return p2dArray.filter(function(row){
+        var key = row.join("-");
+        return !(tempKeys[key] = ++tempKeys[key]|0)
+    });
+};
+
+/**
+ * sorts a two dimensional array by the given index
+ *
+ * @param {Array} targetArray the Array to be sorted
+ * @param {String} index the index of the field to sort by
+ * @param {Boolean} [sortAsc=false] TRUE sorts ascending, FALSE sorts decending
+ * @param {Boolean} [isNumber=false] TRUE sorts numerical, FALSE or undefined sorts alphanumerical
+ *
+ * @return targetArray
+ *
+ * @throws {RangeError} if index is outside pArray.length
+ */
+ArrayUtils.sort2d = function(targetArray, index, sortAsc, isNumber) {
+    if (targetArray.length == 0)
+        return targetArray;
+    if (targetArray[0].length == 0)
+        return targetArray;
+    if (index >= targetArray[0].length)
+        throw new RangeError("Index Out Of Bounds: " + index + " >= " + targetArray[0].length);
+
+    var sortFn = function (x, y) {
+        if ( isNumber ) {
+            xx = Number(x[index]);
+            yy = Number(y[index]);
+        }
+        else {
+            xx = x[index];
+            yy = y[index];
+
+            xx = xx.toLowerCase();
+            xx = xx.replace(/ä/g,"ae");
+            xx = xx.replace(/ö/g,"oe");
+            xx = xx.replace(/ü/g,"ue");
+            xx = xx.replace(/ß/g,"ss");
+
+            yy = yy.toLowerCase();
+            yy = yy.replace(/ä/g,"ae");
+            yy = yy.replace(/ö/g,"oe");
+            yy = yy.replace(/ü/g,"ue");
+            yy = yy.replace(/ß/g,"ss");
+        }
+        if (xx == yy)
+            return 0;
+        if (xx < yy)
+            return (sortAsc ? -1 : 1);
+        return (sortAsc ? 1 : -1);
+    }
+
+    targetArray.sort(sortFn);
+    return targetArray
+}
+
+/**
+* sorts an array with columns
+*
+* @param {Array} targetArray the array with data
+* @param {Array} sortOrder array with the format [columnIndex1, sortDescending1, columnIndex2, sortDescending2, ...],
+*                           the columnIndex must be an integer, sortDescending must be boolean (true -> descending, just like db.DESCENDING)
+* @example
+* ArrayUtils.sortMulti(rows, [1, true, 2, true, 5, false]);
+*
+* @return {void}
+*/
+ArrayUtils.sortMulti = function(targetArray, sortOrder) {
+    /*
+     * @param {String} a req value 1, first compared element
+     * @param {String} b req value 2, sencond compared element
+     *
+     * @return {Integer} -1 - set a below b, 0 - equal, 1 - set b below a 
+     */
+    var sortFn = function(a, b) {
+        var stringComparison = function(a, b) {
+            a = a.toLowerCase();
+            a = a.replace(/ä/g,"ae");
+            a = a.replace(/ö/g,"oe");
+            a = a.replace(/ü/g,"ue");
+            a = a.replace(/ß/g,"ss");
+
+            b = b.toLowerCase();
+            b = b.replace(/ä/g,"ae");
+            b = b.replace(/ö/g,"oe");
+            b = b.replace(/ü/g,"ue");
+            b = b.replace(/ß/g,"ss");
+
+            return( a == b ) ? 0 : ( a > b ) ? 1 : -1;
+        }
+        
+        var swap = 0;
+        
+        for (let i = 0, l = sortOrder.length; i < l; i += 2)
+        {
+            let colIndex = sortOrder[i];
+            let sortDesc = sortOrder[i+1];
+            if (swap || colIndex == undefined || sortDesc == undefined)
+                return swap;
+            
+            if (isNaN(a[colIndex] - b[colIndex]))
+                if ((isNaN(a[colIndex])) && (isNaN(b[colIndex])))
+                    swap =  stringComparison(a[colIndex], b[colIndex]);
+                else
+                    swap = (isNaN(a[colIndex]) ? 1 : -1);
+            else
+                swap = (a[colIndex] - b[colIndex]);
+            
+            swap *= (sortDesc ? -1 : 1);
+        }
+        return swap;
+    }
+
+    targetArray.sort(sortFn);
+    return targetArray;
+}
+
+/**
+* removes an specific element from an array
+*
+* @param {Array} targetArray Array from which the element should be removed
+* @param {String} elementPos  index of the element which should be removed
+*
+* @return {Array} array containing the deleted element
+*/
+ArrayUtils.removeElement = function(targetArray, elementPos) {
+    return targetArray.splice(elementPos, 1);
+}
+    
+/**
+ * concats arrays column by column;
+ * see example for more details
+ *
+ * @param {Array} array1 you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want
+ * @param {Array} arrayN you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want
+ *
+ * @return {Array} concatenated array
+ * 
+ * @example
+ * var a = ["a", "b", "c"];
+ * var b = ["A", "B", "C"];
+ * logging.show(JSON.stringify(concatArrayColumns(a, b)));
+ * //[["a","A"],["b","B"],["c","C"]]
+ *
+ * logging.show(JSON.stringify(a.concat(b)));
+ * //["a","b","c","A","B","C"]
+ */
+ArrayUtils.concatColumns = function(array1, arrayN) {
+    var res, i, ii, l, ll, inpArr;
+    res = [];
+
+    for (i = 0, l = arguments.length; i < l; i++) {//this function can handle an "unlimited" amount of functionparams
+        inpArr = arguments[i];
+        for (ii = 0, ll = inpArr.length; ii < ll; ii++) {
+            if (res[ii] == undefined)
+                res[ii] = [];
+
+            res[ii] = res[ii].concat(inpArr[ii]);
+        }
+    }
+    return res;
+}
+
+
+/**
+ * returns if an element is in an array; 
+ * this is needed because there is currently no support for Array.prototype.includes() and we cannot easily use a polyfill and extend the Array.prototype
+ *
+ * @param {Array} targetArray array where the element should be searched in 
+ * @param {AnyPrimitiveType} element the element which should be looked for
+ * @param {Boolean} [ignoreStringCase=false] if you've got a string array y
+ *
+ * @return {Boolean} true if it has the element
+ */
+ArrayUtils.hasElement = function(targetArray, element, ignoreStringCase) {
+    var i, l;
+    if (ignoreStringCase)//do only once to save ressources and not for every array element
+        element = element.toString().toLowerCase();
+    for (i = 0, l = targetArray.length; i < l; i++) {
+        if (ignoreStringCase) {
+            if (targetArray[i].toString().toLowerCase() == element)	
+                return true;
+        }
+        else {
+            if (targetArray[i] == element)
+                return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * joins an array but skips empty elements (null, undefined, "")
+ * 
+ * @param {Array} pArray the array to join
+ * @param {String} [pSeparator=", "] the separator
+ * 
+ * @return {String} the resulting string
+ */
+ArrayUtils.joinNonEmptyFields = function (pArray, pSeparator)
+{
+    return pArray.filter(function (element) 
+    {
+        return element !== null && element !== undefined && element !== "";
+    }).join(pSeparator);
+}
+
+/**
+ * Class containing utility functions for use with JSON
+ * @class
+ */
+function JSONUtils() {
+}
+
+/**
+ * A custom JSON.stringify() to
+ * - keep the functions as string
+ * - stringify JavaArrays
+ * - stringify undefined as undefined and not as null
+ *
+ * @param {Object} obj the object to stringify
+ *
+ * @return {String} the stringified object as string representation
+ */
+JSONUtils.customStringify = function(obj) {
+    //stringify part from JSON polyfill: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
+    var toString = Object.prototype.toString;
+    var escMap = {
+        '"': '\\"', 
+        '\\': '\\\\', 
+        '\b': '\\b', 
+        '\f': '\\f', 
+        '\n': '\\n', 
+        '\r': '\\r', 
+        '\t': '\\t'
+    };
+    var escFunc = function (m) {
+        return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1);
+    };
+    var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
+
+    var stringify = function (value){
+        //because: "undefined == null" is true
+        if (value === undefined) {
+            return 'undefined';
+        } else if (value == null) {
+            return 'null';
+        } else if (typeof value === 'number') {
+            return isFinite(value) ? value.toString() : 'null';
+        } else if (typeof value === 'boolean') {
+            return value.toString();
+        } else if (typeof value === 'object')
+{
+            //check with "hasOwnProperty" because in JavaArrays using value.toJSON would cause an exception
+            if (toString.call(value) != '[object Map]' && value.hasOwnProperty("toJSON") && typeof value.toJSON === 'function') {
+                return value.toString();
+            } else if (toString.call(value) === '[object Array]') {
+                var res = '[';
+                for (var i = 0; i < value.length; i++)
+                    res += (i ? ', ' : '') + stringify(value[i]);
+                return res + ']';
+            } else if (toString.call(value) === '[object Object]' || toString.call(value) === '[object Map]') {
+                var tmp = [];
+                for (var k in value) {
+                    if (hasOwnProperty.call(value, k))
+                        tmp.push(stringify(k) + ': ' + stringify(value[k]));
+                }
+                return '{' + tmp.join(', ') + '}';
+            //custom addition to stringify Rhino JavaArrays
+            } else if (toString.call(value) === '[object JavaArray]') {
+                return value.toSource().toString();
+            }
+        }
+        //custom addition for function transform
+        //JSON.stringify returns null on functions (default)
+        //instead return source code of funciton for callback-functions
+        else if (typeof value === 'function')
+        {
+            return value.toString();
+        }
+
+        return '"' + value.toString().replace(escRE, escFunc) + '"';
+    }
+
+    return stringify(obj);
+}
+
+/**
+ * Class containing functions for Javascript-Objects
+ * Do not create an instance of this
+ * 
+ * @class
+ */
+function ObjectUtils(){}
+
+/**
+ * checks if a value exists in the object
+ * mostly usefull for primitve datatypes
+ * 
+ * @param pObject {Object} the object where the value is searched
+ * @param pValue  {Boolean|Number|String} the value that is searched
+ * 
+ * @return {Boolean} true if the value was found, false if not
+ */
+ObjectUtils.existsValue = function(pObject, pValue)
+{
+    for (var key in pObject)
+    {
+        if (pObject[key] === pValue)
+            return true;
+    }
+    return false;
+};
+
+/**
+ * Class containing functions for sequential numbers
+ * Do not create an instance of this!
+ * 
+ * @class
+ */
+function NumberSequencingUtils() {}
+    
+/**
+ * Delivers the next unique number
+ *
+ * @param {String} pColumn database column that contains unique numbers
+ * @param {String} pTable database table
+ * @param {Number} [pStartNumber=1000] number to start numeration
+ * @param {String} [pCondition=no condition] SQL Where Conditon
+ * 
+ * @result {String} next valid number
+ */
+NumberSequencingUtils.getNextUniqueNumber = function(pColumn, pTable, pStartNumber, pCondition) {
+    var maxNum = NumberSequencingUtils.getMaxUniqueNumber(pColumn, pTable, pCondition);
+    if(maxNum == "0") {
+        if(pStartNumber == undefined)   pStartNumber = 1000;
+        return pStartNumber;
+    }
+    return eMath.addInt(maxNum, "1");//increment currently highest number
+}
+    
+/**
+ * Checks if the passed number is valid (has to be unique)
+ * 
+ * @param {String} pNumber number to check
+ * @param {String} pColumn req database column that contains unique numbers
+ * @param {String} pTable req database table
+ * @param {String} pCondition opt SQL Where Conditon
+ * 
+ * @result {boolean} passed number is valid
+ */
+NumberSequencingUtils.validateUniqueNumber = function(pNumber, pColumn, pTable, pCondition) {
+    var maxNum = NumberSequencingUtils.getMaxUniqueNumber(pColumn, pTable, pCondition);
+    return Number(pNumber) > Number(maxNum);
+}
+    
+/**
+ * Delivers the hightest number currently stored in database
+ * 
+ * @param {String} pColumn req database column that contains unique numbers
+ * @param {String} pTable req database table
+ * @param {String} pCondition opt SQL Where Conditon
+ * 
+ * @result {String} hightest number
+ */
+NumberSequencingUtils.getMaxUniqueNumber = function(pColumn, pTable, pCondition) {
+    var condition = "";
+    if(pCondition != undefined)
+        condition += " where " + pCondition;
+    var maxNum = db.cell("select max(" + pColumn + ") from " + pTable + condition);
+    return maxNum == "" ? "0" : maxNum;
+}
+
+/**
+ * functions for trees
+ * Do not create an instance of this!
+ * 
+ * @class
+ */
+function TreeUtils () {}
+
+/**
+ * sorts an array in a way that a tree(-table) can be built (parents are added before children)
+ * This function does not garantee that the order of the children stays the same. If you need this, use TreeUtils.treeOrderBy
+ * 
+ * consider the use of TreeUtils.treeOrderBy as it may be more performant, but it needs the layernumber of each row.
+ * 
+ * @param {Array} pArray two-dimensional array to sort
+ * @param {Number} pUidIndex the index of the uid in a row
+ * @param {Number} pParentIdIndex the index of the parent id in a row
+ * 
+ * @return {Array} the sorted array
+ */
+TreeUtils.sortArrayForTree = function (pArray, pUidIndex, pParentIdIndex) 
+{
+    if (pArray.length <= 1)
+        return pArray;
+    var rows = {};
+    var allIds = {};
+    
+    pArray.forEach(function (row) {allIds[row[pUidIndex]] = true;});
+    var index = 0;
+    
+    do {
+        var oldIndex = index;
+        pArray.forEach(function (row)
+        {
+            if (!(row[pUidIndex] in this) && (row[pParentIdIndex] in this || !allIds[row[pParentIdIndex]]))
+                this[row[pUidIndex]] = {
+                    data : row,
+                    index : index++
+                };
+        }, rows);
+        
+        /* stop if no new items were added, otherwise incorrect data (for instance 
+           an item that is it's own parent) could cause an infinite loop           */
+    } while (oldIndex != index); 
+    var sortedArray = new Array(index);
+    for (let i in rows)
+        sortedArray[rows[i].index] = rows[i].data;
+    return sortedArray;
+}
+
+/**
+ * like TreeUtils.sortArrayForTree, this function garantees that parents are added before children
+ * But it works in a different way based on the layer number.
+ * 
+ * It can also sort all children based on the given orderBys. For this you can Probvide an array of Indexes and direction (pOrderByIndexes)
+ *  
+ * @param {Array} pData two-dimensional array to sort
+ * @param {Number} pLayerIndex The index of the layernumber-Field
+ * @param {Array[][]} pOrderByIndexes Array containing arrays of [field-index, direction]. The direction can be true (desc) or false (asc).
+ * 
+ * @return {Array} the sorted array
+ */
+TreeUtils.treeOrderBy = function(pData, pLayerIndex, pOrderByIndexes)
+{
+    pOrderByIndexes = [[pLayerIndex, false]].concat(pOrderByIndexes)
+    
+    return pData.sort(function(pRow1, pRow2) 
+    {
+        for (let i = 0; i < pOrderByIndexes.length; i++) {
+            var orderBy = pOrderByIndexes[i];
+            if (pRow1[orderBy[0]] > pRow2[orderBy[0]]) return (orderBy[1] ? -1 : 1);
+            if (pRow1[orderBy[0]] < pRow2[orderBy[0]]) return (orderBy[1] ? 1 : -1);
+        }
+
+        return 0;
+    })
+}
+
+/**
+ * functions for numbered codes
+ * Do not create an instance of this!
+ * 
+ * @class
+ */
+function ConsecutiveCodeUtils () {}
+/**
+ * Sets the code of the given Table to the current max-code + 1
+ */
+ConsecutiveCodeUtils.setCode = function(pId, pTable, pIdCol, pCodeCol)
+{
+    var max = db.cell("select max(" + pCodeCol + ") from " + pTable);
+    if (!max)
+        max = -1;
+    
+    db.updateData(pTable, [pCodeCol], null, [parseInt(max)+1], SqlCondition.equals(pTable + "." + pIdCol, pId, "1=2"));
 }
\ No newline at end of file
diff --git a/process/_test_clientProcess/process.js b/process/_test_clientProcess/process.js
index 9161a9a2bede588a70ca5152ecd6bf9e7e70bede..273b7362004c369f828a237f97eafb777ee393d7 100644
--- a/process/_test_clientProcess/process.js
+++ b/process/_test_clientProcess/process.js
@@ -1,20 +1,21 @@
-import("system.datetime");
-import("system.util");
-import("system.logging");
-import("Liquibase_lib");
-import("system.db");
-import("system.fileIO");
-
-var alias = "_____SYSTEMALIAS";
-//var alias = "betterDataSys";
-//var alias = "betterData";
-//var alias = db.getCurrentAlias();
-var outFolderPath = "C:\\temp\\generatedData\\" + alias + "\\";
-
-var excludedTables = ["AB_COUNTRYINFO", "AB_LANGUAGE"];
-//LiquiUtils.exportAllTablesAsLiquibaseFiles(outFolderPath, null, alias, excludedTables, true);
-
-alias = "_____SYSTEMALIAS";
-outFolderPath = "C:\\temp\\generatedData\\" + alias + "\\";
-LiquiUtils.exportTableAsLiquibaseFiles(outFolderPath, "ASYS_USERS", null, "PROPKEY in ('mailserverAlias', 'userserverEnabled') ", null, false, alias);
+import("Sql_lib");
+import("system.datetime");
+import("system.util");
+import("system.logging");
+import("Liquibase_lib");
+import("system.db");
+import("system.fileIO");
+
+var alias = SqlUtils.getSystemAlias();
+//var alias = "betterDataSys";
+//var alias = "betterData";
+//var alias = db.getCurrentAlias();
+var outFolderPath = "C:\\temp\\generatedData\\" + alias + "\\";
+
+var excludedTables = ["AB_COUNTRYINFO", "AB_LANGUAGE"];
+//LiquiUtils.exportAllTablesAsLiquibaseFiles(outFolderPath, null, alias, excludedTables, true);
+
+alias = SqlUtils.getSystemAlias();
+outFolderPath = "C:\\temp\\generatedData\\" + alias + "\\";
+LiquiUtils.exportTableAsLiquibaseFiles(outFolderPath, "ASYS_USERS", null, "PROPKEY in ('mailserverAlias', 'userserverEnabled') ", null, false, alias);
 logging.log("finish");
\ No newline at end of file