From d82b788cda566b236d68d01f45778c9e5ef71c00 Mon Sep 17 00:00:00 2001
From: "S.Listl" <S.Listl@SLISTL.aditosoftware.local>
Date: Mon, 22 Jul 2019 11:28:38 +0200
Subject: [PATCH] Attribute usage fix

---
 .../recordcontainers/db/onDBDelete.js         |   4 +-
 .../recordcontainers/db/onDBInsert.js         |   5 +-
 .../recordcontainers/db/onDBUpdate.js         |   5 +-
 process/Attribute_lib/process.js              |  22 +-
 process/Bulkmail_lib/process.js               | 498 +++++++++---------
 process/Sql_lib/process.js                    |  20 +-
 6 files changed, 281 insertions(+), 273 deletions(-)

diff --git a/entity/AttributeUsage_entity/recordcontainers/db/onDBDelete.js b/entity/AttributeUsage_entity/recordcontainers/db/onDBDelete.js
index b41e99cd82..c3a22ddade 100644
--- a/entity/AttributeUsage_entity/recordcontainers/db/onDBDelete.js
+++ b/entity/AttributeUsage_entity/recordcontainers/db/onDBDelete.js
@@ -1,7 +1,7 @@
 import("system.vars");
 import("Attribute_lib");
 
-var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
-var objectType = vars.get("$field.OBJECT_TYPE");
+var data = vars.get("$local.rowdata");
+var objectType = data["OBJECT_TYPE.value"];
 
 AttributeUsageUtil.deleteChildrenUsages(attributeId, objectType);
\ No newline at end of file
diff --git a/entity/AttributeUsage_entity/recordcontainers/db/onDBInsert.js b/entity/AttributeUsage_entity/recordcontainers/db/onDBInsert.js
index 73c28f2916..cb887e0535 100644
--- a/entity/AttributeUsage_entity/recordcontainers/db/onDBInsert.js
+++ b/entity/AttributeUsage_entity/recordcontainers/db/onDBInsert.js
@@ -1,8 +1,9 @@
 import("system.vars");
 import("Attribute_lib");
 
-var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
-var objectType = vars.get("$field.OBJECT_TYPE");
+var data = vars.get("$local.rowdata");
+var attributeId = data["AB_ATTRIBUTE_ID.value"];
+var objectType = data["OBJECT_TYPE.value"];
 
 AttributeUsageUtil.insertChildrenUsages(attributeId, objectType);
 AttributeUsageUtil.removeDuplicates(attributeId);
\ No newline at end of file
diff --git a/entity/AttributeUsage_entity/recordcontainers/db/onDBUpdate.js b/entity/AttributeUsage_entity/recordcontainers/db/onDBUpdate.js
index 02890f1b92..c32017dd3c 100644
--- a/entity/AttributeUsage_entity/recordcontainers/db/onDBUpdate.js
+++ b/entity/AttributeUsage_entity/recordcontainers/db/onDBUpdate.js
@@ -1,9 +1,10 @@
 import("system.vars");
 import("Attribute_lib");
 
-var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
+var data = vars.get("$local.rowdata");
+var attributeId = data["AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID"];
 var oldObjectType = vars.get("$context.originalObjectType");
-var newObjectType = vars.get("$field.OBJECT_TYPE");
+var newObjectType = data["AB_ATTRIBUTEUSAGE.OBJECT_TYPE"];
 
 AttributeUsageUtil.updateChildrenUsages(attributeId, oldObjectType, newObjectType);
 AttributeUsageUtil.removeDuplicates(attributeId);
\ No newline at end of file
diff --git a/process/Attribute_lib/process.js b/process/Attribute_lib/process.js
index 4ccea89794..db9b5170bd 100644
--- a/process/Attribute_lib/process.js
+++ b/process/Attribute_lib/process.js
@@ -1,3 +1,4 @@
+import("system.logging");
 import("Context_lib");
 import("system.util");
 import("system.datetime");
@@ -822,10 +823,17 @@ AttributeUsageUtil.updateChildrenUsages = function (pAttributeId, pOldObjectType
     
     var table = "AB_ATTRIBUTEUSAGE";
     
-    var sqlSelect = "select AB_ATTRIBUTEID, AB_ATTRIBUTEUSAGEID, "
-        + " (select count(*) from AB_ATTRIBUTEUSAGE where AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID and OBJECT_TYPE = '" 
-        + pNewObjectType + "') = 0"
-        + " from AB_ATTRIBUTE left join AB_ATTRIBUTEUSAGE on AB_ATTRIBUTEID = AB_ATTRIBUTE_ID and OBJECT_TYPE = '" + pOldObjectType + "'";
+    var countSubQuery = SqlCondition.begin()
+        .andPrepare("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pNewObjectType)
+        .and("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+        .buildSql("select count(*) from AB_ATTRIBUTEUSAGE");
+    
+    var sqlSelect = SqlBuilder.begin()
+        .select(["AB_ATTRIBUTEID", "AB_ATTRIBUTEUSAGEID", countSubQuery])
+        .from("AB_ATTRIBUTE")
+        .leftJoin("AB_ATTRIBUTEUSAGE", SqlCondition.begin()
+            .andPrepare("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pOldObjectType)
+            .and("AB_ATTRIBUTEID = AB_ATTRIBUTE_ID"));
     
     var updateCond = SqlCondition.begin();
     
@@ -845,11 +853,13 @@ AttributeUsageUtil.updateChildrenUsages = function (pAttributeId, pOldObjectType
         var condition = SqlCondition.begin()
             .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
             .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeId);
-        var attributes = db.table(condition.buildSql(sqlSelect));
+        var query = sqlSelect.where(condition);
+        var attributes = db.table(query.build());
+        logging.log(JSON.stringify(attributes, null, "\t"))
         
         attributes.forEach(function (row)
         {
-            if (row[1] && row[2] != "true")
+            if (row[1] && row[2] != "0")
                 deleteCond.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1]);
             else if (row[1])
                 updateCond.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1]);
diff --git a/process/Bulkmail_lib/process.js b/process/Bulkmail_lib/process.js
index 3ec4471587..6d109c2b44 100644
--- a/process/Bulkmail_lib/process.js
+++ b/process/Bulkmail_lib/process.js
@@ -1,250 +1,250 @@
-import("system.util");
-import("Contact_lib");
-import("system.datetime");
-import("system.neon");
-import("Employee_lib");
-import("system.vars");
-import("KeywordRegistry_basic");
-import("Sql_lib");
-import("system.db");
-import("DocumentTemplate_lib");
-import("Communication_lib");
-import("Email_lib");
-import("system.process");
-import("system.notification");
-
-/**
- * functions for bulk mails
- */
-function BulkMailUtils () {}
-
-/**
- * Executes a process to send bulk mails on the server and creates a notification when finished.
- * 
- * @param {String} pBulkMailId id of the bulk mail
- * @param {String} [pUser=currentUser] User that will get the notification, if null (not undefined!), no notification
- *                                      will be created.
- */
-BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pUser)
-{
-    if (pUser === undefined)
-        pUser = EmployeeUtils.getCurrentUserId();
-    process.execute("sendBulkMail_serverProcess", 
-        {
-            bulkMailId : pBulkMailId,
-            user : pUser || ""
-        }
-    );
-}
-
-/**
- * Sends a bulk mail. You should only call this function on the server because it
- * can take some time to execute, use BulkMailUtils.sendBulkMailOnServer instead.
- * 
- * @param {String} pBulkMailId id of the bulk mail 
- * 
- * @return {Object} count of sucessful and failed mails 
- */
-BulkMailUtils.sendBulkMail = function (pBulkMailId)
-{
-    var [templateId, subject, emailSender] = db.array(db.ROW, SqlCondition.begin()
-        .andPrepare("BULKMAIL.BULKMAILID", pBulkMailId)
-        .buildSql("select DOCUMENTTEMPLATE_ID, SUBJECT, SENDER from BULKMAIL", "1=2")
-    );
-    var template = BulkMailUtils.getBulkMailTemplate(pBulkMailId, templateId);
-    
-    var sql = SqlBuilder.begin()
-        .select("BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, (" + CommUtil.getStandardSubSqlMail() + ")")
-        .from("BULKMAILRECIPIENT")
-        .join("CONTACT", "BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID")
-        .where(SqlCondition.begin()
-            .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)
-            .andPrepare("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$sent(), "# != ?")
-            .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)))
-        .build();
-        
-    var recipientData = db.table(sql);
-    var contactIds = recipientData.map(function (e) {return e[1];});
-    var successIds = [];
-    var failedIds = [];
-    var sentDate = vars.get("$sys.date");
-    var mails = template.getReplacedEmailsByContactIds(contactIds);
-    
-    var subjectTemplate = new DocumentTemplate(subject, DocumentTemplate.types.PLAIN);
-    var subjects = subjectTemplate.getReplacedContentByContactIds(contactIds);
-    
-    for (let i = 0, l = recipientData.length; i < l; i++)
-    {
-        let isSuccess = false;
-        let contactId = recipientData[i][1];
-        let email = mails[contactId];
-        if (email !== undefined && recipientData[i][2])
-        {
-            email.toRecipients = [recipientData[i][2]];
-            email.sender = emailSender;
-            email.subject = subjects[contactId];
-
-            isSuccess = email.send();
-        }
-        if (isSuccess)
-            successIds.push(recipientData[i][0]); //set the recipient status to 'sent'
-        else
-            failedIds.push(recipientData[i][0]); //set the recipient status to 'failed'
-    }
-    db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$sent(), sentDate], 
-        SqlCondition.begin()
-            .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds)
-            .build("1=2")
-    );
-    db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$failed(), sentDate], 
-        SqlCondition.begin()
-            .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds)
-            .build("1=2")
-    );
-        
-    db.updateData("BULKMAIL", ["STATUS"], null, [$KeywordRegistry.bulkMailStatus$sent()], 
-        SqlCondition.equals("BULKMAIL.BULKMAILID", pBulkMailId, "1=2"));
-        
-    return {
-        sucessful : successIds.length,
-        failed : failedIds.length
-    };
-}
-
-
-BulkMailUtils.openAddRecipientView = function (pContactIds)
-{
-    var params = {
-        "ContactIds_param" : pContactIds
-    };
-    neon.openContext("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params);
-}
-
-/**
- * deletes all bulk mail recipients that have a commrestriction for emails
- * 
- * @param {String} pBulkMailId
- */
-BulkMailUtils.removeCommRestrictionRecipients = function (pBulkMailId)
-{
-    var recipientIds = db.array(db.COLUMN, SqlBuilder.begin()
-        .select("BULKMAILRECIPIENTID")
-        .from("BULKMAILRECIPIENT")
-        .join("CONTACT", SqlCondition.begin()
-            .and("BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID")
-            .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail())))
-        .where(SqlCondition.begin()
-            .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId))
-        .build());
-
-    if (recipientIds.length)
-    {
-        db.deleteData("BULKMAILRECIPIENT", SqlCondition.begin()
-            .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", recipientIds)
-            .build("1=2"));
-    }
-}
-
-/**
- * adds recipients to a bulkmail
- * 
- * @param {String} pBulkMailId bulk mail id
- * @param {String[]} pContactIds contact ids of the recipients
- */
-BulkMailUtils.addRecipients = function (pBulkMailId, pContactIds)
-{
-    var columns = [
-        "BULKMAILRECIPIENTID",
-        "BULKMAIL_ID",
-        "CONTACT_ID",
-        "STATUS"
-    ];
-    var inserts = [];
-    for (let i = 0, l = pContactIds.length; i < l; i++)
-    {
-        inserts.push(["BULKMAILRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContactIds[i], $KeywordRegistry.bulkMailRecipientStatus$pending()]]);
-    }
-    db.inserts(inserts);
-}
-
-/**
- * Loads the document template of a bulk mail. If the bulk mail itself has a
- * template, it is preferred over the documentTemplate-id.
- * 
- * @param {String} pBulkMailId bulkmail id
- * @param {String} pDocumentTemplateId documentTemplate id
- * 
- * @return {DocumentTemplate} the document template, null if no content was found.
- */
-BulkMailUtils.getBulkMailTemplate = function (pBulkMailId, pDocumentTemplateId)
-{
-    var template = DocumentTemplate.loadTemplate(pBulkMailId, "BULKMAIL");
-    if (!template.type)
-        template = DocumentTemplate.loadTemplate(pDocumentTemplateId);
-    return template;
-}
-
-BulkMailUtils.isRecipient = function (pBulkMailId, pContactId)
-{
-    return db.cell(SqlCondition.begin()
-        .andPrepare("BULKMAILRECIPIENT.CONTACT_ID", pContactId)
-        .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)
-        .buildSql("select count(*) from BULKMAILRECIPIENT") //TODO: is there a way exists could be used?
-    ) != "0";
-}
-
-function SerialLetterUtils () {}
-
-/**
- * adds recipients to a serial letter
- * 
- * @param {String} pSerialLetterId serial letter id
- * @param {String[]} pContactIds contact ids of the recipients
- */
-SerialLetterUtils.addRecipients = function (pSerialLetterId, pContactIds)
-{
-    var columns = [
-        "LETTERRECIPIENTID",
-        "SERIALLETTER_ID",
-        "CONTACT_ID"
-    ];
-    var inserts = [];
-    for (let i = 0, l = pContactIds.length; i < l; i++)
-    {
-        inserts.push(["LETTERRECIPIENT", columns, null, [util.getNewUUID(), pSerialLetterId, pContactIds[i]]]);
-    }
-    db.inserts(inserts);
-}
-
-
-SerialLetterUtils.openAddRecipientView = function (pContactIds)
-{
-    var params = {
-        "ContactIds_param" : pContactIds
-    };
-    neon.openContext("SerialLetterAddRecipients", "SerialLetterAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params);
-}
-
-/**
- * executes a server process that builds a serial letter
- * 
- * @param {String} pSerialLetterId serial letter id
- * @param {String[]} [pRecipientIds] Letter recipient ids of that should be used.
- *                                    If omitted, all recipients of the letter will be used.
- */
-SerialLetterUtils.buildSerialLetter = function (pSerialLetterId, pRecipientIds)
-{
-    process.execute("buildSerialLetter_serverProcess", {
-        "serialLetterId" : pSerialLetterId,
-        "recipientIds" : JSON.stringify(pRecipientIds)
-    });
-}
-
-SerialLetterUtils.isRecipient = function (pSerialLetterId, pContactId)
-{
-    return db.cell(SqlCondition.begin()
-        .andPrepare("LETTERRECIPIENT.CONTACT_ID", pContactId)
-        .andPrepare("LETTERRECIPIENT.SERIALLETTER_ID", pSerialLetterId)
-        .buildSql("select count(*) from LETTERRECIPIENT") //TODO: is there a way exists could be used?
-    ) != "0";
+import("system.util");
+import("Contact_lib");
+import("system.datetime");
+import("system.neon");
+import("Employee_lib");
+import("system.vars");
+import("KeywordRegistry_basic");
+import("Sql_lib");
+import("system.db");
+import("DocumentTemplate_lib");
+import("Communication_lib");
+import("Email_lib");
+import("system.process");
+import("system.notification");
+
+/**
+ * functions for bulk mails
+ */
+function BulkMailUtils () {}
+
+/**
+ * Executes a process to send bulk mails on the server and creates a notification when finished.
+ * 
+ * @param {String} pBulkMailId id of the bulk mail
+ * @param {String} [pUser=currentUser] User that will get the notification, if null (not undefined!), no notification
+ *                                      will be created.
+ */
+BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pUser)
+{
+    if (pUser === undefined)
+        pUser = EmployeeUtils.getCurrentUserId();
+    process.execute("sendBulkMail_serverProcess", 
+        {
+            bulkMailId : pBulkMailId,
+            user : pUser || ""
+        }
+    );
+}
+
+/**
+ * Sends a bulk mail. You should only call this function on the server because it
+ * can take some time to execute, use BulkMailUtils.sendBulkMailOnServer instead.
+ * 
+ * @param {String} pBulkMailId id of the bulk mail 
+ * 
+ * @return {Object} count of sucessful and failed mails 
+ */
+BulkMailUtils.sendBulkMail = function (pBulkMailId)
+{
+    var [templateId, subject, emailSender] = db.array(db.ROW, SqlCondition.begin()
+        .andPrepare("BULKMAIL.BULKMAILID", pBulkMailId)
+        .buildSql("select DOCUMENTTEMPLATE_ID, SUBJECT, SENDER from BULKMAIL", "1=2")
+    );
+    var template = BulkMailUtils.getBulkMailTemplate(pBulkMailId, templateId);
+    
+    var sql = SqlBuilder.begin()
+        .select("BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, (" + CommUtil.getStandardSubSqlMail() + ")")
+        .from("BULKMAILRECIPIENT")
+        .join("CONTACT", "BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID")
+        .where(SqlCondition.begin()
+            .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)
+            .andPrepare("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$sent(), "# != ?")
+            .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)))
+        .build();
+        
+    var recipientData = db.table(sql);
+    var contactIds = recipientData.map(function (e) {return e[1];});
+    var successIds = [];
+    var failedIds = [];
+    var sentDate = vars.get("$sys.date");
+    var mails = template.getReplacedEmailsByContactIds(contactIds);
+    
+    var subjectTemplate = new DocumentTemplate(subject, DocumentTemplate.types.PLAIN);
+    var subjects = subjectTemplate.getReplacedContentByContactIds(contactIds);
+    
+    for (let i = 0, l = recipientData.length; i < l; i++)
+    {
+        let isSuccess = false;
+        let contactId = recipientData[i][1];
+        let email = mails[contactId];
+        if (email !== undefined && recipientData[i][2])
+        {
+            email.toRecipients = [recipientData[i][2]];
+            email.sender = emailSender;
+            email.subject = subjects[contactId];
+
+            isSuccess = email.send();
+        }
+        if (isSuccess)
+            successIds.push(recipientData[i][0]); //set the recipient status to 'sent'
+        else
+            failedIds.push(recipientData[i][0]); //set the recipient status to 'failed'
+    }
+    db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$sent(), sentDate], 
+        SqlCondition.begin()
+            .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds)
+            .build("1=2")
+    );
+    db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$failed(), sentDate], 
+        SqlCondition.begin()
+            .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds)
+            .build("1=2")
+    );
+        
+    db.updateData("BULKMAIL", ["STATUS"], null, [$KeywordRegistry.bulkMailStatus$sent()], 
+        SqlCondition.equals("BULKMAIL.BULKMAILID", pBulkMailId, "1=2"));
+        
+    return {
+        sucessful : successIds.length,
+        failed : failedIds.length
+    };
+}
+
+
+BulkMailUtils.openAddRecipientView = function (pContactIds)
+{
+    var params = {
+        "ContactIds_param" : pContactIds
+    };
+    neon.openContext("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params);
+}
+
+/**
+ * deletes all bulk mail recipients that have a commrestriction for emails
+ * 
+ * @param {String} pBulkMailId
+ */
+BulkMailUtils.removeCommRestrictionRecipients = function (pBulkMailId)
+{
+    var recipientIds = db.array(db.COLUMN, SqlBuilder.begin()
+        .select("BULKMAILRECIPIENTID")
+        .from("BULKMAILRECIPIENT")
+        .join("CONTACT", SqlCondition.begin()
+            .and("BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID")
+            .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail())))
+        .where(SqlCondition.begin()
+            .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId))
+        .build());
+
+    if (recipientIds.length)
+    {
+        db.deleteData("BULKMAILRECIPIENT", SqlCondition.begin()
+            .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", recipientIds)
+            .build("1=2"));
+    }
+}
+
+/**
+ * adds recipients to a bulkmail
+ * 
+ * @param {String} pBulkMailId bulk mail id
+ * @param {String[]} pContactIds contact ids of the recipients
+ */
+BulkMailUtils.addRecipients = function (pBulkMailId, pContactIds)
+{
+    var columns = [
+        "BULKMAILRECIPIENTID",
+        "BULKMAIL_ID",
+        "CONTACT_ID",
+        "STATUS"
+    ];
+    var inserts = [];
+    for (let i = 0, l = pContactIds.length; i < l; i++)
+    {
+        inserts.push(["BULKMAILRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContactIds[i], $KeywordRegistry.bulkMailRecipientStatus$pending()]]);
+    }
+    db.inserts(inserts);
+}
+
+/**
+ * Loads the document template of a bulk mail. If the bulk mail itself has a
+ * template, it is preferred over the documentTemplate-id.
+ * 
+ * @param {String} pBulkMailId bulkmail id
+ * @param {String} pDocumentTemplateId documentTemplate id
+ * 
+ * @return {DocumentTemplate} the document template, null if no content was found.
+ */
+BulkMailUtils.getBulkMailTemplate = function (pBulkMailId, pDocumentTemplateId)
+{
+    var template = DocumentTemplate.loadTemplate(pBulkMailId, "BULKMAIL");
+    if (!template.type)
+        template = DocumentTemplate.loadTemplate(pDocumentTemplateId);
+    return template;
+}
+
+BulkMailUtils.isRecipient = function (pBulkMailId, pContactId)
+{
+    return db.cell(SqlCondition.begin()
+        .andPrepare("BULKMAILRECIPIENT.CONTACT_ID", pContactId)
+        .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)
+        .buildSql("select count(*) from BULKMAILRECIPIENT") //TODO: is there a way exists could be used?
+    ) != "0";
+}
+
+function SerialLetterUtils () {}
+
+/**
+ * adds recipients to a serial letter
+ * 
+ * @param {String} pSerialLetterId serial letter id
+ * @param {String[]} pContactIds contact ids of the recipients
+ */
+SerialLetterUtils.addRecipients = function (pSerialLetterId, pContactIds)
+{
+    var columns = [
+        "LETTERRECIPIENTID",
+        "SERIALLETTER_ID",
+        "CONTACT_ID"
+    ];
+    var inserts = [];
+    for (let i = 0, l = pContactIds.length; i < l; i++)
+    {
+        inserts.push(["LETTERRECIPIENT", columns, null, [util.getNewUUID(), pSerialLetterId, pContactIds[i]]]);
+    }
+    db.inserts(inserts);
+}
+
+
+SerialLetterUtils.openAddRecipientView = function (pContactIds)
+{
+    var params = {
+        "ContactIds_param" : pContactIds
+    };
+    neon.openContext("SerialLetterAddRecipients", "SerialLetterAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params);
+}
+
+/**
+ * executes a server process that builds a serial letter
+ * 
+ * @param {String} pSerialLetterId serial letter id
+ * @param {String[]} [pRecipientIds] Letter recipient ids of that should be used.
+ *                                    If omitted, all recipients of the letter will be used.
+ */
+SerialLetterUtils.buildSerialLetter = function (pSerialLetterId, pRecipientIds)
+{
+    process.execute("buildSerialLetter_serverProcess", {
+        "serialLetterId" : pSerialLetterId,
+        "recipientIds" : JSON.stringify(pRecipientIds)
+    });
+}
+
+SerialLetterUtils.isRecipient = function (pSerialLetterId, pContactId)
+{
+    return db.cell(SqlCondition.begin()
+        .andPrepare("LETTERRECIPIENT.CONTACT_ID", pContactId)
+        .andPrepare("LETTERRECIPIENT.SERIALLETTER_ID", pSerialLetterId)
+        .buildSql("select count(*) from LETTERRECIPIENT") //TODO: is there a way exists could be used?
+    ) != "0";
 }
\ No newline at end of file
diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index 817b916a62..97349505c7 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -835,25 +835,20 @@ SqlBuilder.prototype.having = function (pCondition)
 SqlBuilder.prototype._getClause = function (pElement, pPrefix, pAutoJoin)
 {
     var preparedValues = [];
-    if (pElement instanceof SqlBuilder || pElement instanceof SqlCondition)
+    if (typeof pElement !== "string")
     {
-        pElement = _getElement(pElement);
-    }
-    else if (typeof pElement !== "string" && pElement.length !== undefined) //array
-    {
-        if (pAutoJoin)
+        if (pElement.length !== undefined && pAutoJoin) //array of fields
         {
             for (let i = 0, l = pElement.length; i < l; i++)
             {
-                if (pElement[i] instanceof SqlBuilder)
-                    pElement[i] = _getElement(pElement);
+                if (typeof pElement[i] !== "string")
+                    pElement[i] = _getElement(pElement[i]);
             }
             pElement = pElement.join(", ");
         }
         else
         {
-            preparedValues = preparedValues.concat(pElement[1]);
-            pElement = pElement[0];
+            pElement = _getElement(pElement);
         }
     }
     
@@ -864,9 +859,10 @@ SqlBuilder.prototype._getClause = function (pElement, pPrefix, pAutoJoin)
     
     function _getElement (element)
     {
-        element = element.build();
+        if (element instanceof SqlBuilder || element instanceof SqlCondition)
+            element = element.build();
         preparedValues = preparedValues.concat(element[1]);
-        if (element instanceof SqlBuilder)
+        if (element instanceof SqlBuilder || pAutoJoin)
             return "(" + element[0] + ")";
         return element[0];
     }
-- 
GitLab