diff --git a/entity/Appointment_entity/Appointment_entity.aod b/entity/Appointment_entity/Appointment_entity.aod
index d615ac8db071653c41b13edc8e574505ab3f4b50..ebfd0f83c692dbda16668ec3f20a2da687b74c0e 100644
--- a/entity/Appointment_entity/Appointment_entity.aod
+++ b/entity/Appointment_entity/Appointment_entity.aod
@@ -270,6 +270,7 @@
       <jDitoRecordAlias>Data_alias</jDitoRecordAlias>
       <contentProcess>%aditoprj%/entity/Appointment_entity/recordcontainers/jdito/contentProcess.js</contentProcess>
       <rowCountProcess>%aditoprj%/entity/Appointment_entity/recordcontainers/jdito/rowCountProcess.js</rowCountProcess>
+      <hasDependentRecords v="true" />
       <onInsert>%aditoprj%/entity/Appointment_entity/recordcontainers/jdito/onInsert.js</onInsert>
       <onUpdate>%aditoprj%/entity/Appointment_entity/recordcontainers/jdito/onUpdate.js</onUpdate>
       <onDelete>%aditoprj%/entity/Appointment_entity/recordcontainers/jdito/onDelete.js</onDelete>
diff --git a/entity/Appointment_entity/afterUiInit.js b/entity/Appointment_entity/afterUiInit.js
index 84f0515ebf4c6c9ba00bb4a071bacebe74fbaace..d1a3fedb127f027d614ce245fc9901599743d2a3 100644
--- a/entity/Appointment_entity/afterUiInit.js
+++ b/entity/Appointment_entity/afterUiInit.js
@@ -2,7 +2,7 @@ import("system.util");
 import("system.neon");
 import("system.vars");
 
-if(vars.exists("$param.Entry_param") && vars.get("$param.Entry_param"))
+if(vars.get("$param.Entry_param"))
 {
     var entry = JSON.parse(vars.getString("$param.Entry_param"));
    
diff --git a/entity/Appointment_entity/recordcontainers/jdito/contentProcess.js b/entity/Appointment_entity/recordcontainers/jdito/contentProcess.js
index 709bfac8d00e3b805a383dc0c78c0cb8b91a07d1..a2b8753c1cf9bcd5783b27f12226693770d0e7af 100644
--- a/entity/Appointment_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/Appointment_entity/recordcontainers/jdito/contentProcess.js
@@ -18,12 +18,12 @@ var appointmentUids;
 /**
  * Will be used, if the user is operating the calendar.
  */
-if(vars.exists("$param.Entry_param") && vars.get("$param.Entry_param"))
+if(vars.get("$param.Entry_param") && JSON.parse(vars.get("$param.Entry_param"))[calendars.ID] != undefined)
 {
     var entry = JSON.parse(vars.getString("$param.Entry_param"));
 
     var masterEntry = null;
-    if (vars.exists("$param.MasterEntry_param") && vars.get("$param.MasterEntry_param") != "") {
+    if (vars.get("$param.MasterEntry_param") != "") {
         masterEntry = JSON.parse(vars.getString("$param.MasterEntry_param"));
     }
 
diff --git a/entity/Appointment_entity/recordcontainers/jdito/onInsert.js b/entity/Appointment_entity/recordcontainers/jdito/onInsert.js
index 21d79d0f9f82bfa904e33604db62defa2ca9733f..548745a3cf60a6dfa340e0dec3315f2345f18534 100644
--- a/entity/Appointment_entity/recordcontainers/jdito/onInsert.js
+++ b/entity/Appointment_entity/recordcontainers/jdito/onInsert.js
@@ -8,6 +8,7 @@ import("system.neon");
 import("system.vars");
 import("system.text");
 import("system.db");
+import("system.entities");
 
 var event = JSON.parse(vars.getString("$param.Entry_param"));
 
@@ -22,36 +23,23 @@ event[calendars.LOCATION] = fields["LOCATION.value"];
 event[calendars.DESCRIPTION] = fields["DESCRIPTION.value"];
 event[calendars.DTSTART] = fields["BEGIN.value"];
 event[calendars.DTEND] = fields["END.value"];
-event["X-ADITO-ISALLDAYEVENT"] = fields["ALLDAY.value"];
 event[calendars.CLASSIFICATION] = fields["CLASSIFICATION.value"];
 event[calendars.TRANSPARENCY] = fields["TRANSPARENCY.value"];
 event[calendars.CATEGORIES] = fields["CATEGORIES.value"];
 event[calendars.ORGANIZER] = fields["ORGANIZER.value"];
+event["X-ADITO-ISALLDAYEVENT"] = fields["ALLDAY.value"];
 
 if(fields["RRULE.value"])
+{
     event[calendars.RRULE] = [fields["RRULE.value"]];
+}
 if (fields["REMINDER.value"])
 {
     event[calendars.HASREMINDER] = "true";
     event[calendars.REMINDER_DURATION] = fields["REMINDER.value"];
 }
 
-
-var idstringarray = calendars.insert([event]);
-
-if(event["LINKS"])
-{
-    event["LINKS"].forEach(function(pLink){
-        neon.addRecord("AppointmentLinks",
-        {
-            "OBJECTID" : pLink["OBJECT_ID"],
-            "OBJECTTYPE" : pLink["OBJECT_TYPE"],
-            "APPOINTMENT_ID" : idstringarray[0]
-        });
-    })
-}
-
-event[calendars.ID] = idstringarray[0];
+event[calendars.ID] = calendars.insert([event])[0];
 neon.setFieldValue("$field.UID", event[calendars.ID]);
 
 vars.set("$context.editmode", calendars.MODE_UPDATE);
diff --git a/entity/DocumentTemplate_entity/entityfields/openhtmleditor/stateProcess.js b/entity/DocumentTemplate_entity/entityfields/openhtmleditor/stateProcess.js
index c0824ad21cb8d86f3e4cd4b0729d393cb7db7870..718a96109027807c05614a94925ff71302ec524c 100644
--- a/entity/DocumentTemplate_entity/entityfields/openhtmleditor/stateProcess.js
+++ b/entity/DocumentTemplate_entity/entityfields/openhtmleditor/stateProcess.js
@@ -7,5 +7,12 @@ import("system.neon");
 var template = DocumentTemplateUtils.getTemplate(vars.get("$field.DOCUMENTTEMPLATEID"), false);
 var kind = vars.get("$field.KIND");
 
-if(template.type == DocumentTemplate.types.HTML && kind == $KeywordRegistry.documentTemplateType$textModular() || kind == $KeywordRegistry.documentTemplateType$mail())
-    result.string(neon.COMPONENTSTATE_EDITABLE);
+if (template)
+{
+    if(template.type == DocumentTemplate.types.HTML && 
+        kind == $KeywordRegistry.documentTemplateType$textModular() || 
+        kind == $KeywordRegistry.documentTemplateType$mail())
+    {
+        result.string(neon.COMPONENTSTATE_EDITABLE);
+    }
+}
diff --git a/entity/Email_entity/entityfields/sendmail/onActionProcess.js b/entity/Email_entity/entityfields/sendmail/onActionProcess.js
index 0ee0af9d3e78ea7027cafebd667d54814c15e43c..8c93bbcfad19c8a77e2d8aa228f336723f2b466c 100644
--- a/entity/Email_entity/entityfields/sendmail/onActionProcess.js
+++ b/entity/Email_entity/entityfields/sendmail/onActionProcess.js
@@ -30,7 +30,7 @@ if (vars.exists("$param.AdditionalPlaceholders_param") && vars.get("$param.Addit
     });
 }
 
-var eml = EmailWritingUtils.openMailTemplate(
+EmailWritingUtils.openMailTemplate(
     vars.get("$field.RECIPIENT"), 
     EmployeeUtils.getCurrentContactId(), 
     vars.get("$field.DOCUMENT_TEMPLATE"), 
diff --git a/entity/Letter_entity/entityfields/downloadletterandcreateactivity/onActionProcess.js b/entity/Letter_entity/entityfields/downloadletterandcreateactivity/onActionProcess.js
index 0c453f91c3e8e31847be4c6cca575257597a7650..69b7b7b8cb88c489754264203262574b6ff4e2be 100644
--- a/entity/Letter_entity/entityfields/downloadletterandcreateactivity/onActionProcess.js
+++ b/entity/Letter_entity/entityfields/downloadletterandcreateactivity/onActionProcess.js
@@ -33,6 +33,4 @@ if (template)
     }
 
     ActivityUtils.createNewActivity(null, links, null, null, translate.text("Letter"), text.parseDocument(content), $KeywordRegistry.activityDirection$outgoing(), [[template.filename, content, false]]);
-}
-else
-    throw new Error("Error while using a document template: The provided template does not contain data.");
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/entity/Offer_entity/entityfields/contact_id/stateProcess.js b/entity/Offer_entity/entityfields/contact_id/stateProcess.js
index e51ff93affb4e84e12bff1e0a4a0145c88fc2dd2..0dcccb84c7750c4743ac3fb30a625052af4e8830 100644
--- a/entity/Offer_entity/entityfields/contact_id/stateProcess.js
+++ b/entity/Offer_entity/entityfields/contact_id/stateProcess.js
@@ -3,4 +3,11 @@ import("system.result");
 import("system.neon");
 import("system.vars");
 
-result.string(OfferUtils.isEditable(vars.get("$field.STATUS")) ? neon.COMPONENTSTATE_AUTO : neon.COMPONENTSTATE_DISABLED);
+if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT)
+{
+    result.string(neon.COMPONENTSTATE_READONLY);
+}
+else
+{
+    result.string(OfferUtils.isEditable(vars.get("$field.STATUS")) ? neon.COMPONENTSTATE_AUTO : neon.COMPONENTSTATE_DISABLED);
+}
diff --git a/entity/Organisation_entity/Organisation_entity.aod b/entity/Organisation_entity/Organisation_entity.aod
index b8e0c8e551c964283d0876a372462282547610c8..be40c333adc19fbc28ccd8715d0596b2f151a47f 100644
--- a/entity/Organisation_entity/Organisation_entity.aod
+++ b/entity/Organisation_entity/Organisation_entity.aod
@@ -1436,6 +1436,7 @@
       <title>New appointment</title>
       <onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/newappointment/onActionProcess.js</onActionProcess>
       <iconId>VAADIN:CALENDAR</iconId>
+      <state>EDITABLE</state>
       <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/newappointment/stateProcess.js</stateProcess>
       <tooltip>New Appointment</tooltip>
     </entityActionField>
diff --git a/entity/Person_entity/Person_entity.aod b/entity/Person_entity/Person_entity.aod
index f294673d397ad7e5b12ab7f16821daa5d3c0d75b..d318ebbdde32ccfe73c757d1e904246333556526 100644
--- a/entity/Person_entity/Person_entity.aod
+++ b/entity/Person_entity/Person_entity.aod
@@ -1453,6 +1453,7 @@
       <title>New appointment</title>
       <onActionProcess>%aditoprj%/entity/Person_entity/entityfields/newappointment/onActionProcess.js</onActionProcess>
       <iconId>VAADIN:CALENDAR</iconId>
+      <state>EDITABLE</state>
       <stateProcess>%aditoprj%/entity/Person_entity/entityfields/newappointment/stateProcess.js</stateProcess>
       <tooltip>New Appointment</tooltip>
     </entityActionField>
diff --git a/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/onActionProcess.js b/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/onActionProcess.js
index b760e1613d9594960cabd2067bade94a03105560..6de5e67211d4a10723765b058e83b58c8cdf6530 100644
--- a/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/onActionProcess.js
+++ b/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/onActionProcess.js
@@ -25,12 +25,10 @@ var summary = translate.text("Site visit") + " || " + fullName;
 var description = fullName;
 var standardMail = CommUtil.getStandardMail(selectionRowData[0].CONTACT_ID); 
 
-var startTime = selectionRowData[0].BEGIN_TIME;
-var endTime = selectionRowData[0].END_TIME;
-var duration = eMath.subInt(endTime, startTime);
-var entryDate = datetime.toLocaleDate(selectionRowData[0].ENTRYDATE, "dd-MM-yyyy");
-startTime = entryDate + " " + datetime.toLocaleDate(selectionRowData[0].BEGIN_TIME, "HH:mm:ss.S");
-startTime = datetime.toLong(startTime, "dd-MM-yyyy HH:mm:ss.S", "UTC"); // #1076044 set tz to prevent time gaps.
+//creates an js date object with the current utc time and adds the appointment begin/end time.
+//necessary for CalendarUtil.createEntry() 
+var start = new Date(datetime.today(vars.get("$sys.timezone"))+Number(selectionRowData[0].BEGIN_TIME));
+var end = new Date(datetime.today(vars.get("$sys.timezone"))+Number(selectionRowData[0].END_TIME));
 
 var links = [
     {
@@ -44,8 +42,8 @@ var links = [
 ];
 
 var params = {
-    "Entry_param": JSON.stringify(CalendarUtil.createEntry(summary, description, links, undefined, undefined, Date(Date.toExponential(startTime)),
-                                                                Date(Date.toExponential(endTime)), undefined, undefined, undefined, [standardMail], 
+    "Entry_param": JSON.stringify(CalendarUtil.createEntry(summary, description, links, undefined, undefined, start,
+                                                                end, undefined, undefined, undefined, [standardMail], 
                                                                 undefined, undefined, undefined))
 };
 
diff --git a/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/stateProcess.js b/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/stateProcess.js
index 3f986eb99cdd8c56ea85788bc2ab0f547f172b25..181e70bbeca24bebbcad6595818e49b2e55484ad 100644
--- a/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/stateProcess.js
+++ b/entity/VisitPlanEntry_entity/entityfields/entityactiongroup/children/newappointment/stateProcess.js
@@ -2,9 +2,14 @@ import("system.logging");
 import("system.result");
 import("system.vars");
 import("system.neon");
+import("Util_lib");
 
-if (vars.get("$field.ISGROUP") == "false" && vars.get("$sys.selectionRows") != "" 
+if (Utils.toBoolean(vars.get("$field.ISGROUP")) 
         && (vars.get("$field.STATUS") == "VISITSTATUSAPPPLANED"
-        || vars.get("$field.STATUS_APPOINTMENT") == "VISITSTATUSAPPOINTMENTCONFIRMED     "))
-    result.string(neon.COMPONENTSTATE_DISABLED);
+        || vars.get("$field.STATUS_APPOINTMENT") == "VISITSTATUSAPPOINTMENTCONFIRMED     ")
+        || Utils.isNullOrEmpty(vars.get("$sys.selectionRows"))
+        || Utils.isNullOrEmpty(vars.get("$field.PARENT_ID")))
+{
+    result.string(neon.COMPONENTSTATE_DISABLED);    
+}
 
diff --git a/process/CalendarUtil_test/CalendarUtil_test.aod b/process/CalendarUtil_test/CalendarUtil_test.aod
new file mode 100644
index 0000000000000000000000000000000000000000..003d70031c462bade2eda8dbf49aa510a48faf9f
--- /dev/null
+++ b/process/CalendarUtil_test/CalendarUtil_test.aod
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
+  <name>CalendarUtil_test</name>
+  <title>[TEST] Calendar_lib</title>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <icon>VAADIN:CHECK_CIRCLE</icon>
+  <process>%aditoprj%/process/CalendarUtil_test/process.js</process>
+  <alias>Data_alias</alias>
+  <variants>
+    <element>EXECUTABLE</element>
+  </variants>
+</process>
diff --git a/process/CalendarUtil_test/process.js b/process/CalendarUtil_test/process.js
new file mode 100644
index 0000000000000000000000000000000000000000..6c1808870af916388b07ef1d865e2658de2ff71f
--- /dev/null
+++ b/process/CalendarUtil_test/process.js
@@ -0,0 +1,82 @@
+import("system.calendars");
+import("system.result");
+import("system.db");
+import("UnitTest_lib");
+import("Calendar_lib");
+import("Sql_lib");
+
+
+var testUids = [];
+var tester = new Tester("Test Calendar_lib");
+var newSilentEvent = new TestSuite("CalendarUtil.newSilentEvent", [
+    new Test("Should create an appointment without graphical user interaction.", function(pTester, pDataProvider){
+        var uid = CalendarUtil.newSilentEvent(pDataProvider[0], pDataProvider[1], pDataProvider[2], pDataProvider[3], pDataProvider[4], 
+            pDataProvider[5], pDataProvider[6], pDataProvider[7], pDataProvider[8], pDataProvider[9], pDataProvider[10], pDataProvider[11], 
+            pDataProvider[12], pDataProvider[13]);
+            
+            var doesLinksExists;
+            var doesExists = Number(newSelect("count(*)", SqlUtils.getSystemAlias())
+                                    .from("asys_calendarbackend")
+                                    .where("asys_calendarbackend.elementuid", uid)
+                                    .cell());
+            
+            pTester.expectThat(doesExists).isGreaterEqual(1).assert();          
+            
+            if(pDataProvider[2] && doesExists) 
+            {
+                doesLinksExists = newSelect("ab_appointmentlink_id")
+                                        .from("ab_appointmentlink")
+                                        .where("ab_appointmentlink.appointment_id", uid)
+                                        .arrayColumn();
+
+                pTester.expectThat(doesLinksExists).hasMinLength(1).assert();
+            }
+            
+            doesLinksExists.length > 0 ? testUids.push([uid, doesLinksExists]) : testUids.push([uid]);
+    }, function(){
+        return [
+          ["Testtermin 1","Testbeschreibung",[{"OBJECT_ID":"c7ddf982-0e58-4152-b82b-8f5673b0b729", "OBJECT_TYPE":"Person"}], "Admin", ["Admin"], 
+            new Date("2021", "02", "29", "08", "30"), new Date("2021", "02", "29", "08", "45"), ["Meeting"], undefined, undefined, undefined, 
+                "Microsoft Teams", false, calendars.CLASSIFICATION_PUBLIC],      
+          ["Testtermin 2","Testbeschreibung",
+              [{"OBJECT_ID":"c7ddf982-0e58-4152-b82b-8f5673b0b729", "OBJECT_TYPE":"Person"},
+              {"OBJECT_ID":"18ed06df-30b2-4d59-a4b4-a6e646f6f05a", "OBJECT_TYPE":"Activity"}, 
+              {"OBJECT_ID":"449080f6-b714-4189-a261-37439d0d4010", "OBJECT_TYPE":"Organisation"}, 
+              {"OBJECT_ID":"e9cb198d-c420-4192-9c29-b23682457d8e", "OBJECT_TYPE":"Task"}], 
+              "Admin", ["Admin"], new Date("2021", "03", "08", "14", "30"), new Date("2021", "03", "08", "15", "45"), ["Meeting", "OutOfOffice"], 
+              calendars.STATUS_ACCEPTED, new Date("2021", "03", "08", "14", "15"), 
+              ["herbert.obermaier@hotmail.de", "lisa.lustig@yahoo.com"], "Microsoft Teams", false, calendars.CLASSIFICATION_PUBLIC],
+          ["Testtermin 3","Testbeschreibung",null, "Admin", ["Admin"], 
+            new Date("2021", "03", "29", "10", "00"), new Date("2021", "03", "29", "11", "00"), ["Vacation"], undefined, undefined, undefined, 
+                "Microsoft Teams", false, calendars.CLASSIFICATION_PUBLIC],
+          ["Testtermin 4","Testbeschreibung",[{"OBJECTID": "d8f35764-2c56-45be-93c7-f1e0695e2417", "OBJECT_TYPE":"Task"}], "Admin", ["Admin"], 
+            new Date("2021", "03", "29", "10", "00"), new Date("2021", "03", "29", "11", "00"), ["Vacation"], undefined, undefined, undefined, 
+                "Microsoft Teams", false, calendars.CLASSIFICATION_PUBLIC],
+        ];
+    })
+], null, null, function(){
+    for (i = 0; i < testUids.length; i++) 
+    {
+        if(testUids[i].length > 1)
+        {
+            newWhere("asys_calendarbackend.elementuid", testUids[i][0], null, null, SqlUtils.getSystemAlias())
+                .deleteData();  
+            testUids[i][1].forEach(function(pLinkUid){
+                newWhere("ab_appointmentlink.ab_appointmentlink_id", pLinkUid, null, null, db.getCurrentAlias())
+                    .deleteData();
+            });
+        }
+        else 
+        {
+            newWhere("asys_calendarbackend.elementuid", testUids[i][0], null, null, SqlUtils.getSystemAlias())
+                .deleteData();
+        }
+    }
+});
+
+tester.initCoverage(CalendarUtil);
+tester.test(newSilentEvent);
+
+tester.summary();
+
+result.object(tester.getResults());
\ No newline at end of file
diff --git a/process/Calendar_lib/process.js b/process/Calendar_lib/process.js
index 455b28d263c1e6def680391206245b213240c4e8..de9bc373d0fee6098f61111dfa3ae96f8c5ab735 100644
--- a/process/Calendar_lib/process.js
+++ b/process/Calendar_lib/process.js
@@ -1,657 +1,658 @@
-import("system.translate");
-import("system.datetime");
-import("system.neon");
-import("system.calendars");
-import("system.vars");
-import("system.db");
-import("system.swing");
-import("system.eMath");
-import("system.logging");
-import("system.tools");
-import("system.text");
-import("system.question");
-import("system.SQLTYPES");
-import("system.result");
-import("system.util");
-import("system.entities");
-import("Util_lib");
-import("Sql_lib");
-
-
-/**
- * Functions for the calendar.
- * <p>
- * <b><u>Do not create an instance of this!</u></b>
- * @class
- */
-function CalendarUtil(){}
-
-
-/*
- * Creates and opens an new task object (with link).
- *
- * @param {String} pSummary (optional)              <p>
- *                                                  The summary.<br>
- * @param {String} pDescription (optional)          <p>
- *                                                  The description.<br>
- * @param {Boolean} pWithLink (optional)            Case if its true, then an a shortcut to $image.frametable will created.<br>
- * @param {String[][]} pWithLink (optional)         <p>
- *                                                  The required informations:<br>
- *                                                  <ul>
- *                                                  <li>pWithLink[0]: Name of the frame.</li>
- *                                                  <li>pWithLink[1]: Id of the shown record.</li>
- *                                                  <li>pWithLink[2]: Linking title.</li>
- * @param {String} pUser (optional)                 <p>
- *                                                  The user (login).<br>
- * @param {[]} pAffectedUsers (optional)            <p>
- *                                                  The affected users. (login)<br>
- * @param {date} pStart (optional)                  <p>
- *                                                  Start of the task.<br>
- * @param {date} pDuration (optional)               <p>
- *                                                  Duration of the task.<br>
- * @param {integer} pCategory (optional)            <p>
- *                                                  calendars.CATEGORIES , encoded(String) (e.g.: text.encodeMS(["Service"]))
- * @param {String} pStatus (optional)               <p>
- *                                                  Status of the appointment. (calendars.STATUS_TENTATIVE, <br>
- *                                                  calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED)<br>
- * @param {Array{[]} pComps4Refresh (optional)      <p>
- *                                                  The component which will be updated.<br>
- *
- * @return {void}
- */
-CalendarUtil.newTodo = function(pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh)
-{
-    var todo = CalendarUtil.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 <br>
- * attribute order like the ADITO core, which is:<br>
- * <p>
- * exchangeEmail -> calendarID -> email<br>
- * <p>
- * <b><u>DO NOT CHANGE THIS ORDER!</u></b>
- *
- * @param {String} pUser            <p>
- *                                  To check.
- * @return                          <p>
- *                                  Effective calendar id.<br>
- */
-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 exchangeEmail;
-    else if(calendarId)
-        return calendarId;
-    else if(email)
-        return email;
-    else
-        return "";
-}
-
-
-/*
- * Creates and opens an new appointment object (with link).
- *
- * @param {String} pSummary (optional)          <p>
- *                                              The summary.<br>
- * @param {String} pDescription (optional)      <p>
- *                                              The description.<br>
- * @param {Boolean} pWithLink (optional)        <p>
- *                                              True sets an link to $image.frametable<br>
- * @param {String[][]} pWithLink (optional)     Description:<br>
- *                                              <ul>
- *                                              <li>pWithLink[0]: Name of the frame</li>
- *                                              <li>pWithLink[1]: Id of the shown record</li>
- *                                              <li>pWithLink[2]: linking title</li>
- *                                              </ul>
- * @param {String} pUser (optional)             <p>
- *                                              The user (login).
- * @param {[]} pAffectedUsers (optional)        <p>
- *                                              The affected users (login).
- * @param {Date} pStart (optional)              <p>
- *                                              Begin of the task.<br>
- * @param {Date} pDuration (optional)           <p>
- *                                              Duration.<br>
- * @param {Number} pCategory (optional)         <p>
- *                                              calendars.CATEGORIES , encoded(String) (z.B.: text.encodeMS(["Service"])).<br>
- * @param {String} pStatus (optional)           <p>
- *                                              Status of the appointment:<br>
- *                                              <ul>
- *                                              <li>calendars.STATUS_TENTATIVE</li>
- *                                              <li>calendars.STATUS_CONFIRMED</li>
- *                                              <li>calendars.STATUS_CANCELLED</li>
- *                                              </ul>
- * @param {Array{[]} pComps4Refresh (optional)  <p>
- *                                              The component which will be updated.<br>
- * @param {Array{[]} pWorklistId (optional)     <p>
- *                                              The worklist id.<br>
- * 
- * @return {void}
- */
-CalendarUtil.newEvent = function( pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh, pWorklistId)
-{
-    var event = CalendarUtil.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 (let 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);
-    }
-}
-
-
-/*
- * Creates an new appointment entry.
- *
- * @param {String} pSummary (optional)              <p>
- *                                                  The summary/title of the appointment.
- * @param {String} pDescription (optional)          <p>
- *                                                  The appointment description.
- * @param {String} pLinks (optional)                <p>
- *                                                  The links as objects <u>(key: "OBJECT_ID" & "OBJECT_TYPE")</u> in an array.
- * @param {String} pOwner (optional)                <p>
- *                                                  The calendar-user (username) which will be specified as entry-owner.
- * @param {String[]} pAffectedUsers (optional)      <p>
- *                                                  The affected users (username).
- * @param {Date} pStart (optional)                  <p>
- *                                                  The start of the appointment.
- * @param {Date} pEnd (optional)                    <p>
- *                                                  The end of the appointment.
- * @param {String[]} pCategories (optional)         <p>
- *                                                  The categories of the appointment, the default ones are:<br>
- *                                                  <ul>
- *                                                  <li>Meeting</li>
- *                                                  <li>Organisation</li>
- *                                                  <li>OutOfOffice</li>
- *                                                  <li>Vacation</li>
- *                                                  </ul>
- * @param {String} pStatus (optional)               <p>
- *                                                  Status of the appointment:<br>
- *                                                  <ul>
- *                                                  <li>calendars.STATUS_TENTATIVE</li>
- *                                                  <li>calendars.STATUS_CONFIRMED</li>
- *                                                  <li>calendars.STATUS_CANCELLED</li>
- *                                                  </ul>
- * @param {Date} pReminder (optional)               <p>
- *                                                  Date of the reminder for the appointment.
- * @param {String[]} pExternalAttendees (optional)  <p>
- *                                                  External attendees.
- * @param {String} pLocation (optional)             <p>
- *                                                  The location of the appointment.
- * @param {Boolean} pIsAllDay (optional)            <p>
- *                                                  Whether if it is an all-day appointment or not.
- * @param {String} pClassification (optional)       <p>
- *                                                  The classification of the appointment:<br>
- *                                                  <ul>
- *                                                  <li>calendars.CLASSIFICATION_PUBLIC</li>
- *                                                  <li>calendars.CLASSIFICATION_PRIVATE</li>
- *                                                  </ul>
- * @return {void}
- */
-CalendarUtil.newSilentEvent = function(pSummary, pDescription, pLinks, pOwner, pAffectedUsers, pStart, pEnd, pCategories, pStatus, 
-                                            pReminder, pExternalAttendees, pLocation, pIsAllDay, pClassification)
-{   
-    var event = CalendarUtil.createEntry(pSummary, pDescription, pLinks, pOwner, pAffectedUsers, pStart, pEnd, pCategories, pStatus, pReminder, 
-                                                pExternalAttendees, pLocation, pIsAllDay, pClassification);
-    
-    var ids = calendars.insert([event], calendars.GROUP_SINGLE);
-    
-    if(pLinks)
-    {
-        var conf;
-        
-        pLinks.forEach(function(pLink){
-            conf = entities.createConfigForAddingRows().entity("AppointmentLink_entity").fieldValues({
-                "APPOINTMENT_ID" : ids[0],
-                "OBJECTID" : pLink["OBJECT_ID"],
-                "OBJECTTYPE" : pLink["OBJECT_TYPE"]
-            });
-            
-            entities.createRow(conf);
-        });
-    }
-}
-
-/*
- * Creates an new appointment object, which is responsible for holding the data<br>
- * till it's used to insert with: calendars.insert (e.g.: in Appointment_entity). 
- *
- * @param {String} pSummary (optional)              <p>
- *                                                  The summary/title of the appointment.
- * @param {String} pDescription (optional)          <p>
- *                                                  The description of the appointment.
- * @param {Object[]} pLinks (optional)              <p>
- *                                                  The links as objects <u>(key: "OBJECT_ID" & "OBJECT_TYPE")</u> in an array.
- * @param {String} pOwner (optional)                <p>
- *                                                  The calendar-user (username) which will be specified as entry-owner.
- * @param {String[]} pAffectedUsers (optional)      <p>
- *                                                  The affected users (usernames), which will be added to the appointment.
- * @param {Date} pStart (optional)                  <p>
- *                                                  Start of the appointment.
- * @param {Date} pEnd (optional)                    <p>
- *                                                  Duration of the appointment.
- * @param {String[]} pCategories (optional)         <p>
- *                                                  The categories of the appointment, the default ones are:<br>
- *                                                  <ul>
- *                                                  <li>Meeting</li>
- *                                                  <li>Organisation</li>
- *                                                  <li>OutOfOffice</li>
- *                                                  <li>Vacation</li>
- *                                                  </ul>
- * @param {String} pStatus (optional)               Status of the appointment:<br>
- *                                                  <ul>
- *                                                  <li>calendars.STATUS_TENTATIVE</li>
- *                                                  <li>calendars.STATUS_CONFIRMED</li>
- *                                                  <li>calendars.STATUS_CANCELLED</li>
- *                                                  </ul>
- * @param {Date} pReminder (optional)               <p>
- *                                                  Date of reminder.
- * @param {String[]} pExternalAttendees (optional)  <p>
- *                                                  External attendes (e-mail addresses).
- * @param {String} pLocation (optional)             <p>
- *                                                  The locations of the appointment.
- * @param {Boolean} pIsAllDay (optional)            <p>
- *                                                  Whether if it is an all-day appointment or not.
- * @param {String} pClassification (optional)       <p>
- *                                                  The classification of the appointment:
- *                                                  <ul>
- *                                                  <li>calendars.CLASSIFICATION_PUBLIC</li>
- *                                                  <li>calendars.CLASSIFICATION_PRIVATE</li>
- *                                                  </ul>
- * @return {Object}                                 
- */
-CalendarUtil.createEntry = function(pSummary, pDescription, pLinks, pOwner, pAffectedUsers, pStart, pEnd, pCategories, pStatus, 
-                                        pReminder, pExternalAttendees, pLocation, pIsAllDay, pClassification)
-{
-    var entry = {};
-    
-    entry[calendars.TYPE] = calendars.VEVENT; // hardcoded, cause only other option would be calendars.VTODO for an task
-                                              // since tasks are handled via. Task_entity there is no need for the calendars.VTODO option.
-    
-    if (!pDescription)
-    {
-        if(vars.getString("$sys.scope") == "vaadin")
-        {
-            pDescription = neon.getImageContent(vars.getString("$sys.currententityname"));
-        }
-        else
-        {
-            pDescription = swing.getImageContent(); //todo: check whether it's necessary or not. #1047482
-        }
-    }
-    
-    if (!pOwner)
-    {
-        pOwner = vars.getString("$sys.user");
-    }
-    
-    if (!pStart) 
-    {
-        entry[calendars.DTSTART] = Date.now();
-    }
-    else
-    {
-        entry[calendars.DTSTART] = pStart.getTime();
-    }
-    
-    if (!pEnd) 
-    {
-        let tempStartdate = entry[calendars.DTSTART];
-        entry[calendars.DTEND] = tempStartdate.setHours(tempStartdate.getHours()+1);
-    }
-    else
-    {
-        entry[calendars.DTEND] = pEnd.getTime().toString();
-    }
-    
-    if (!pCategories || pCategories == []) 
-    {
-        pCategories = "";
-    }
-    else 
-    {
-        for (i = 0; i < pCategories.length; i++)
-        {
-            pCategories[i] = translate.text(pCategories[i]);
-        }
-        
-        pCategories = text.encodeMS(pCategories);
-    }
-
-    if ((pAffectedUsers == null || pAffectedUsers == undefined) && (pExternalAttendees == null || pExternalAttendees == undefined))
-    {
-        entry[calendars.AFFECTEDUSERS] = "";
-    }
-    else
-    {
-        var affectedUsers = [];
-        
-        if(pAffectedUsers && pAffectedUsers != [])
-        {
-            affectedUsers = calendars.getCalendarUsers(pAffectedUsers);
-        }
-        
-        if(pExternalAttendees && pExternalAttendees != [])
-        {
-            for(let i = 0; i < pExternalAttendees.length; i++)
-            {
-                affectedUsers.push("; mailto:" + pExternalAttendees[i] + "; CN:" +  pExternalAttendees[i] + "; ")
-            }
-        }    
-        
-        entry[calendars.AFFECTEDUSERS] = text.encodeMS(affectedUsers);
-    }
-    
-    if (!pStatus)
-    {
-        pStatus = calendars.STATUS_CONFIRMED;
-    }
-    
-    if(!pReminder)
-    {
-        entry[calendars.HASREMINDER] = "true";
-        entry[calendars.REMINDER_DURATION] = pReminder.getTime().toString();
-    }
-    else
-    {
-        entry[calendars.HASREMINDER] = "false";
-    }
-    
-    if(pLinks) 
-    {
-        entry["LINKS"] = pLinks;
-    }
-    
-    entry[calendars.USER] = calendars.getCalendarUser(pOwner);
-    entry[calendars.DESCRIPTION] = pDescription;
-    entry[calendars.SUMMARY] = pSummary || "";
-    entry[calendars.STATUS] = CalendarUtil.mapCalendarStatus(pStatus, calendars.getBackendType());
-    entry[calendars.CLASSIFICATION] = pClassification || calendars.CLASSIFICATION_PUBLIC;
-    entry[calendars.CATEGORIES] = pCategories;
-    entry[calendars.TRANSPARENCY] =  "OPAQUE";
-    entry[calendars.LOCATION] = pLocation || "";
-    entry["X-ADITO-ISALLDAYEVENT"] = pIsAllDay ? "TRUE" : "FALSE";
-    entry[calendars.DTSTART] = entry[calendars.DTSTART].toString();
-
-    return entry;
-}
-
-/*
- * Add an condition.<br>
- *
- * @param {[]} pConditions              <p>
- *                                      The condition.<br>
- * @param {Integer} pIndex              <p>
- *                                      Index of the condition.<br>
- * @param {Object} pValues              <p>
- *                                      The values.<br>
- * @return {void}
- */
-CalendarUtil.addEntryCondition = function(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]];
-} 
-
-/*
- * Returns the date without the time.<br>
- *
- * @param {String} datetimeIn           <p>
- *                                      Datetime.<br>
- * @return {Date}                       <p>
- *                                      The desired date.<br>
- */
-CalendarUtil.getDate = function(datetimeIn)
-{
-    if ( datetimeIn != "")
-        return datetime.clearTime(datetimeIn);
-    else return "";
-}
-
-/*
- * Resets the event filter.<br>
- *
- * @return {Object}
- */
-CalendarUtil.reset_filterEvent = function()
-{
-    var today = CalendarUtil.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"
-    };
-}
-
-/*
- * Gibt den richtigen Status zum Prüfen je nach Backend zurück
- * Returns the matching status, to the corresponding backend.
- *
- *
- * @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
- */
-CalendarUtil.mapCalendarStatus = function(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;
-    }
-}
-
-/**
- * Returns the "real" calendar system/backend type<br>
- * (e.g.: BackendType & SyncBackendType)
- *
- * @param {Number} pScope               <p>
- *                                      The needed scope:<br>
- *                                      <ul>
- *                                      <li>calendars.VEVENT</li>
- *                                      <li>calendars.VTODO</li>
- *                                      </ul>
- * @return {Number}                     <p>
- *                                      The backend type (e.g.: calendars.BACKEND_*).<br>
- */
-CalendarUtil.getCalendarSystemType = function(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;
-}
-
-CalendarUtil.buildEntriesFromUids = function(appointmentUids) 
-{
-    var entryArray = new Array(appointmentUids.length);
-    
-    for(var i = 0; i < appointmentUids.length; i++)
-    {
-        var hasPermission = true;
-        
-        if(vars.get("$param.ErrorOnPermissionDenied") == "false" || vars.getString("$param.LinkedAppointmentsFromDashlet_param"))
-            hasPermission = hasUserPermissionForReadingEntry(getEntryOwnerCn(appointmentUids[i]));
-       
-        if(hasPermission)
-            entryArray[i] = CalendarUtil.buildEntry(calendars.getEntry(appointmentUids[i], null, null), null);
-    }
-    
-    //filter out all null
-    var filteredEntryArray = entryArray.filter(function (el) {
-        return el != null;
-    });
-    
-    return filteredEntryArray;
-}
-
-
-CalendarUtil.countEntriesFromUids = function(appointmentUids) 
-{
-    return CalendarUtil.buildEntriesFromUids(appointmentUids).length;
-}
-
-CalendarUtil.buildEntry = function (pEntry, pMasterentry)
-{
-    var uid = pEntry[calendars.ID];    
-    var summary = pEntry[calendars.SUMMARY];
-    var attendees = pEntry[calendars.AFFECTEDUSERS];
-    var startdate = pEntry[calendars.DTSTART];
-    var enddate = pEntry[calendars.DTEND];
-    var links = pEntry[calendars.LINKS];
-    var description = pEntry[calendars.DESCRIPTION];
-    if(pEntry[calendars.ORGANIZER2] != undefined)
-        var organizer = pEntry[calendars.ORGANIZER2]["paramvalue"];
-    if(pEntry[calendars.USER2] != undefined)
-        var owner = JSON.stringify(pEntry[calendars.USER2]);
-    var status = pEntry[calendars.STATUS];
-    var location = pEntry[calendars.LOCATION];
-    var reminder = pEntry[calendars.REMINDER_DURATION];
-    var remindercheck = pEntry[calendars.HASREMINDER]
-    var classification = pEntry[calendars.CLASSIFICATION];
-    var transparency = pEntry[calendars.TRANSPARENCY];
-    var categories = pEntry[calendars.CATEGORIES];
-    var isAllDay = pEntry["X-ADITO-ISALLDAYEVENT"] != null ? pEntry["X-ADITO-ISALLDAYEVENT"] : "FALSE";
-    
-    var masterBegin = pMasterentry != null ? pMasterentry[calendars.DTSTART] : null
-    var masterEnd = pMasterentry != null ? pMasterentry[calendars.DTEND] : null
-    
-    // Recurrence
-    var recurrenceID = pEntry[calendars.RECURRENCEID];
-    var rrule = null;
-    if (pMasterentry != null) { // Entry is a recurrence exception, therefore get rrule from master
-        rrule = pMasterentry[calendars.RRULE] != null ? pMasterentry[calendars.RRULE][0] : null;
-    } else {
-        rrule = pEntry[calendars.RRULE] != null ? pEntry[calendars.RRULE][0] : null;
-    }
-    
-    return [
-            uid, 
-            attendees.length, 
-            startdate, 
-            enddate, 
-            summary, 
-            organizer,
-            owner,
-            attendees, 
-            status,  
-            description, 
-            location, 
-            '', 
-            isAllDay,
-            classification,
-            transparency, 
-            categories, 
-            reminder, 
-            remindercheck, 
-            rrule, 
-            recurrenceID, 
-            null, 
-            masterBegin, 
-            masterEnd,
-            null
-        ];
-}
-
-
-function hasUserPermissionForReadingEntry(calUserCn)
-{
-    return calendars.hasPermission(calUserCn, calendars.VEVENT, "READ");
-}
-
-function getEntryOwnerCn(appointmentUid)
-{
-    
-    var owner = newSelect("ASYS_CALENDARBACKEND.OWNER", "_____SYSTEMALIAS")
-        .from("ASYS_CALENDARBACKEND")
-        .whereIfSet("ASYS_CALENDARBACKEND.ELEMENTUID", appointmentUid)
-        .cell(true);
-        
-    var ownerArr = text.decodeMS(owner);
-    return ownerArr[1].split(":")[1];
+import("system.translate");
+import("system.datetime");
+import("system.neon");
+import("system.calendars");
+import("system.vars");
+import("system.db");
+import("system.swing");
+import("system.eMath");
+import("system.logging");
+import("system.tools");
+import("system.text");
+import("system.question");
+import("system.SQLTYPES");
+import("system.result");
+import("system.util");
+import("system.entities");
+import("Util_lib");
+import("Sql_lib");
+
+
+/**
+ * Functions for the calendar.
+ * <p>
+ * <b><u>Do not create an instance of this!</u></b>
+ * @class
+ */
+function CalendarUtil(){}
+
+
+/*
+ * Creates and opens an new task object (with link).
+ *
+ * @param {String} pSummary (optional)              <p>
+ *                                                  The summary.<br>
+ * @param {String} pDescription (optional)          <p>
+ *                                                  The description.<br>
+ * @param {Boolean} pWithLink (optional)            Case if its true, then an a shortcut to $image.frametable will created.<br>
+ * @param {String[][]} pWithLink (optional)         <p>
+ *                                                  The required informations:<br>
+ *                                                  <ul>
+ *                                                  <li>pWithLink[0]: Name of the frame.</li>
+ *                                                  <li>pWithLink[1]: Id of the shown record.</li>
+ *                                                  <li>pWithLink[2]: Linking title.</li>
+ * @param {String} pUser (optional)                 <p>
+ *                                                  The user (login).<br>
+ * @param {[]} pAffectedUsers (optional)            <p>
+ *                                                  The affected users. (login)<br>
+ * @param {date} pStart (optional)                  <p>
+ *                                                  Start of the task.<br>
+ * @param {date} pDuration (optional)               <p>
+ *                                                  Duration of the task.<br>
+ * @param {integer} pCategory (optional)            <p>
+ *                                                  calendars.CATEGORIES , encoded(String) (e.g.: text.encodeMS(["Service"]))
+ * @param {String} pStatus (optional)               <p>
+ *                                                  Status of the appointment. (calendars.STATUS_TENTATIVE, <br>
+ *                                                  calendars.STATUS_CONFIRMED, calendars.STATUS_CANCELLED)<br>
+ * @param {Array{[]} pComps4Refresh (optional)      <p>
+ *                                                  The component which will be updated.<br>
+ *
+ * @return {void}
+ */
+CalendarUtil.newTodo = function(pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh)
+{
+    var todo = CalendarUtil.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 <br>
+ * attribute order like the ADITO core, which is:<br>
+ * <p>
+ * exchangeEmail -> calendarID -> email<br>
+ * <p>
+ * <b><u>DO NOT CHANGE THIS ORDER!</u></b>
+ *
+ * @param {String} pUser            <p>
+ *                                  To check.
+ * @return                          <p>
+ *                                  Effective calendar id.<br>
+ */
+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 exchangeEmail;
+    else if(calendarId)
+        return calendarId;
+    else if(email)
+        return email;
+    else
+        return "";
+}
+
+
+/*
+ * Creates and opens an new appointment object (with link).
+ *
+ * @param {String} pSummary (optional)          <p>
+ *                                              The summary.<br>
+ * @param {String} pDescription (optional)      <p>
+ *                                              The description.<br>
+ * @param {Boolean} pWithLink (optional)        <p>
+ *                                              True sets an link to $image.frametable<br>
+ * @param {String[][]} pWithLink (optional)     Description:<br>
+ *                                              <ul>
+ *                                              <li>pWithLink[0]: Name of the frame</li>
+ *                                              <li>pWithLink[1]: Id of the shown record</li>
+ *                                              <li>pWithLink[2]: linking title</li>
+ *                                              </ul>
+ * @param {String} pUser (optional)             <p>
+ *                                              The user (login).
+ * @param {[]} pAffectedUsers (optional)        <p>
+ *                                              The affected users (login).
+ * @param {Date} pStart (optional)              <p>
+ *                                              Begin of the task.<br>
+ * @param {Date} pDuration (optional)           <p>
+ *                                              Duration.<br>
+ * @param {Number} pCategory (optional)         <p>
+ *                                              calendars.CATEGORIES , encoded(String) (z.B.: text.encodeMS(["Service"])).<br>
+ * @param {String} pStatus (optional)           <p>
+ *                                              Status of the appointment:<br>
+ *                                              <ul>
+ *                                              <li>calendars.STATUS_TENTATIVE</li>
+ *                                              <li>calendars.STATUS_CONFIRMED</li>
+ *                                              <li>calendars.STATUS_CANCELLED</li>
+ *                                              </ul>
+ * @param {Array{[]} pComps4Refresh (optional)  <p>
+ *                                              The component which will be updated.<br>
+ * @param {Array{[]} pWorklistId (optional)     <p>
+ *                                              The worklist id.<br>
+ * 
+ * @return {void}
+ */
+CalendarUtil.newEvent = function( pSummary, pDescription, pWithLink, pUser, pAffectedUsers, pStart, pDuration, pCategory, pStatus, pComps4Refresh, pWorklistId)
+{
+    var event = CalendarUtil.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 (let 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);
+    }
+}
+
+
+/*
+ * Creates an new appointment entry.
+ *
+ * @param {String} pSummary (optional)              <p>
+ *                                                  The summary/title of the appointment.
+ * @param {String} pDescription (optional)          <p>
+ *                                                  The appointment description.
+ * @param {String} pLinks (optional)                <p>
+ *                                                  The links as objects <u>(key: "OBJECT_ID" & "OBJECT_TYPE")</u> in an array.
+ * @param {String} pOwner (optional)                <p>
+ *                                                  The calendar-user (username) which will be specified as entry-owner.
+ * @param {String[]} pAffectedUsers (optional)      <p>
+ *                                                  The affected users (username).
+ * @param {Date} pStart (optional)                  <p>
+ *                                                  The start of the appointment.
+ * @param {Date} pEnd (optional)                    <p>
+ *                                                  The end of the appointment.
+ * @param {String[]} pCategories (optional)         <p>
+ *                                                  The categories of the appointment, the default ones are:<br>
+ *                                                  <ul>
+ *                                                  <li>Meeting</li>
+ *                                                  <li>Organisation</li>
+ *                                                  <li>OutOfOffice</li>
+ *                                                  <li>Vacation</li>
+ *                                                  </ul>
+ * @param {String} pStatus (optional)               <p>
+ *                                                  Status of the appointment:<br>
+ *                                                  <ul>
+ *                                                  <li>calendars.STATUS_TENTATIVE</li>
+ *                                                  <li>calendars.STATUS_CONFIRMED</li>
+ *                                                  <li>calendars.STATUS_CANCELLED</li>
+ *                                                  </ul>
+ * @param {Date} pReminder (optional)               <p>
+ *                                                  Date of the reminder for the appointment.
+ * @param {String[]} pExternalAttendees (optional)  <p>
+ *                                                  External attendees.
+ * @param {String} pLocation (optional)             <p>
+ *                                                  The location of the appointment.
+ * @param {Boolean} pIsAllDay (optional)            <p>
+ *                                                  Whether if it is an all-day appointment or not.
+ * @param {String} pClassification (optional)       <p>
+ *                                                  The classification of the appointment:<br>
+ *                                                  <ul>
+ *                                                  <li>calendars.CLASSIFICATION_PUBLIC</li>
+ *                                                  <li>calendars.CLASSIFICATION_PRIVATE</li>
+ *                                                  </ul>
+ * @return {void}
+ */
+CalendarUtil.newSilentEvent = function(pSummary, pDescription, pLinks, pOwner, pAffectedUsers, pStart, pEnd, pCategories, pStatus, 
+                                            pReminder, pExternalAttendees, pLocation, pIsAllDay, pClassification)
+{   
+    var event = CalendarUtil.createEntry(pSummary, pDescription, pLinks, pOwner, pAffectedUsers, pStart, pEnd, pCategories, pStatus, pReminder, 
+                                                pExternalAttendees, pLocation, pIsAllDay, pClassification);
+    
+    var ids = calendars.insert([event], calendars.GROUP_SINGLE);
+    
+    if(pLinks)
+    {
+        var conf;
+        
+        pLinks.forEach(function(pLink){
+            conf = entities.createConfigForAddingRows().entity("AppointmentLink_entity").fieldValues({
+                "APPOINTMENT_ID" : ids[0],
+                "OBJECTID" : pLink["OBJECT_ID"],
+                "OBJECTTYPE" : pLink["OBJECT_TYPE"]
+            });
+            
+            entities.createRow(conf);
+        });
+    }
+}
+
+/*
+ * Creates an new appointment object, which is responsible for holding the data<br>
+ * till it's used to insert with: calendars.insert (e.g.: in Appointment_entity). 
+ *
+ * @param {String} pSummary (optional)              <p>
+ *                                                  The summary/title of the appointment.
+ * @param {String} pDescription (optional)          <p>
+ *                                                  The description of the appointment.
+ * @param {Object[]} pLinks (optional)              <p>
+ *                                                  The links as objects <u>(key: "OBJECT_ID" & "OBJECT_TYPE")</u> in an array.
+ * @param {String} pOwner (optional)                <p>
+ *                                                  The calendar-user (username) which will be specified as entry-owner.
+ * @param {String[]} pAffectedUsers (optional)      <p>
+ *                                                  The affected users (usernames), which will be added to the appointment.
+ * @param {Date} pStart (optional)                  <p>
+ *                                                  Start of the appointment.
+ * @param {Date} pEnd (optional)                    <p>
+ *                                                  Duration of the appointment.
+ * @param {String[]} pCategories (optional)         <p>
+ *                                                  The categories of the appointment, the default ones are:<br>
+ *                                                  <ul>
+ *                                                  <li>Meeting</li>
+ *                                                  <li>Organisation</li>
+ *                                                  <li>OutOfOffice</li>
+ *                                                  <li>Vacation</li>
+ *                                                  </ul>
+ * @param {String} pStatus (optional)               Status of the appointment:<br>
+ *                                                  <ul>
+ *                                                  <li>calendars.STATUS_TENTATIVE</li>
+ *                                                  <li>calendars.STATUS_CONFIRMED</li>
+ *                                                  <li>calendars.STATUS_CANCELLED</li>
+ *                                                  </ul>
+ * @param {Date} pReminder (optional)               <p>
+ *                                                  Date of reminder.
+ * @param {String[]} pExternalAttendees (optional)  <p>
+ *                                                  External attendes (e-mail addresses).
+ * @param {String} pLocation (optional)             <p>
+ *                                                  The locations of the appointment.
+ * @param {Boolean} pIsAllDay (optional)            <p>
+ *                                                  Whether if it is an all-day appointment or not.
+ * @param {String} pClassification (optional)       <p>
+ *                                                  The classification of the appointment:
+ *                                                  <ul>
+ *                                                  <li>calendars.CLASSIFICATION_PUBLIC</li>
+ *                                                  <li>calendars.CLASSIFICATION_PRIVATE</li>
+ *                                                  </ul>
+ * @return {Object}                                 
+ */
+CalendarUtil.createEntry = function(pSummary, pDescription, pLinks, pOwner, pAffectedUsers, pStart, pEnd, pCategories, pStatus, 
+                                        pReminder, pExternalAttendees, pLocation, pIsAllDay, pClassification)
+{
+    var entry = {};
+    
+    entry[calendars.TYPE] = calendars.VEVENT; // hardcoded, cause only other option would be calendars.VTODO for an task
+                                              // since tasks are handled via. Task_entity there is no need for the calendars.VTODO option.
+    
+    if (!pDescription)
+    {
+        if(vars.getString("$sys.scope") == "vaadin")
+        {
+            pDescription = neon.getImageContent(vars.getString("$sys.currententityname"));
+        }
+        else
+        {
+            pDescription = swing.getImageContent(); //todo: check whether it's necessary or not. #1047482
+        }
+    }
+    
+    if (!pOwner)
+    {
+        pOwner = vars.getString("$sys.user");
+    }
+    
+    if (!pStart) 
+    {
+        entry[calendars.DTSTART] = Date.now();
+    }
+    else
+    {
+        entry[calendars.DTSTART] = pStart.getTime();
+    }
+    
+    if (!pEnd) 
+    {
+        let tempStartdate = entry[calendars.DTSTART];
+        entry[calendars.DTEND] = tempStartdate + datetime.ONE_HOUR;
+    }
+    else
+    {
+        entry[calendars.DTEND] = pEnd.getTime().toString();
+    }
+    
+    if (!pCategories || pCategories == []) 
+    {
+        pCategories = "";
+    }
+    else 
+    {
+        for (i = 0; i < pCategories.length; i++)
+        {
+            pCategories[i] = translate.text(pCategories[i]);
+        }
+        
+        pCategories = text.encodeMS(pCategories);
+    }
+
+    if ((pAffectedUsers == null || pAffectedUsers == undefined) && (pExternalAttendees == null || pExternalAttendees == undefined))
+    {
+        entry[calendars.AFFECTEDUSERS] = "";
+    }
+    else
+    {
+        var affectedUsers = [];
+        affectedUsers.push(calendars.getCalendarUser(vars.get("$sys.user")));
+        
+        if(pAffectedUsers && pAffectedUsers != [])
+        {
+            affectedUsers = calendars.getCalendarUsers(pAffectedUsers);
+        }
+        
+        if(pExternalAttendees && pExternalAttendees != [])
+        {
+            for(let i = 0; i < pExternalAttendees.length; i++)
+            {
+                affectedUsers.push("; mailto:" + pExternalAttendees[i] + "; CN:" +  pExternalAttendees[i] + "; ")
+            }
+        }    
+        
+        entry[calendars.AFFECTEDUSERS] = text.encodeMS(affectedUsers);
+    }
+    
+    if (!pStatus)
+    {
+        pStatus = calendars.STATUS_CONFIRMED;
+    }
+    
+    if(pReminder)
+    {
+        entry[calendars.HASREMINDER] = "true";
+        entry[calendars.REMINDER_DURATION] = pReminder.getTime().toString();
+    }
+    else
+    {
+        entry[calendars.HASREMINDER] = "false";
+    }
+    
+    if(pLinks) 
+    {
+        entry["LINKS"] = pLinks;
+    }
+    
+    entry[calendars.USER] = calendars.getCalendarUser(pOwner);
+    entry[calendars.DESCRIPTION] = pDescription;
+    entry[calendars.SUMMARY] = pSummary || "";
+    entry[calendars.STATUS] = CalendarUtil.mapCalendarStatus(pStatus, calendars.getBackendType());
+    entry[calendars.CLASSIFICATION] = pClassification || calendars.CLASSIFICATION_PUBLIC;
+    entry[calendars.CATEGORIES] = pCategories;
+    entry[calendars.TRANSPARENCY] =  "OPAQUE";
+    entry[calendars.LOCATION] = pLocation || "";
+    entry["X-ADITO-ISALLDAYEVENT"] = pIsAllDay ? "TRUE" : "FALSE";
+    entry[calendars.DTSTART] = entry[calendars.DTSTART].toString();
+
+    return entry;
+}
+
+/*
+ * Add an condition.<br>
+ *
+ * @param {[]} pConditions              <p>
+ *                                      The condition.<br>
+ * @param {Integer} pIndex              <p>
+ *                                      Index of the condition.<br>
+ * @param {Object} pValues              <p>
+ *                                      The values.<br>
+ * @return {void}
+ */
+CalendarUtil.addEntryCondition = function(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]];
+} 
+
+/*
+ * Returns the date without the time.<br>
+ *
+ * @param {String} datetimeIn           <p>
+ *                                      Datetime.<br>
+ * @return {Date}                       <p>
+ *                                      The desired date.<br>
+ */
+CalendarUtil.getDate = function(datetimeIn)
+{
+    if ( datetimeIn != "")
+        return datetime.clearTime(datetimeIn);
+    else return "";
+}
+
+/*
+ * Resets the event filter.<br>
+ *
+ * @return {Object}
+ */
+CalendarUtil.reset_filterEvent = function()
+{
+    var today = CalendarUtil.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"
+    };
+}
+
+/*
+ * Gibt den richtigen Status zum Prüfen je nach Backend zurück
+ * Returns the matching status, to the corresponding backend.
+ *
+ *
+ * @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
+ */
+CalendarUtil.mapCalendarStatus = function(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;
+    }
+}
+
+/**
+ * Returns the "real" calendar system/backend type<br>
+ * (e.g.: BackendType & SyncBackendType)
+ *
+ * @param {Number} pScope               <p>
+ *                                      The needed scope:<br>
+ *                                      <ul>
+ *                                      <li>calendars.VEVENT</li>
+ *                                      <li>calendars.VTODO</li>
+ *                                      </ul>
+ * @return {Number}                     <p>
+ *                                      The backend type (e.g.: calendars.BACKEND_*).<br>
+ */
+CalendarUtil.getCalendarSystemType = function(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;
+}
+
+CalendarUtil.buildEntriesFromUids = function(appointmentUids) 
+{
+    var entryArray = new Array(appointmentUids.length);
+    
+    for(var i = 0; i < appointmentUids.length; i++)
+    {
+        var hasPermission = true;
+        
+        if(vars.get("$param.ErrorOnPermissionDenied") == "false" || vars.getString("$param.LinkedAppointmentsFromDashlet_param"))
+            hasPermission = hasUserPermissionForReadingEntry(getEntryOwnerCn(appointmentUids[i]));
+       
+        if(hasPermission)
+            entryArray[i] = CalendarUtil.buildEntry(calendars.getEntry(appointmentUids[i], null, null), null);
+    }
+    
+    //filter out all null
+    var filteredEntryArray = entryArray.filter(function (el) {
+        return el != null;
+    });
+    
+    return filteredEntryArray;
+}
+
+
+CalendarUtil.countEntriesFromUids = function(appointmentUids) 
+{
+    return CalendarUtil.buildEntriesFromUids(appointmentUids).length;
+}
+
+CalendarUtil.buildEntry = function (pEntry, pMasterentry)
+{
+    var uid = pEntry[calendars.ID];    
+    var summary = pEntry[calendars.SUMMARY];
+    var attendees = pEntry[calendars.AFFECTEDUSERS];
+    var startdate = pEntry[calendars.DTSTART];
+    var enddate = pEntry[calendars.DTEND];
+    var links = pEntry[calendars.LINKS];
+    var description = pEntry[calendars.DESCRIPTION];
+    if(pEntry[calendars.ORGANIZER2] != undefined)
+        var organizer = pEntry[calendars.ORGANIZER2]["paramvalue"];
+    if(pEntry[calendars.USER2] != undefined)
+        var owner = JSON.stringify(pEntry[calendars.USER2]);
+    var status = pEntry[calendars.STATUS];
+    var location = pEntry[calendars.LOCATION];
+    var reminder = pEntry[calendars.REMINDER_DURATION];
+    var remindercheck = pEntry[calendars.HASREMINDER]
+    var classification = pEntry[calendars.CLASSIFICATION];
+    var transparency = pEntry[calendars.TRANSPARENCY];
+    var categories = pEntry[calendars.CATEGORIES];
+    var isAllDay = pEntry["X-ADITO-ISALLDAYEVENT"] != null ? pEntry["X-ADITO-ISALLDAYEVENT"] : "FALSE";
+    
+    var masterBegin = pMasterentry != null ? pMasterentry[calendars.DTSTART] : null
+    var masterEnd = pMasterentry != null ? pMasterentry[calendars.DTEND] : null
+    
+    // Recurrence
+    var recurrenceID = pEntry[calendars.RECURRENCEID];
+    var rrule = null;
+    if (pMasterentry != null) { // Entry is a recurrence exception, therefore get rrule from master
+        rrule = pMasterentry[calendars.RRULE] != null ? pMasterentry[calendars.RRULE][0] : null;
+    } else {
+        rrule = pEntry[calendars.RRULE] != null ? pEntry[calendars.RRULE][0] : null;
+    }
+    
+    return [
+            uid, 
+            attendees.length, 
+            startdate, 
+            enddate, 
+            summary, 
+            organizer,
+            owner,
+            attendees, 
+            status,  
+            description, 
+            location, 
+            '', 
+            isAllDay,
+            classification,
+            transparency, 
+            categories, 
+            reminder, 
+            remindercheck, 
+            rrule, 
+            recurrenceID, 
+            null, 
+            masterBegin, 
+            masterEnd,
+            null
+        ];
+}
+
+
+function hasUserPermissionForReadingEntry(calUserCn)
+{
+    return calendars.hasPermission(calUserCn, calendars.VEVENT, "READ");
+}
+
+function getEntryOwnerCn(appointmentUid)
+{
+    
+    var owner = newSelect("ASYS_CALENDARBACKEND.OWNER", "_____SYSTEMALIAS")
+        .from("ASYS_CALENDARBACKEND")
+        .whereIfSet("ASYS_CALENDARBACKEND.ELEMENTUID", appointmentUid)
+        .cell(true);
+        
+    var ownerArr = text.decodeMS(owner);
+    return ownerArr[1].split(":")[1];
 }
\ No newline at end of file
diff --git a/process/Email_lib/process.js b/process/Email_lib/process.js
index 8f560c533ad7994e8b1896715b41ee6aabb311b6..61787a5d831ae07d315df9530951b04849e2b560 100644
--- a/process/Email_lib/process.js
+++ b/process/Email_lib/process.js
@@ -34,7 +34,8 @@ function EmailWritingUtils () {}
  * @param {Placeholder[]} [pAdditionalPlaceholders] additional placeholders
  * @return {Array} the eml document as array with [filename, base64]
  */
-EmailWritingUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId, pBindata, pAttachments, pSubject, pEmailFilename, pAdditionalPlaceholders)
+EmailWritingUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId, pBindata, pAttachments, pSubject,
+                                                    pEmailFilename, pAdditionalPlaceholders)
 {
     if (pToRecipients && typeof(pToRecipients) == "string")
         pToRecipients = [pToRecipients];
@@ -66,9 +67,13 @@ EmailWritingUtils.openMailTemplate = function (pToRecipients, pSenderContactId,
     }
     
     if (pSubject)
+    {
         email.subject = pSubject;
+    }
+    
+    var isBinaryExistent = db.getBinaryCount("DOCUMENTTEMPLATE", "DOCUMENT", pTemplateId, SqlUtils.getBinariesAlias(), null);
     
-    return email.downloadEML(pEmailFilename);
+    return isBinaryExistent > 0 ? email.downloadEML(pEmailFilename) : [];
 }
 
 
diff --git a/process/UnitTest_lib/process.js b/process/UnitTest_lib/process.js
index 382d37e021567c924734a1350a7a368acd1796d3..210ab48b8f435e633a665feed0a9dd766630d990 100644
--- a/process/UnitTest_lib/process.js
+++ b/process/UnitTest_lib/process.js
@@ -261,7 +261,7 @@ Tester.prototype.equals = function(pExpect, pCustomDescription)
 
     if(Utils.isObject(this.actualValue) || Utils.isObject(this.expectedValue))
     {
-        this.expectedDisplayValue = JSON.stringify(this.actualValue);
+        this.expectedDisplayValue = JSON.stringify(this.actualValue, _getCircularReplacer());
         this._testResult = Utils.isEqual(this.actualValue, this.expectedValue);
         this._generateAssertDescription({custom: pCustomDescription, operator: "===", name: "Object value"});
     }
@@ -272,6 +272,24 @@ Tester.prototype.equals = function(pExpect, pCustomDescription)
     }
 
     return this;
+    
+    //custom replacer that supports cyclic references
+    function _getCircularReplacer () 
+    {
+        var seen = new WeakSet();
+        return function (key, value) 
+        {
+            if (typeof value === "object" && value !== null) 
+            {
+                if (seen.has(value)) 
+                {
+                    return "{...}";
+                }
+                seen.add(value);
+            }
+            return value;
+        }
+    }
 }
 
 /**
@@ -1202,7 +1220,7 @@ Tester.prototype._generateTestTitle = function(pTest, pDataProviderIndex)
         var titleValues = [];
 
         this.dataProvider[pDataProviderIndex].forEach(function(pElement) {
-            titleValues.push(pElement === undefined ? "undefined" : JSON.stringify(pElement));
+            titleValues.push(pElement === undefined ? "undefined" : JSON.stringify(pElement, _getCircularReplacer()));
         });
         this._log("", this.t.info("\t\u2699 Test: " + pTest.name) + this.t.debug(" (#" + (pDataProviderIndex + 1) + ": " + titleValues.join(" | ") + ")"));
     }
@@ -1210,6 +1228,24 @@ Tester.prototype._generateTestTitle = function(pTest, pDataProviderIndex)
     {
         this._log("info", "\t\u2699 Test: " + pTest.name);
     }
+    
+    //custom replacer that supports cyclic references
+    function _getCircularReplacer () 
+    {
+        var seen = new WeakSet();
+        return function (key, value) 
+        {
+            if (typeof value === "object" && value !== null) 
+            {
+                if (seen.has(value)) 
+                {
+                    return "{...}";
+                }
+                seen.add(value);
+            }
+            return value;
+        }
+    }
 }
 
 /**
diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js
index 8ea572643943a2724f7a323e207122ff530957de..4ef286fe09d3ed55ed6394c8b950ed650c71c67c 100644
--- a/process/Util_lib/process.js
+++ b/process/Util_lib/process.js
@@ -100,7 +100,7 @@ Utils.isNotNullOrEmptyString = function (pValue)
  */
 Utils.clone = function (pObject)
 {
-    var referenceMap = new Map();
+    var referenceMap = new WeakMap();
     return _clone(pObject);
     
     function _clone (pObject)
@@ -173,35 +173,62 @@ Utils.clone = function (pObject)
  */
 Utils.isEqual = function (pFirstObject, pSecondObject)
 {
-    var firstType = typeof pFirstObject;
-    var secondType = typeof pSecondObject;
-    if (firstType !== secondType)
-        return false;
-    if (firstType === "object" && pFirstObject !== null && pSecondObject !== null) //check for null because typeof null is also "object"
+    var comparedObjects = new WeakMap();
+    return _isEqual(pFirstObject, pSecondObject);
+    
+    function _isEqual (pFirstObject, pSecondObject)
     {
-        var isFirstArray = Array.isArray(pFirstObject);
-        var isSecondArray = Array.isArray(pSecondObject);
-        if (isFirstArray !== isSecondArray) //return false if only one object is an array
-            return false;
-        if (isFirstArray && pFirstObject.length !== pSecondObject.length)
+        var firstType = typeof pFirstObject;
+        var secondType = typeof pSecondObject;
+        if (firstType !== secondType)
+        {
             return false;
-        
-        for (let key in pSecondObject)
+        }
+        if (firstType === "object" && pFirstObject !== null && pSecondObject !== null) //check for null because typeof null is also "object"
         {
-            if (!(key in pFirstObject))
+            //All object comparisons are saved in comparedObjects, this makes it possible to detect recursions.
+            //If two objects have been checked for equality already, it can be safely assumed they are equal,
+            //because if they weren't, this function would have returned false already
+            if (comparedObjects.get(pFirstObject) === pSecondObject)
+            {
+                return true;
+            }
+            comparedObjects.set(pFirstObject, pSecondObject);
+            
+            var isFirstArray = Array.isArray(pFirstObject);
+            var isSecondArray = Array.isArray(pSecondObject);
+            if (isFirstArray !== isSecondArray) //return false if only one object is an array
+            {
+                return false;
+            }
+            if (isFirstArray && pFirstObject.length !== pSecondObject.length)
+            {
                 return false;
+            }
+            
+            for (let key in pSecondObject)
+            {
+                if (!(key in pFirstObject))
+                {
+                    return false;
+                }
+            }
+            for (let key in pFirstObject)
+            {
+                if (!(key in pSecondObject) || !_isEqual(pFirstObject[key], pSecondObject[key]))
+                {
+                    return false;
+                }
+            }
+            return true;
         }
-        for (let key in pFirstObject)
+        if (firstType === "number" && Number.isNaN(pFirstObject) && Number.isNaN(pSecondObject)) //NaN should be equal to NaN
         {
-            if (!(key in pSecondObject) || !Utils.isEqual(pFirstObject[key], pSecondObject[key]))
-                return false;
+            return true;
         }
-        return true;
+
+        return pFirstObject === pSecondObject;
     }
-    if (firstType === "number" && Number.isNaN(pFirstObject) && Number.isNaN(pSecondObject)) //NaN should be equal to NaN
-        return true;
-    
-    return pFirstObject === pSecondObject;
 }
 
 /**
diff --git a/process/Utils_test/Utils_test.aod b/process/Utils_test/Utils_test.aod
new file mode 100644
index 0000000000000000000000000000000000000000..f877069734b9b8465243129b62e612e9f887887a
--- /dev/null
+++ b/process/Utils_test/Utils_test.aod
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
+  <name>Utils_test</name>
+  <title>[TEST] Util_lib</title>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <icon>VAADIN:CHECK_CIRCLE</icon>
+  <process>%aditoprj%/process/Utils_test/process.js</process>
+  <variants>
+    <element>EXECUTABLE</element>
+  </variants>
+</process>
diff --git a/process/Utils_test/process.js b/process/Utils_test/process.js
new file mode 100644
index 0000000000000000000000000000000000000000..3a094ad4dcb4e17784be072b5c6089ac636b2d8a
--- /dev/null
+++ b/process/Utils_test/process.js
@@ -0,0 +1,542 @@
+import("Util_lib");
+import("system.result");
+import("system.vars");
+import("UnitTest_lib");
+
+var isEmpty = new TestSuite("Utils.isEmpty", [
+    new Test("should test if an object, string or array is empty",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isEmpty(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [true, ""],
+                [true, []],
+                [true, {}],
+                [true, new Set()],
+                [true, new Map()],
+                [true, function () {}],
+                [false, "never gonna"],
+                [false, ["give"]],
+                [false, {you: "up"}],
+                [false, new Set(["never gonna"])],
+                [false, new Map([["let", "you down"]])]
+            ];
+        }
+    )
+]);
+
+var isNullOrEmpty = new TestSuite("Utils.isNullOrEmpty", [
+    new Test("should test if value is null, undefined or empty",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isNullOrEmpty(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [true, null],
+                [true, undefined],
+                [true, ""],
+                [true, []],
+                [true, {}],
+                [true, new Set()],
+                [true, new Map()],
+                [true, function () {}],
+                [false, "never gonna"],
+                [false, ["give"]],
+                [false, {you: "up"}],
+                [false, new Set(["never gonna"])],
+                [false, new Map([["let", "you down"]])]
+            ];
+        }
+    )
+]);
+
+var isNullOrEmptyString = new TestSuite("Utils.isNullOrEmptyString", [
+    new Test("should test if value is null, undefined or empty string",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isNullOrEmptyString(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [true, null],
+                [true, undefined],
+                [true, ""],
+                [false, 0],
+                [false, 42],
+                [false, false],
+                [false, true],
+                [false, []],
+                [false, {}],
+                [false, new Set()],
+                [false, new Map()],
+                [false, function () {}],
+                [false, "never gonna"],
+                [false, ["give"]],
+                [false, {you: "up"}],
+                [false, new Set(["never gonna"])],
+                [false, new Map([["let", "you down"]])]
+            ];
+        }
+    )
+]);
+
+var isNotNullOrEmptyString = new TestSuite("Utils.isNotNullOrEmptyString", [
+    new Test("should test if value isn't null, undefined or empty string",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isNotNullOrEmptyString(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, ""],
+                [true, 0],
+                [true, 42],
+                [true, false],
+                [true, true],
+                [true, []],
+                [true, {}],
+                [true, new Set()],
+                [true, new Map()],
+                [true, function () {}],
+                [true, "never gonna"],
+                [true, ["give"]],
+                [true, {you: "up"}],
+                [true, new Set(["never gonna"])],
+                [true, new Map([["let", "you down"]])]
+            ];
+        }
+    )
+]);
+
+var clone = new TestSuite("Utils.clone", [
+    new Test("should test if value is copied correctly",
+        function (pTester, pDataProvider) 
+        {
+            var original = pDataProvider[0];
+            var copy = Utils.clone(original);
+
+            pTester.expectThat(original).equals(copy).assert();
+        },
+        function dataProvider() 
+        {
+            var recursiveObj = {test: "Test"};
+            recursiveObj.recursion = recursiveObj;
+            return [
+                [null],
+                [undefined],
+                [0],
+                [42],
+                [""],
+                ["yeee"],
+                [[]],
+                [{}],
+                [["one", "two", 3, 4]],
+                [{one: "two", three: 4}],
+                [["one", "two", [3, 4, {five: "6"}]]],
+                [{one: "two", three: [4, {five: "6"}]}],
+                [new Set(["one", "two", 3, 4])],
+                [new Map([["one", "two"], [3, 4]])],
+                [recursiveObj]
+            ];
+        }
+    )
+]);
+
+var isEqual = new TestSuite("Utils.isEqual", [
+    new Test("should test if object equality is checked correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isEqual(pDataProvider[1], pDataProvider[2]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            var num = 0;
+            var str = "0";
+            var objStr = new String("0");
+            var obj_1 = {a: 1, b: 2, c: 3};
+            var obj_2 = {x: null, y: "test", z: true};
+            var recursiveObj1 = {test: "Test"};
+            recursiveObj1.recursion = recursiveObj1;
+            var recursiveObj2 = {test: "Test"};
+            recursiveObj2.recursion = recursiveObj2;
+            
+            return [
+                [true,  num,     num],
+                [true,  str,     str],
+                [true,  true,    true],
+                [true,  objStr,  objStr],
+                [true,  obj_1,   obj_1],
+                [true,  obj_2,   obj_2],
+                [false, true,    false],
+                [false, obj_1,   obj_2],
+                [false, num,     objStr],
+                [false, num,     str],
+                [false, objStr,  str],
+                [false, null,    undefined],
+                [false, objStr,  null],
+                [false, objStr,  undefined],
+                [false, null,    "null"],
+                [true, recursiveObj1, recursiveObj2]
+            ];
+        }
+    )
+]);
+
+var isFunction = new TestSuite("Utils.isFunction", [
+    new Test("should test if functions are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isFunction(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [true, function () {}],
+                [true, (function () {}).bind({})],
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [false, 0],
+                [false, "str"],
+                [false, {}],
+                [false, []],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isString = new TestSuite("Utils.isString", [
+    new Test("should test if strings are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isString(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [false, 0],
+                [true, ""],
+                [true, "str"],
+                [false, new String("")],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isNumber = new TestSuite("Utils.isNumber", [
+    new Test("should test if numbers are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isNumber(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [true, 0],
+                [true, NaN],
+                [true, Infinity],
+                [false, "0"],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isNumeric = new TestSuite("Utils.isNumeric", [
+    new Test("should test if numbers are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isNumeric(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [true, 0],
+                [true, "0"],
+                [false, "notnumber"],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isInteger = new TestSuite("Utils.isInteger", [
+    new Test("should test if integers are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isInteger(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [true, 0],
+                [true, "0"],
+                [true, 1.0],
+                [true, "1.0"],
+                [false, 1.2],
+                [false, "1.2"],
+                [false, NaN],
+                [false, Infinity],
+                [false, "notnumber"],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isFloat = new TestSuite("Utils.isFloat", [
+    new Test("should test if floats are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isFloat(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [false, 0],
+                [false, "0"],
+                [false, 1.0],
+                [false, "1.0"],
+                [true, 1.2],
+                [true, "1.2"],
+                [false, NaN],
+                [false, Infinity],
+                [false, "notnumber"],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isObject = new TestSuite("Utils.isObject", [
+    new Test("should test if objects are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isObject(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [false, 0],
+                [false, "0"],
+                [false, NaN],
+                [false, Infinity],
+                [true, {}],
+                [true, []],
+                [false, function () {}],
+                [true, new Map()],
+                [true, new Set()]
+            ];
+        }
+    )
+]);
+
+var isMap = new TestSuite("Utils.isMap", [
+    new Test("should test if maps are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isMap(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [false, true],
+                [false, false],
+                [false, 0],
+                [false, "0"],
+                [false, NaN],
+                [false, Infinity],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [true, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var isBoolean = new TestSuite("Utils.isBoolean", [
+    new Test("should test if booleans are identified correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.isBoolean(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [false, null],
+                [false, undefined],
+                [true, true],
+                [true, false],
+                [false, 0],
+                [false, "0"],
+                [false, NaN],
+                [false, Infinity],
+                [false, {}],
+                [false, []],
+                [false, function () {}],
+                [false, new Map()],
+                [false, new Set()]
+            ];
+        }
+    )
+]);
+
+var toBoolean = new TestSuite("Utils.toBoolean", [
+    new Test("should test if boolean-ish values are parsed correctly",
+        function (pTester, pDataProvider) 
+        {
+            var expectValue = pDataProvider[0];
+            var actualValue = Utils.toBoolean(pDataProvider[1]);
+
+            pTester.expectThat(actualValue).equals(expectValue).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [true, true],
+                [true, 1],
+                [true, "1"],
+                [true, "true"],
+                [true, {}],
+                [true, []],
+                [true, function () {}],
+                [true, new Map()],
+                [true, new Set()],
+                [false, false],
+                [false, 0],
+                [false, "0"],
+                [false, ""],
+                [false, "false"],
+                [false, null],
+                [false, "null"],
+                [false, undefined],
+                [false, "undefined"]
+            ];
+        }
+    )
+]);
+
+var tester = new Tester("Test Util_lib");
+tester.initCoverage(Utils);
+tester.test(isEmpty);
+tester.test(isNullOrEmpty);
+tester.test(isNullOrEmptyString);
+tester.test(clone);
+tester.test(isEqual);
+tester.test(isFunction);
+tester.test(isString);
+tester.test(isNumber);
+tester.test(isNumeric);
+tester.test(isInteger);
+tester.test(isFloat);
+tester.test(isObject);
+tester.test(isMap);
+tester.test(isBoolean);
+tester.test(toBoolean);
+
+tester.summary();
+    
+result.object(tester.getResults());