From 50b3739a308114ea218f1f7bb2d969bb82a4daa0 Mon Sep 17 00:00:00 2001
From: "S.Listl" <s.listl@adito.de>
Date: Thu, 5 Dec 2019 11:34:44 +0100
Subject: [PATCH] Objectselection attributes with lookup

---
 entity/Activity_entity/Activity_entity.aod    |   8 +
 .../AnyContact_entity/AnyContact_entity.aod   |   6 +
 .../AttributeRelation_entity.aod              |  47 ++
 .../dropdowndefinition/valueProcess.js        |   7 +
 .../dropdownfilter/valueProcess.js            |   7 +
 .../children/entity_param/valueProcess.js     |   8 +
 .../children/filter_param/valueProcess.js     |   9 +
 .../entityfields/value/displayValueProcess.js |  14 +-
 .../entityfields/value/dropDownProcess.js     |   4 +-
 .../entityfields/value/mandatoryProcess.js    |   2 +-
 .../entityfields/value/stateProcess.js        |   6 +-
 .../value_lookup/contentTypeProcess.js        |   6 +
 .../value_lookup/displayValueProcess.js       |   5 +
 .../value_lookup/mandatoryProcess.js          |   5 +
 .../entityfields/value_lookup/stateProcess.js |  12 +
 .../recordcontainers/jdito/contentProcess.js  |  11 +-
 .../recordcontainers/jdito/onInsert.js        |   5 +-
 .../recordcontainers/jdito/onUpdate.js        |  14 +-
 entity/Attribute_entity/Attribute_entity.aod  |   6 +
 entity/BulkMail_entity/BulkMail_entity.aod    |  39 +-
 .../CampaignStep_entity.aod                   |   6 +
 entity/Campaign_entity/Campaign_entity.aod    |   6 +
 entity/Contract_entity/Contract_entity.aod    |   6 +
 .../DocumentTemplate_entity.aod               |   6 +
 entity/Employee_entity/Employee_entity.aod    |  12 +
 entity/Language_entity/Language_entity.aod    |   6 +
 .../ObjectProxy_entity/ObjectProxy_entity.aod | 211 ++++++++
 .../ObjectProxy_entity/contentTitleProcess.js |   4 +
 .../entityfields/consumer/valueProcess.js     |  37 ++
 .../filteredobjects/targetConsumerProcess.js  |   5 +
 .../recordcontainers/jdito/contentProcess.js  |  38 ++
 .../recordcontainers/jdito/rowCountProcess.js |  19 +
 entity/Offer_entity/Offer_entity.aod          |   6 +
 entity/Order_entity/Order_entity.aod          |   6 +
 .../Organisation_entity.aod                   |   6 +
 entity/Person_entity/Person_entity.aod        |   6 +
 entity/Product_entity/Product_entity.aod      |   6 +
 .../Salesproject_entity.aod                   |   6 +
 .../SerialLetter_entity.aod                   |   6 +
 .../SupportTicket_entity.aod                  |   8 +
 entity/Task_entity/Task_entity.aod            |   6 +
 neonContext/ObjectProxy/ObjectProxy.aod       |   6 +
 .../AttributeRelationMultiEdit_view.aod       |   4 +
 .../AttributeRelationTreeEdit_view.aod        |   4 +
 .../QuickEntryEdit_view.aod                   |   2 +-
 process/Attribute_lib/process.js              | 485 ++++++++++--------
 46 files changed, 879 insertions(+), 255 deletions(-)
 create mode 100644 entity/AttributeRelation_entity/entityfields/dropdowndefinition/valueProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/dropdownfilter/valueProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/objects/children/entity_param/valueProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/objects/children/filter_param/valueProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/value_lookup/contentTypeProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/value_lookup/displayValueProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/value_lookup/mandatoryProcess.js
 create mode 100644 entity/AttributeRelation_entity/entityfields/value_lookup/stateProcess.js
 create mode 100644 entity/ObjectProxy_entity/ObjectProxy_entity.aod
 create mode 100644 entity/ObjectProxy_entity/contentTitleProcess.js
 create mode 100644 entity/ObjectProxy_entity/entityfields/consumer/valueProcess.js
 create mode 100644 entity/ObjectProxy_entity/entityfields/filteredobjects/targetConsumerProcess.js
 create mode 100644 entity/ObjectProxy_entity/recordcontainers/jdito/contentProcess.js
 create mode 100644 entity/ObjectProxy_entity/recordcontainers/jdito/rowCountProcess.js
 create mode 100644 neonContext/ObjectProxy/ObjectProxy.aod

diff --git a/entity/Activity_entity/Activity_entity.aod b/entity/Activity_entity/Activity_entity.aod
index 2f8b91974f..1b8fa244df 100644
--- a/entity/Activity_entity/Activity_entity.aod
+++ b/entity/Activity_entity/Activity_entity.aod
@@ -79,6 +79,14 @@
     <entityProvider>
       <name>#PROVIDER</name>
       <recordContainer>db</recordContainer>
+      <dependencies>
+        <entityDependency>
+          <name>3ea70130-21b0-42ec-88e2-1a898059dcff</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Activities</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
+      </dependencies>
     </entityProvider>
     <entityField>
       <name>ICON</name>
diff --git a/entity/AnyContact_entity/AnyContact_entity.aod b/entity/AnyContact_entity/AnyContact_entity.aod
index 9a481a5282..e6f1469bc9 100644
--- a/entity/AnyContact_entity/AnyContact_entity.aod
+++ b/entity/AnyContact_entity/AnyContact_entity.aod
@@ -69,6 +69,12 @@
           <fieldName>Contacts</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>60518a9f-eaa5-4af8-8cd6-4276047f4371</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Contacts</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/AttributeRelation_entity/AttributeRelation_entity.aod b/entity/AttributeRelation_entity/AttributeRelation_entity.aod
index 65a21feaf5..280b54d25e 100644
--- a/entity/AttributeRelation_entity/AttributeRelation_entity.aod
+++ b/entity/AttributeRelation_entity/AttributeRelation_entity.aod
@@ -298,6 +298,41 @@
       <name>GetTheme_param</name>
       <expose v="true" />
     </entityParameter>
+    <entityField>
+      <name>VALUE_LOOKUP</name>
+      <title>Value</title>
+      <consumer>Objects</consumer>
+      <contentTypeProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/value_lookup/contentTypeProcess.js</contentTypeProcess>
+      <mandatoryProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/value_lookup/mandatoryProcess.js</mandatoryProcess>
+      <stateProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/value_lookup/stateProcess.js</stateProcess>
+      <displayValueProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/value_lookup/displayValueProcess.js</displayValueProcess>
+    </entityField>
+    <entityConsumer>
+      <name>Objects</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>ObjectProxy_entity</entityName>
+        <fieldName>FilteredObjects</fieldName>
+      </dependency>
+      <children>
+        <entityParameter>
+          <name>Entity_param</name>
+          <valueProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/objects/children/entity_param/valueProcess.js</valueProcess>
+        </entityParameter>
+        <entityParameter>
+          <name>Filter_param</name>
+          <valueProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/objects/children/filter_param/valueProcess.js</valueProcess>
+        </entityParameter>
+      </children>
+    </entityConsumer>
+    <entityField>
+      <name>DROPDOWNDEFINITION</name>
+      <valueProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/dropdowndefinition/valueProcess.js</valueProcess>
+    </entityField>
+    <entityField>
+      <name>DROPDOWNFILTER</name>
+      <valueProcess>%aditoprj%/entity/AttributeRelation_entity/entityfields/dropdownfilter/valueProcess.js</valueProcess>
+    </entityField>
   </entityFields>
   <recordContainers>
     <jDitoRecordContainer>
@@ -344,6 +379,18 @@
         <jDitoRecordFieldMapping>
           <name>USER_EDIT.value</name>
         </jDitoRecordFieldMapping>
+        <jDitoRecordFieldMapping>
+          <name>VALUE_LOOKUP.value</name>
+        </jDitoRecordFieldMapping>
+        <jDitoRecordFieldMapping>
+          <name>VALUE_LOOKUP.displayValue</name>
+        </jDitoRecordFieldMapping>
+        <jDitoRecordFieldMapping>
+          <name>DROPDOWNDEFINITION.value</name>
+        </jDitoRecordFieldMapping>
+        <jDitoRecordFieldMapping>
+          <name>DROPDOWNFILTER.value</name>
+        </jDitoRecordFieldMapping>
       </recordFieldMappings>
     </jDitoRecordContainer>
   </recordContainers>
diff --git a/entity/AttributeRelation_entity/entityfields/dropdowndefinition/valueProcess.js b/entity/AttributeRelation_entity/entityfields/dropdowndefinition/valueProcess.js
new file mode 100644
index 0000000000..616aec7d09
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/dropdowndefinition/valueProcess.js
@@ -0,0 +1,7 @@
+import("system.result");
+import("Sql_lib");
+
+result.string(newSelect("DROPDOWNDEFINITION")
+    .from("AB_ATTRIBUTE")
+    .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", "$field.AB_ATTRIBUTE_ID")
+    .cell());
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/dropdownfilter/valueProcess.js b/entity/AttributeRelation_entity/entityfields/dropdownfilter/valueProcess.js
new file mode 100644
index 0000000000..9b3220d760
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/dropdownfilter/valueProcess.js
@@ -0,0 +1,7 @@
+import("system.result");
+import("Sql_lib");
+
+result.string(newSelect("DROPDOWNFILTER")
+    .from("AB_ATTRIBUTE")
+    .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", "$field.AB_ATTRIBUTE_ID")
+    .cell());
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/objects/children/entity_param/valueProcess.js b/entity/AttributeRelation_entity/entityfields/objects/children/entity_param/valueProcess.js
new file mode 100644
index 0000000000..a911aa21fa
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/objects/children/entity_param/valueProcess.js
@@ -0,0 +1,8 @@
+import("Attribute_lib");
+import("system.vars");
+import("system.result");
+
+if (AttributeTypeUtil.useLookup(vars.get("$field.ATTRIBUTE_TYPE")))
+    result.string(vars.get("$field.DROPDOWNDEFINITION"));
+else
+    result.string("");
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/objects/children/filter_param/valueProcess.js b/entity/AttributeRelation_entity/entityfields/objects/children/filter_param/valueProcess.js
new file mode 100644
index 0000000000..e43c27b884
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/objects/children/filter_param/valueProcess.js
@@ -0,0 +1,9 @@
+import("system.vars");
+import("system.result");
+
+var filter = vars.get("$field.DROPDOWNFILTER");
+if (filter)
+{
+    filter = JSON.parse(filter);
+    result.string(JSON.stringify(filter.filter));
+}
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/value/displayValueProcess.js b/entity/AttributeRelation_entity/entityfields/value/displayValueProcess.js
index 415b66e5b0..1a44f8dfe0 100644
--- a/entity/AttributeRelation_entity/entityfields/value/displayValueProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/value/displayValueProcess.js
@@ -1,17 +1,5 @@
-import("Context_lib");
-import("system.db");
 import("system.result");
 import("system.vars");
 import("Attribute_lib");
-import("Keyword_lib");
-import("system.tools");
-import("Sql_lib");
 
-var attrType = vars.get("$field.ATTRIBUTE_TYPE");
-var dropDownDef = db.cell(
-    SqlCondition.begin()
-        .andPrepareVars("AB_ATTRIBUTE.AB_ATTRIBUTEID", "$field.AB_ATTRIBUTE_ID")
-        .buildSql("select DROPDOWNDEFINITION from AB_ATTRIBUTE")
-);
-
-result.string(AttributeTypeUtil.getAttributeViewValue(attrType, vars.get("$field.VALUE"), dropDownDef));
+result.string(AttributeTypeUtil.getAttributeViewValue(vars.get("$field.ATTRIBUTE_TYPE"), vars.get("$field.VALUE"), vars.get("$field.DROPDOWNDEFINITION")));
diff --git a/entity/AttributeRelation_entity/entityfields/value/dropDownProcess.js b/entity/AttributeRelation_entity/entityfields/value/dropDownProcess.js
index 97299f24fa..732f5909c3 100644
--- a/entity/AttributeRelation_entity/entityfields/value/dropDownProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/value/dropDownProcess.js
@@ -9,5 +9,7 @@ import("Sql_lib");
 var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
 var attrType = vars.get("$field.ATTRIBUTE_TYPE");
 
-var res = AttributeUtil.getPossibleListValues(attributeId, attrType);
+var res = null;
+if (!AttributeTypeUtil.useLookup(attrType))
+    res = AttributeUtil.getPossibleListValues(attributeId, attrType);
 result.object(res);
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/value/mandatoryProcess.js b/entity/AttributeRelation_entity/entityfields/value/mandatoryProcess.js
index 010fc7942e..d63b076d5a 100644
--- a/entity/AttributeRelation_entity/entityfields/value/mandatoryProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/value/mandatoryProcess.js
@@ -4,4 +4,4 @@ import("Attribute_lib");
 
 var attributeType = vars.get("$field.ATTRIBUTE_TYPE");
 var contentType = AttributeTypeUtil.getContentType(attributeType);
-result.string(contentType != null && contentType != "BOOLEAN");
\ No newline at end of file
+result.string(!AttributeTypeUtil.useLookup(attributeType) && contentType != null && contentType != "BOOLEAN");
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/value/stateProcess.js b/entity/AttributeRelation_entity/entityfields/value/stateProcess.js
index 48ef8ec666..b1efa78201 100644
--- a/entity/AttributeRelation_entity/entityfields/value/stateProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/value/stateProcess.js
@@ -4,9 +4,11 @@ import("system.result");
 import("Attribute_lib");
 
 var attributeType = vars.get("$field.ATTRIBUTE_TYPE");
-result.string(AttributeTypeUtil.getContentType(attributeType));
+
 var fieldState;
-if (AttributeTypeUtil.getContentType(attributeType) != null)
+if (AttributeTypeUtil.useLookup(attributeType) && vars.get("$sys.recordstate") != neon.OPERATINGSTATE_VIEW)
+    fieldState = neon.COMPONENTSTATE_INVISIBLE;
+else if (AttributeTypeUtil.getContentType(attributeType) != null)
     fieldState = neon.COMPONENTSTATE_EDITABLE;
 else
     fieldState = neon.COMPONENTSTATE_READONLY;
diff --git a/entity/AttributeRelation_entity/entityfields/value_lookup/contentTypeProcess.js b/entity/AttributeRelation_entity/entityfields/value_lookup/contentTypeProcess.js
new file mode 100644
index 0000000000..e67d19d3de
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/value_lookup/contentTypeProcess.js
@@ -0,0 +1,6 @@
+import("system.vars");
+import("system.result");
+import("Attribute_lib");
+
+var attributeType = vars.get("$field.ATTRIBUTE_TYPE");
+result.string(AttributeTypeUtil.getContentType(attributeType));
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/value_lookup/displayValueProcess.js b/entity/AttributeRelation_entity/entityfields/value_lookup/displayValueProcess.js
new file mode 100644
index 0000000000..86e28c21c7
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/value_lookup/displayValueProcess.js
@@ -0,0 +1,5 @@
+import("system.result");
+import("system.vars");
+import("Attribute_lib");
+
+result.string(AttributeTypeUtil.getAttributeViewValue(vars.get("$field.ATTRIBUTE_TYPE"), vars.get("$field.VALUE_LOOKUP"), vars.get("$field.DROPDOWNDEFINITION")));
diff --git a/entity/AttributeRelation_entity/entityfields/value_lookup/mandatoryProcess.js b/entity/AttributeRelation_entity/entityfields/value_lookup/mandatoryProcess.js
new file mode 100644
index 0000000000..0aeb432ff7
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/value_lookup/mandatoryProcess.js
@@ -0,0 +1,5 @@
+import("system.vars");
+import("system.result");
+import("Attribute_lib");
+
+result.string(AttributeTypeUtil.useLookup(vars.get("$field.ATTRIBUTE_TYPE")));
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/value_lookup/stateProcess.js b/entity/AttributeRelation_entity/entityfields/value_lookup/stateProcess.js
new file mode 100644
index 0000000000..e0ce90c175
--- /dev/null
+++ b/entity/AttributeRelation_entity/entityfields/value_lookup/stateProcess.js
@@ -0,0 +1,12 @@
+import("system.neon");
+import("system.vars");
+import("system.result");
+import("Attribute_lib");
+
+var fieldState;
+if (AttributeTypeUtil.useLookup(vars.get("$field.ATTRIBUTE_TYPE")))
+    fieldState = neon.COMPONENTSTATE_EDITABLE;
+else 
+    fieldState = neon.COMPONENTSTATE_INVISIBLE;
+
+result.string(fieldState);
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/recordcontainers/jdito/contentProcess.js b/entity/AttributeRelation_entity/recordcontainers/jdito/contentProcess.js
index 0300adac47..dc05f17a36 100644
--- a/entity/AttributeRelation_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/AttributeRelation_entity/recordcontainers/jdito/contentProcess.js
@@ -28,7 +28,8 @@ var defaultFields = [
     "AB_ATTRIBUTERELATION.DATE_NEW",
     "AB_ATTRIBUTERELATION.USER_NEW",
     "AB_ATTRIBUTERELATION.DATE_EDIT",
-    "AB_ATTRIBUTERELATION.USER_EDIT"
+    "AB_ATTRIBUTERELATION.USER_EDIT",
+    "AB_ATTRIBUTE.DROPDOWNFILTER"
 ];
 
 //these fields hold the attributeRelation value, depending on the attribute type
@@ -137,7 +138,7 @@ if (getTree)
 var attrRelations = db.table(attributeSql.build()).map(
     function (row) 
     {
-        var [attrRelId, attrId, attrParentId, attrType, attrName, dropDownDef, comboViewVal, dateNew, userNew, dateEdit, userEdit] = row;
+        var [attrRelId, attrId, attrParentId, attrType, attrName, dropDownDef, comboViewVal, dateNew, userNew, dateEdit, userEdit, dropDownFilter] = row;
         attrName = translate.text(attrName);
         attrType = attrType.trim();
         if (!getTree && !displaySimpleName && attrParentId)
@@ -169,7 +170,11 @@ var attrRelations = db.table(attributeSql.build()).map(
             dateNew, 
             userNew, 
             dateEdit, 
-            userEdit
+            userEdit,
+            value,
+            viewValue,
+            dropDownDef,
+            dropDownFilter
         ];
     }
 );
diff --git a/entity/AttributeRelation_entity/recordcontainers/jdito/onInsert.js b/entity/AttributeRelation_entity/recordcontainers/jdito/onInsert.js
index d082f34bf9..4a307bcf23 100644
--- a/entity/AttributeRelation_entity/recordcontainers/jdito/onInsert.js
+++ b/entity/AttributeRelation_entity/recordcontainers/jdito/onInsert.js
@@ -29,7 +29,10 @@ var valueField = AttributeTypeUtil.getDatabaseField(type);
 if (valueField)
 {
     columns.push(valueField);
-    values.push(rowdata["VALUE.value"]);
+    if (AttributeTypeUtil.useLookup(type))
+        values.push(rowdata["VALUE_LOOKUP.value"])
+    else
+        values.push(rowdata["VALUE.value"]);
 }
 db.insertData("AB_ATTRIBUTERELATION", columns, null, values);
 
diff --git a/entity/AttributeRelation_entity/recordcontainers/jdito/onUpdate.js b/entity/AttributeRelation_entity/recordcontainers/jdito/onUpdate.js
index 493d61541c..b1661291fc 100644
--- a/entity/AttributeRelation_entity/recordcontainers/jdito/onUpdate.js
+++ b/entity/AttributeRelation_entity/recordcontainers/jdito/onUpdate.js
@@ -31,10 +31,13 @@ if (uid.length === 1)
         columns.push(row);
         values.push(""); //set every field but the correct value field to null
     });
-    values[AttributeTypeUtil.getTypeColumnIndex(type) + 3] = rowdata["VALUE.value"];
+    if (AttributeTypeUtil.useLookup(type))
+        values[AttributeTypeUtil.getTypeColumnIndex(type) + 3] = rowdata["VALUE_LOOKUP.value"];
+    else
+        values[AttributeTypeUtil.getTypeColumnIndex(type) + 3] = rowdata["VALUE.value"];
 
-    db.updateData("AB_ATTRIBUTERELATION", columns, null, values, 
-        SqlCondition.equals("AB_ATTRIBUTERELATION.AB_ATTRIBUTERELATIONID", uid[0], "1=2"));
+    newWhere("AB_ATTRIBUTERELATION.AB_ATTRIBUTERELATIONID", "$local.uid")
+        .updateData(true, "AB_ATTRIBUTERELATION", columns, null, values);
 }
 else
 {
@@ -59,7 +62,10 @@ else
     if (valueField)
     {
         columns.push(valueField);
-        values.push(vars.get("$field.VALUE"));
+        if (AttributeTypeUtil.useLookup(type))
+            values.push(rowdata["VALUE_LOOKUP.value"])
+        else
+            values.push(rowdata["VALUE.value"]);
     }
     db.insertData("AB_ATTRIBUTERELATION", columns, null, values);
 }
diff --git a/entity/Attribute_entity/Attribute_entity.aod b/entity/Attribute_entity/Attribute_entity.aod
index 2d24ae89fb..492a65f54e 100644
--- a/entity/Attribute_entity/Attribute_entity.aod
+++ b/entity/Attribute_entity/Attribute_entity.aod
@@ -20,6 +20,12 @@
           <fieldName>ActivityAtrributes</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>42e6f528-8452-4262-97bd-68e8cbe99c74</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Attributes</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/BulkMail_entity/BulkMail_entity.aod b/entity/BulkMail_entity/BulkMail_entity.aod
index 46802d788e..f8662fcf48 100644
--- a/entity/BulkMail_entity/BulkMail_entity.aod
+++ b/entity/BulkMail_entity/BulkMail_entity.aod
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.12" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.12">
+<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.13" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.13">
   <name>BulkMail_entity</name>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <documentation>%aditoprj%/entity/BulkMail_entity/documentation.adoc</documentation>
@@ -7,6 +7,7 @@
   <title>Bulk mail</title>
   <grantUpdateProcess>%aditoprj%/entity/BulkMail_entity/grantUpdateProcess.js</grantUpdateProcess>
   <contentTitleProcess>%aditoprj%/entity/BulkMail_entity/contentTitleProcess.js</contentTitleProcess>
+  <afterUiInit>%aditoprj%/entity/BulkMail_entity/afterUiInit.js</afterUiInit>
   <iconId>VAADIN:AT</iconId>
   <titlePlural>Bulk mails</titlePlural>
   <recordContainer>db</recordContainer>
@@ -15,16 +16,15 @@
       <name>#PROVIDER</name>
       <dependencies>
         <entityDependency>
-          <name>88f8ded7-fe8f-41ef-8e01-030bae0867ee</name>
-          <entityName>BulkMailAddRecipients_entity</entityName>
-          <fieldName>BulkMails</fieldName>
+          <name>4ef3e311-d2b2-45bf-8f82-7f9ae7e107a9</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Bulkmails</fieldName>
           <isConsumer v="false" />
         </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
       <name>BULKMAILID</name>
-      <valueProcess>%aditoprj%/entity/BulkMail_entity/entityfields/bulkmailid/valueProcess.js</valueProcess>
     </entityField>
     <entityField>
       <name>NAME</name>
@@ -121,6 +121,7 @@
     </entityConsumer>
     <entityField>
       <name>bindata</name>
+      <title>Custom template</title>
       <contentType>FILE</contentType>
       <valueProcess>%aditoprj%/entity/BulkMail_entity/entityfields/bindata/valueProcess.js</valueProcess>
       <onValueChange>%aditoprj%/entity/BulkMail_entity/entityfields/bindata/onValueChange.js</onValueChange>
@@ -294,12 +295,35 @@
       <name>CopyBulkMailId_param</name>
       <expose v="true" />
     </entityParameter>
+    <entityProvider>
+      <name>BulkMailsNotSent</name>
+      <dependencies>
+        <entityDependency>
+          <name>16cdf326-0b43-4d72-bf19-21434e047e85</name>
+          <entityName>BulkMailAddRecipients_entity</entityName>
+          <fieldName>BulkMails</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
+      </dependencies>
+      <children>
+        <entityParameter>
+          <name>BulkMailStatus_param</name>
+          <valueProcess>%aditoprj%/entity/BulkMail_entity/entityfields/bulkmailsnotsent/children/bulkmailstatus_param/valueProcess.js</valueProcess>
+          <expose v="false" />
+        </entityParameter>
+      </children>
+    </entityProvider>
+    <entityParameter>
+      <name>BulkMailStatus_param</name>
+      <expose v="true" />
+    </entityParameter>
   </entityFields>
   <recordContainers>
     <dbRecordContainer>
       <name>db</name>
       <alias>Data_alias</alias>
       <fromClauseProcess>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/fromClauseProcess.js</fromClauseProcess>
+      <conditionProcess>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/conditionProcess.js</conditionProcess>
       <onDBInsert>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/onDBInsert.js</onDBInsert>
       <onDBUpdate>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js</onDBUpdate>
       <onDBDelete>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/onDBDelete.js</onDBDelete>
@@ -328,6 +352,7 @@
           <name>DESCRIPTION.value</name>
           <recordfield>BULKMAIL.DESCRIPTION</recordfield>
           <isFilterable v="true" />
+          <isLookupFilter v="true" />
         </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>DOCUMENTTEMPLATE_ID.value</name>
@@ -337,16 +362,19 @@
           <name>NAME.value</name>
           <recordfield>BULKMAIL.NAME</recordfield>
           <isFilterable v="true" />
+          <isLookupFilter v="true" />
         </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>SUBJECT.value</name>
           <recordfield>BULKMAIL.SUBJECT</recordfield>
           <isFilterable v="true" />
+          <isLookupFilter v="true" />
         </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>SENDER.value</name>
           <recordfield>BULKMAIL.SENDER</recordfield>
           <isFilterable v="true" />
+          <isLookupFilter v="true" />
         </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>DOCUMENTTEMPLATE_ID.displayValue</name>
@@ -356,6 +384,7 @@
           <name>STATUS.value</name>
           <recordfield>BULKMAIL.STATUS</recordfield>
           <isFilterable v="true" />
+          <isLookupFilter v="true" />
         </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>STATUS.displayValue</name>
diff --git a/entity/CampaignStep_entity/CampaignStep_entity.aod b/entity/CampaignStep_entity/CampaignStep_entity.aod
index 4d71ee8a7c..970b69407f 100644
--- a/entity/CampaignStep_entity/CampaignStep_entity.aod
+++ b/entity/CampaignStep_entity/CampaignStep_entity.aod
@@ -25,6 +25,12 @@
           <fieldName>CampaignSteps</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>ae4ec215-b2a0-4b19-b4f5-ca2410439421</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>CampaignSteps</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/Campaign_entity/Campaign_entity.aod b/entity/Campaign_entity/Campaign_entity.aod
index 0a14baecde..ec1559090a 100644
--- a/entity/Campaign_entity/Campaign_entity.aod
+++ b/entity/Campaign_entity/Campaign_entity.aod
@@ -28,6 +28,12 @@
           <fieldName>Campaigns</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>ccbac0b6-9708-4afe-9676-1c6aa913463c</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Campaigns</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/Contract_entity/Contract_entity.aod b/entity/Contract_entity/Contract_entity.aod
index 1af265865d..0fd4563f75 100644
--- a/entity/Contract_entity/Contract_entity.aod
+++ b/entity/Contract_entity/Contract_entity.aod
@@ -177,6 +177,12 @@
           <fieldName>Contracts</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>9b6c0b15-b252-49bf-a5b5-f7f74f984a9c</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Contracts</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/DocumentTemplate_entity/DocumentTemplate_entity.aod b/entity/DocumentTemplate_entity/DocumentTemplate_entity.aod
index 38e9072701..e5c3819564 100644
--- a/entity/DocumentTemplate_entity/DocumentTemplate_entity.aod
+++ b/entity/DocumentTemplate_entity/DocumentTemplate_entity.aod
@@ -21,6 +21,12 @@
           <fieldName>DocumentTemplates</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>aa0b6a21-d458-4723-b1d8-526324024662</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>DocumentTemplates</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/Employee_entity/Employee_entity.aod b/entity/Employee_entity/Employee_entity.aod
index ab5ba6e07b..c1e1a567da 100644
--- a/entity/Employee_entity/Employee_entity.aod
+++ b/entity/Employee_entity/Employee_entity.aod
@@ -316,6 +316,18 @@
     <entityField>
       <name>SHORT_UID</name>
     </entityField>
+    <entityProvider>
+      <name>EmployeesByUserId</name>
+      <lookupIdfield>SHORT_UID</lookupIdfield>
+      <dependencies>
+        <entityDependency>
+          <name>2b6d7d96-365d-44a0-a457-4d7a08ae18a5</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Employees</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
+      </dependencies>
+    </entityProvider>
   </entityFields>
   <recordContainers>
     <jDitoRecordContainer>
diff --git a/entity/Language_entity/Language_entity.aod b/entity/Language_entity/Language_entity.aod
index 314954b545..1c5a93ffbe 100644
--- a/entity/Language_entity/Language_entity.aod
+++ b/entity/Language_entity/Language_entity.aod
@@ -75,6 +75,12 @@
           <fieldName>Languages</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>6eb7101e-690f-4f55-a780-7b7129071fd1</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Languages</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
       <children>
         <entityParameter>
diff --git a/entity/ObjectProxy_entity/ObjectProxy_entity.aod b/entity/ObjectProxy_entity/ObjectProxy_entity.aod
new file mode 100644
index 0000000000..af9aecaf75
--- /dev/null
+++ b/entity/ObjectProxy_entity/ObjectProxy_entity.aod
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.13" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.13">
+  <name>ObjectProxy_entity</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <contentTitleProcess>%aditoprj%/entity/ObjectProxy_entity/contentTitleProcess.js</contentTitleProcess>
+  <recordContainer>jdito</recordContainer>
+  <entityFields>
+    <entityProvider>
+      <name>#PROVIDER</name>
+    </entityProvider>
+    <entityField>
+      <name>UID</name>
+    </entityField>
+    <entityField>
+      <name>TITLE</name>
+    </entityField>
+    <entityProvider>
+      <name>FilteredObjects</name>
+      <targetConsumerProcess>%aditoprj%/entity/ObjectProxy_entity/entityfields/filteredobjects/targetConsumerProcess.js</targetConsumerProcess>
+      <dependencies>
+        <entityDependency>
+          <name>92e21c48-19aa-402c-b75f-96cad75d8a14</name>
+          <entityName>AttributeRelation_entity</entityName>
+          <fieldName>Objects</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
+      </dependencies>
+    </entityProvider>
+    <entityParameter>
+      <name>Filter_param</name>
+      <expose v="true" />
+    </entityParameter>
+    <entityParameter>
+      <name>Entity_param</name>
+      <expose v="true" />
+    </entityParameter>
+    <entityConsumer>
+      <name>Offers</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Offer_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Contracts</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Contract_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Organisations</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Organisation_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Salesprojects</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Salesproject_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Activities</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Activity_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Tasks</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Task_entity</entityName>
+        <fieldName>Tasks</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Campaigns</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Campaign_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Orders</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Order_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Persons</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Person_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Products</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Product_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>CampaignSteps</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>CampaignStep_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>SerialLetters</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>SerialLetter_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Bulkmails</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>BulkMail_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Employees</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Employee_entity</entityName>
+        <fieldName>EmployeesByUserId</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Contacts</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>AnyContact_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityField>
+      <name>CONSUMER</name>
+      <valueProcess>%aditoprj%/entity/ObjectProxy_entity/entityfields/consumer/valueProcess.js</valueProcess>
+    </entityField>
+    <entityConsumer>
+      <name>DocumentTemplates</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>DocumentTemplate_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Attributes</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Attribute_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>Languages</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Language_entity</entityName>
+        <fieldName>ISO3Name</fieldName>
+      </dependency>
+    </entityConsumer>
+    <entityConsumer>
+      <name>SupportTickets</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>SupportTicket_entity</entityName>
+        <fieldName>#PROVIDER</fieldName>
+      </dependency>
+    </entityConsumer>
+  </entityFields>
+  <recordContainers>
+    <jDitoRecordContainer>
+      <name>jdito</name>
+      <jDitoRecordAlias>Data_alias</jDitoRecordAlias>
+      <isPageable v="true" />
+      <contentProcess>%aditoprj%/entity/ObjectProxy_entity/recordcontainers/jdito/contentProcess.js</contentProcess>
+      <rowCountProcess>%aditoprj%/entity/ObjectProxy_entity/recordcontainers/jdito/rowCountProcess.js</rowCountProcess>
+      <recordFieldMappings>
+        <jDitoRecordFieldMapping>
+          <name>UID.value</name>
+        </jDitoRecordFieldMapping>
+        <jDitoRecordFieldMapping>
+          <name>TITLE.value</name>
+        </jDitoRecordFieldMapping>
+      </recordFieldMappings>
+    </jDitoRecordContainer>
+  </recordContainers>
+</entity>
diff --git a/entity/ObjectProxy_entity/contentTitleProcess.js b/entity/ObjectProxy_entity/contentTitleProcess.js
new file mode 100644
index 0000000000..94f61859cc
--- /dev/null
+++ b/entity/ObjectProxy_entity/contentTitleProcess.js
@@ -0,0 +1,4 @@
+import("system.vars");
+import("system.result");
+
+result.string(vars.get("$field.TITLE"));
\ No newline at end of file
diff --git a/entity/ObjectProxy_entity/entityfields/consumer/valueProcess.js b/entity/ObjectProxy_entity/entityfields/consumer/valueProcess.js
new file mode 100644
index 0000000000..a946573db4
--- /dev/null
+++ b/entity/ObjectProxy_entity/entityfields/consumer/valueProcess.js
@@ -0,0 +1,37 @@
+import("system.neon");
+import("system.result");
+import("system.vars");
+
+var entity = vars.get("$param.Entity_param");
+var filter = vars.get("$param.Filter_param");
+
+var consumerMap = {
+    "DocumentTemplate_entity" : "DocumentTemplates",
+    "Organisation_entity" : "Organisations",
+    "Salesproject_entity" : "Salesprojects",
+    "SerialLetter_entity" : "SerialLetters",
+    "AnyContact_entity" : "Contacts",
+    "Attribute_entity" : "Attributes",
+    "Activity_entity" : "Activities",
+    "Contract_entity" : "Contracts",
+    "Campaign_entity" : "Campaigns",
+    "CampaignStep_entity" : "CampaignSteps",
+    "BulkMail_entity" : "Bulkmails",
+    "Employee_entity" : "Employees",
+    "Product_entity" : "Products",
+    "Person_entity" : "Persons",
+    "Offer_entity" : "Offers",
+    "Order_entity" : "Orders",
+    "Task_entity" : "Tasks",
+    "Language_entity" : "Languages",
+    "SupportTicket_entity" : "SupportTickets"
+};
+
+if (entity in consumerMap)
+{
+    let consumer = consumerMap[entity];
+    
+    if (filter)
+        neon.setFilter(consumer, filter);
+    result.string(consumer);
+}
diff --git a/entity/ObjectProxy_entity/entityfields/filteredobjects/targetConsumerProcess.js b/entity/ObjectProxy_entity/entityfields/filteredobjects/targetConsumerProcess.js
new file mode 100644
index 0000000000..d56fbfaaa3
--- /dev/null
+++ b/entity/ObjectProxy_entity/entityfields/filteredobjects/targetConsumerProcess.js
@@ -0,0 +1,5 @@
+import("system.result");
+import("system.vars");
+
+if (vars.get("$field.CONSUMER"))
+    result.string(vars.get("$field.CONSUMER"));
\ No newline at end of file
diff --git a/entity/ObjectProxy_entity/recordcontainers/jdito/contentProcess.js b/entity/ObjectProxy_entity/recordcontainers/jdito/contentProcess.js
new file mode 100644
index 0000000000..3cddbf3e5a
--- /dev/null
+++ b/entity/ObjectProxy_entity/recordcontainers/jdito/contentProcess.js
@@ -0,0 +1,38 @@
+import("system.result");
+import("system.vars");
+import("system.entities");
+
+var entity = vars.get("$param.Entity_param");
+var filter = vars.get("$param.Filter_param");
+var pageSize = vars.get("$local.pagesize");
+var startRow = vars.get("$local.startrow");
+var idvalues = vars.get("$local.idvalues");
+
+if (entity)
+{
+    var uidField = "#UID";
+    if (entity == "Employee_entity")
+        uidField = "SHORT_UID";
+    
+    var loadConfig = entities.createConfigForLoadingRows()
+        .entity(entity)
+        .fields([uidField, "#CONTENTTITLE"]);
+    
+    if (idvalues)
+        loadConfig.uids(idvalues)
+    else if (filter)
+        loadConfig.filter(filter);
+    if (pageSize != -1)
+    {
+        loadConfig.count(pageSize);
+        loadConfig.startrow(startRow);
+    }
+
+    var records = entities.getRows(loadConfig).map(function (row)
+    {
+        return [row[uidField], row["#CONTENTTITLE"]];
+    });
+    result.object(records);
+}
+else
+    result.object([]);
\ No newline at end of file
diff --git a/entity/ObjectProxy_entity/recordcontainers/jdito/rowCountProcess.js b/entity/ObjectProxy_entity/recordcontainers/jdito/rowCountProcess.js
new file mode 100644
index 0000000000..4263561d58
--- /dev/null
+++ b/entity/ObjectProxy_entity/recordcontainers/jdito/rowCountProcess.js
@@ -0,0 +1,19 @@
+import("system.result");
+import("system.vars");
+import("system.entities");
+
+var entity = vars.get("$param.Entity_param");
+var filter = vars.get("$param.Filter_param");
+
+if (entity)
+{
+    var loadConfig = entities.createConfigForLoadingRows()
+        .entity(entity);
+        
+    if (filter)
+        loadConfig.filter(filter);
+
+    result.object(entities.getRowCount(loadConfig));
+}
+else
+    result.object(0);
\ No newline at end of file
diff --git a/entity/Offer_entity/Offer_entity.aod b/entity/Offer_entity/Offer_entity.aod
index 1bf4f3eda2..6c0d7f4e85 100644
--- a/entity/Offer_entity/Offer_entity.aod
+++ b/entity/Offer_entity/Offer_entity.aod
@@ -24,6 +24,12 @@
           <fieldName>Offers</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>b34efdc3-f405-4fe6-b9c6-ed79f8f10155</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Offers</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/Order_entity/Order_entity.aod b/entity/Order_entity/Order_entity.aod
index f5d6ebfdfc..4d32515aa7 100644
--- a/entity/Order_entity/Order_entity.aod
+++ b/entity/Order_entity/Order_entity.aod
@@ -20,6 +20,12 @@
           <fieldName>Orders</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>683107cc-2908-4930-8581-d3636f9ec196</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Orders</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/Organisation_entity/Organisation_entity.aod b/entity/Organisation_entity/Organisation_entity.aod
index a903792d97..6714fd6c3b 100644
--- a/entity/Organisation_entity/Organisation_entity.aod
+++ b/entity/Organisation_entity/Organisation_entity.aod
@@ -252,6 +252,12 @@
           <fieldName>Organisations</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>6a11ffa0-a226-436a-9831-4dc02380e715</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Organisations</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityConsumer>
diff --git a/entity/Person_entity/Person_entity.aod b/entity/Person_entity/Person_entity.aod
index 5f05a2c1a3..0a325415b1 100644
--- a/entity/Person_entity/Person_entity.aod
+++ b/entity/Person_entity/Person_entity.aod
@@ -269,6 +269,12 @@ Usually this is used for filtering COMMUNICATION-entries by a specified contact
           <fieldName>Persons</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>a8f2893b-fce7-4a55-97d4-f19ec83f9bdb</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Persons</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/Product_entity/Product_entity.aod b/entity/Product_entity/Product_entity.aod
index 10f6b5d656..8765d36da4 100644
--- a/entity/Product_entity/Product_entity.aod
+++ b/entity/Product_entity/Product_entity.aod
@@ -166,6 +166,12 @@
           <fieldName>Products</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>6d0654ed-35f1-4de4-9cec-ad25f7ad43b6</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Products</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityConsumer>
diff --git a/entity/Salesproject_entity/Salesproject_entity.aod b/entity/Salesproject_entity/Salesproject_entity.aod
index 1cc6cb1fe7..8d57aaf605 100644
--- a/entity/Salesproject_entity/Salesproject_entity.aod
+++ b/entity/Salesproject_entity/Salesproject_entity.aod
@@ -25,6 +25,12 @@
           <fieldName>Salesprojects</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>b53c4fc0-4ac9-4020-a6fd-359c6dd2d2c8</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Salesprojects</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/SerialLetter_entity/SerialLetter_entity.aod b/entity/SerialLetter_entity/SerialLetter_entity.aod
index 0db3d7d3c1..0887853705 100644
--- a/entity/SerialLetter_entity/SerialLetter_entity.aod
+++ b/entity/SerialLetter_entity/SerialLetter_entity.aod
@@ -20,6 +20,12 @@
           <fieldName>SerialLetters</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>988dfd7a-dad6-440f-95d9-2288ba7de92e</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>SerialLetters</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
     </entityProvider>
     <entityField>
diff --git a/entity/SupportTicket_entity/SupportTicket_entity.aod b/entity/SupportTicket_entity/SupportTicket_entity.aod
index 519d9fb9e5..b97c94aa78 100644
--- a/entity/SupportTicket_entity/SupportTicket_entity.aod
+++ b/entity/SupportTicket_entity/SupportTicket_entity.aod
@@ -13,6 +13,14 @@
   <entityFields>
     <entityProvider>
       <name>#PROVIDER</name>
+      <dependencies>
+        <entityDependency>
+          <name>71f2720a-a7d1-422e-8dc6-4e60d586c812</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>SupportTickets</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
+      </dependencies>
     </entityProvider>
     <entityField>
       <name>CODE</name>
diff --git a/entity/Task_entity/Task_entity.aod b/entity/Task_entity/Task_entity.aod
index 35cd13a368..d3a46a6458 100644
--- a/entity/Task_entity/Task_entity.aod
+++ b/entity/Task_entity/Task_entity.aod
@@ -337,6 +337,12 @@
           <fieldName>Tasks</fieldName>
           <isConsumer v="false" />
         </entityDependency>
+        <entityDependency>
+          <name>0a5c7719-8f33-4647-a81d-02763c0df0a3</name>
+          <entityName>ObjectProxy_entity</entityName>
+          <fieldName>Tasks</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
       </dependencies>
       <children>
         <entityParameter>
diff --git a/neonContext/ObjectProxy/ObjectProxy.aod b/neonContext/ObjectProxy/ObjectProxy.aod
new file mode 100644
index 0000000000..4f06534bc1
--- /dev/null
+++ b/neonContext/ObjectProxy/ObjectProxy.aod
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1">
+  <name>ObjectProxy</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <entity>ObjectProxy_entity</entity>
+</neonContext>
diff --git a/neonView/AttributeRelationMultiEdit_view/AttributeRelationMultiEdit_view.aod b/neonView/AttributeRelationMultiEdit_view/AttributeRelationMultiEdit_view.aod
index 4671d3d834..1f1bfe35f5 100644
--- a/neonView/AttributeRelationMultiEdit_view/AttributeRelationMultiEdit_view.aod
+++ b/neonView/AttributeRelationMultiEdit_view/AttributeRelationMultiEdit_view.aod
@@ -20,6 +20,10 @@
           <name>ada9c77a-f37b-4021-a01a-a06edd75cb25</name>
           <entityField>VALUE</entityField>
         </neonTableColumn>
+        <neonTableColumn>
+          <name>197c3752-8d8d-4c46-90db-8399c4bce2e5</name>
+          <entityField>VALUE_LOOKUP</entityField>
+        </neonTableColumn>
       </columns>
     </genericMultipleViewTemplate>
   </children>
diff --git a/neonView/AttributeRelationTreeEdit_view/AttributeRelationTreeEdit_view.aod b/neonView/AttributeRelationTreeEdit_view/AttributeRelationTreeEdit_view.aod
index 1c73ccf35d..837818da7f 100644
--- a/neonView/AttributeRelationTreeEdit_view/AttributeRelationTreeEdit_view.aod
+++ b/neonView/AttributeRelationTreeEdit_view/AttributeRelationTreeEdit_view.aod
@@ -22,6 +22,10 @@
           <name>abec86c9-3d77-4129-b34b-d30da0526572</name>
           <entityField>VALUE</entityField>
         </entityFieldLink>
+        <entityFieldLink>
+          <name>7c82a0bd-a397-46ff-83a4-5fd4a0a08493</name>
+          <entityField>VALUE_LOOKUP</entityField>
+        </entityFieldLink>
       </fields>
     </genericViewTemplate>
   </children>
diff --git a/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod b/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod
index 5cb3318410..81b4e61dfb 100644
--- a/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod
+++ b/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod
@@ -28,7 +28,7 @@
     </genericViewTemplate>
     <neonViewReference>
       <name>25a498e5-5e01-4b34-88e5-c94189922c8a</name>
-      <entityField>Contacts</entityField>
+      <entityField>Contracts</entityField>
       <view>PersonMultiEdit_view</view>
     </neonViewReference>
     <neonViewReference>
diff --git a/process/Attribute_lib/process.js b/process/Attribute_lib/process.js
index fa5fee00de..a566e1359a 100644
--- a/process/Attribute_lib/process.js
+++ b/process/Attribute_lib/process.js
@@ -1,3 +1,4 @@
+import("system.logging");
 import("Employee_lib");
 import("KeywordData_lib");
 import("Context_lib");
@@ -60,44 +61,56 @@ AttributeUtil.getPossibleAttributes = function (pObjectType, pIncludeGroups, pFi
     if (pObjectType == null || (pFilteredAttributeIds && pFilteredAttributeIds.length == 0))
         return [];
     
-    var attrSql = "select AB_ATTRIBUTEID from AB_ATTRIBUTE"
-        + " join AB_ATTRIBUTEUSAGE  on AB_ATTRIBUTEID = AB_ATTRIBUTE_ID";
-    var attrCond = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
-        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
-        .and("ATTRIBUTE_ACTIVE = 1");
-    
+    var attrSelect = newSelect("AB_ATTRIBUTEID, ATTRIBUTE_PARENT_ID, ATTRIBUTE_TYPE")
+                        .from("AB_ATTRIBUTE")
+                        .join("AB_ATTRIBUTEUSAGE", "AB_ATTRIBUTEID = AB_ATTRIBUTE_ID")
+                        .where("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
+                        .and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
+                        .and("ATTRIBUTE_ACTIVE = 1");
+
     if (pAttributeCount)
     {
         for (let attributeId in pAttributeCount)
         {
-            attrCond.andSqlCondition(
-                SqlCondition.begin()
-                    .orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", attributeId, "# != ?")
-                    .orPrepare("AB_ATTRIBUTEUSAGE.MAX_COUNT", pAttributeCount[attributeId], "# > ?")
-                    .or("AB_ATTRIBUTEUSAGE.MAX_COUNT is null")
+            attrSelect.and(newWhere()
+                            .or("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", attributeId, "# != ?")
+                            .or("AB_ATTRIBUTEUSAGE.MAX_COUNT", pAttributeCount[attributeId], "# > ?")
+                            .or("AB_ATTRIBUTEUSAGE.MAX_COUNT is null")
             );
         }
     }
         
     if (pFilteredAttributeIds)
     {
-        var filteredIdsCondition = new SqlCondition();
         var filteredIdChildren = AttributeUtil.getAllChildren(pFilteredAttributeIds);
-        pFilteredAttributeIds.concat(filteredIdChildren).forEach(function(id) 
-        {
-            this.orPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", id);
-        }, filteredIdsCondition);
-
-        attrCond.andSqlCondition(filteredIdsCondition);
+        var allFilteredIds = pFilteredAttributeIds.concat(filteredIdChildren);
+        
+        attrSelect.andIfSet("AB_ATTRIBUTE.AB_ATTRIBUTEID", allFilteredIds, SqlBuilder.IN())
     }
 
     if (!pIncludeGroups)
-        attrCond.andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.GROUP, "# != ?");
-        
-    var attributes = db.array(db.COLUMN, attrCond.buildSql(attrSql));
+        attrSelect.and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.GROUP, "# != ?");
+    
+    var attributes = attrSelect.table();
+    
+    //filter out groups without usable children
+    if (pIncludeGroups && pAttributeCount)
+    {
+        var parentIds = {};
+        attributes.forEach(function (attribute)
+        {
+            this[attribute[1]] = true;
+        }, parentIds);
+        attributes = attributes.filter(function (attribute)
+        {
+            return attribute[2].trim() != $AttributeTypes.GROUP || this[attribute[0]];
+        }, parentIds);
+    }
     
-    return attributes;
+    return attributes.map(function (attribute)
+    {
+        return attribute[0];
+    });
 }
 
 /**
@@ -119,15 +132,17 @@ AttributeUtil.getPossibleListValues = function (pAttributeId, pAttributeType, pI
     var onlyActives = (pIncludeInactives == undefined ? false : pIncludeInactives);
     if (attrType == $AttributeTypes.COMBO.toString())
     {
-        var valueSql = SqlCondition.begin()
-            .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", attributeId)
-            .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE);
-        
+        var valuesSelect = newSelect("AB_ATTRIBUTEID, ATTRIBUTE_NAME")
+                                .from("AB_ATTRIBUTE")
+                                .where("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", attributeId)
+                                .and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE);
+                                
         if (onlyActives)
-            valueSql.andPrepare("AB_ATTRIBUTE.ATTRIBUTE_ACTIVE", "1");
+            valuesSelect.and("AB_ATTRIBUTE.ATTRIBUTE_ACTIVE", "1");
         
-        valueSql = valueSql.buildSql("select AB_ATTRIBUTEID, ATTRIBUTE_NAME from AB_ATTRIBUTE", "1=2", "order by SORTING asc");
-        var valueList = db.table(valueSql);
+        var valueList = valuesSelect.orderBy("SORTING asc")
+                                    .table();
+            
         for (let i = 0; i < valueList.length; i++)
         {
             valueList[i][1] = translate.text(valueList[i][1]);
@@ -143,20 +158,20 @@ AttributeUtil.getPossibleListValues = function (pAttributeId, pAttributeType, pI
     }
     else if (attrType == $AttributeTypes.KEYWORD.toString())
     {
-        var attrKeywordSelect = "select DROPDOWNDEFINITION from AB_ATTRIBUTE";
-        attrKeywordSelect = SqlCondition.begin()
-            .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", attributeId)
-            .buildSql(attrKeywordSelect);
-        var attrKeyword = db.cell(attrKeywordSelect);
+        var attrKeyword = newSelect("DROPDOWNDEFINITION")
+                                .from("AB_ATTRIBUTE")
+                                .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", attributeId)
+                                .cell();
+            
         var keywords = KeywordData.getSimpleData(attrKeyword, null, onlyActives);
         return keywords;
     }
     else if (attrType == $AttributeTypes.OBJECTSELECTION)
     {
-        var [module, filter] = db.array(db.ROW, SqlCondition.begin()
-            .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", attributeId)
-            .buildSql("select DROPDOWNDEFINITION, DROPDOWNFILTER from AB_ATTRIBUTE")
-            );
+        var [module, filter] = newSelect("DROPDOWNDEFINITION, DROPDOWNFILTER")
+                                    .from("AB_ATTRIBUTE")
+                                    .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", attributeId)
+                                    .arrayRow();
         var objects = [];
         if (module)
         {
@@ -207,13 +222,13 @@ AttributeUtil.getFullAttributeName = function (pAttributeId, pSimpleName, pTrans
     var attributeNames = [];
     var attribute;
     do {
-        attribute = db.array(db.ROW, SqlCondition.begin()
-            .andPrepare(["AB_ATTRIBUTE", "AB_ATTRIBUTEID", "ATTRIBUTE"], pAttributeId)
-            .buildSql("select ATTRIBUTE.ATTRIBUTE_NAME, PARENT1.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_PARENT_ID \n\
-                from AB_ATTRIBUTE ATTRIBUTE \n\
-                left join AB_ATTRIBUTE PARENT1 on ATTRIBUTE.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID \n\
-                left join AB_ATTRIBUTE PARENT2 on PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID")
-        );
+        attribute = newSelect("ATTRIBUTE.ATTRIBUTE_NAME, PARENT1.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_PARENT_ID")
+                        .from("AB_ATTRIBUTE", "ATTRIBUTE")
+                        .leftJoin("AB_ATTRIBUTE", "ATTRIBUTE.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID", "PARENT1")
+                        .leftJoin("AB_ATTRIBUTE", "PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID", "PARENT2")
+                        .where(["AB_ATTRIBUTE", "AB_ATTRIBUTEID", "ATTRIBUTE"], pAttributeId)
+                        .arrayRow();
+
         if (attribute.length > 0)
         {
             attributeNames.push(attribute[0]);
@@ -247,10 +262,10 @@ AttributeUtil.getFullAttributeName = function (pAttributeId, pSimpleName, pTrans
  */
 AttributeUtil.getSimpleAttributeName = function (pAttributeId, pTranslate) 
 {
-    var attributeName = db.cell(SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
-        .buildSql("select ATTRIBUTE_NAME from AB_ATTRIBUTE")
-    );
+    var attributeName = newSelect("ATTRIBUTE_NAME")
+                            .from("AB_ATTRIBUTE")
+                            .whereIfSet("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
+                            .cell(true, "");
     if (pTranslate)
         attributeName = translate.text(attributeName);
     return attributeName;
@@ -271,10 +286,11 @@ AttributeUtil.getAllChildren = function (pAttributeIds)
         
     while (pAttributeIds.length > 0)
     {
-        pAttributeIds = db.array(db.COLUMN, SqlCondition.begin()
-            .andIn("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeIds)
-            .buildSql("select AB_ATTRIBUTEID from AB_ATTRIBUTE")
-        );
+        pAttributeIds = newSelect("AB_ATTRIBUTEID")
+                            .from("AB_ATTRIBUTE")
+                            .where("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeIds, SqlBuilder.IN())
+                            .arrayColumn();
+
         if (pAttributeIds.length > 0)
             childIds = childIds.concat(pAttributeIds);
     }
@@ -292,12 +308,10 @@ AttributeUtil.hasRelations = function (pAttributeId)
 {
     if (!pAttributeId)
         return false;
-    return db.cell(SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", pAttributeId)
-        .buildSql(
-            "select count(*) from AB_ATTRIBUTERELATION", "1=2" //TODO: is there a way exists could be used?
-        )
-    ) != "0";
+    return newSelect("count(*)")
+                .from("AB_ATTRIBUTERELATION")
+                .where("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", pAttributeId)
+                .cell() != "0"; //TODO: is there a way exists could be used?
 }
 
 /**
@@ -311,27 +325,26 @@ AttributeUtil.getAttributeType = function (pAttributeId)
 {
     if (!pAttributeId)
         return "";
-    var attrTypeSelect = "select ATTRIBUTE_TYPE from AB_ATTRIBUTE";
-    attrTypeSelect = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
-        .buildSql(attrTypeSelect);
-    return db.cell(attrTypeSelect).trim();
+
+    return newSelect("ATTRIBUTE_TYPE")
+                .from("AB_ATTRIBUTE")
+                .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
+                .cell()
+                .trim();
 }
 
 AttributeUtil.hasAttributes = function (pObjectType)
 {
     if (!pObjectType)
         return false;
-    return db.cell(SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
-        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_ACTIVE", "1")
-        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
-        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.GROUP, "# != ?")
-        .buildSql(
-            "select count(*) from AB_ATTRIBUTEUSAGE \n\
-            join AB_ATTRIBUTE on AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID", "1=2"
-        )
-    ) != "0"; //TODO: is there a way exists could be used?
+    return newSelect("count(*)")
+                .from("AB_ATTRIBUTEUSAGE")
+                .join("AB_ATTRIBUTE", "AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+                .where("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
+                .and("AB_ATTRIBUTE.ATTRIBUTE_ACTIVE", "1")
+                .and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
+                .and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.GROUP, "# != ?")
+                .cell() != "0"; //TODO: is there a way exists could be used?
 }
 
 /*********************************************************************************************************************/
@@ -357,12 +370,6 @@ function AttributeRelationUtils () {}
  */
 AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObjectType, pGetViewValue, pGetAttrname)
 {
-    var attrCond = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTERELATION.OBJECT_ROWID", pObjectRowId)
-        .andPrepare("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", pAttributeId);
-    if (pObjectType != null)
-        attrCond.andPrepare("AB_ATTRIBUTERELATION.OBJECT_TYPE", pObjectType);
-    
     var defaultFields = [
         "AB_ATTRIBUTE.ATTRIBUTE_TYPE", 
         "AB_ATTRIBUTE.DROPDOWNDEFINITION", 
@@ -373,11 +380,11 @@ AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObj
         defaultFields.push("AB_ATTRIBUTE.ATTRIBUTE_NAME");    
     
     var valueFields = AttributeTypeUtil.getAllDatabaseFields();
-    var attributeSql = attrCond.buildSql("select " + defaultFields.join(", ") + ", " + valueFields.join(", ")
-        + " from AB_ATTRIBUTERELATION join AB_ATTRIBUTE on AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID"
-        + " left join AB_ATTRIBUTE COMBOVAL on " + $AttributeTypes.COMBO.databaseField + " = COMBOVAL.AB_ATTRIBUTEID");
-    
-    var attributeValues = db.array(db.ROW, attributeSql);
+
+    var attributeValues = AttributeRelationUtils.getAttributeSqlBuilder(defaultFields.concat(valueFields), pObjectRowId, pObjectType)
+                                                .and("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", pAttributeId)
+                                                .arrayRow();
+        
     if (!attributeValues.length)
         return null;
     
@@ -393,6 +400,24 @@ AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObj
     return value;
 }
 
+/**
+ * Get a SqlBuilder already containing the full select for attributes.
+ * @param {String[]} pFields array of all fields which should be selected
+ * @param {String} pObjectRowId object rowid
+ * @param {String} [pObjectType=null] object-type
+ * 
+ * @return {SqlBuilder} a already filled SqlBuilder
+ */
+AttributeRelationUtils.getAttributeSqlBuilder = function (pFields, pObjectRowId, pObjectType)
+{
+    return newSelect(pFields)
+                .from("AB_ATTRIBUTERELATION")
+                .join("AB_ATTRIBUTE", "AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+                .leftJoin("AB_ATTRIBUTE COMBOVAL", $AttributeTypes.COMBO.databaseField + " = COMBOVAL.AB_ATTRIBUTEID")
+                .where("AB_ATTRIBUTERELATION.OBJECT_ROWID", pObjectRowId)
+                .andIfSet("AB_ATTRIBUTERELATION.OBJECT_TYPE", pObjectType);
+}
+
 /**
  * gets all attributes for a dataset
  * 
@@ -406,12 +431,7 @@ AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObj
  * @return {String[][]} two-dimensional array a row is [attributeId|attributeName, value] (or [attributeId, attributeName, value])
  */
 AttributeRelationUtils.getAllAttributes = function (pObjectRowId, pObjectType, pUseAttributeIds, pUseIdValues)
-{
-    var attrCond = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTERELATION.OBJECT_ROWID", pObjectRowId);
-    if (pObjectType != null)
-        attrCond.andPrepare("AB_ATTRIBUTERELATION.OBJECT_TYPE", pObjectType);
-    
+{    
     var defaultFields = [
         "AB_ATTRIBUTE_ID", 
         "AB_ATTRIBUTE.ATTRIBUTE_TYPE", 
@@ -419,12 +439,13 @@ AttributeRelationUtils.getAllAttributes = function (pObjectRowId, pObjectType, p
         "COMBOVAL.ATTRIBUTE_NAME"
     ];
     var valueFields = AttributeTypeUtil.getAllDatabaseFields();
-    var attributeSql = attrCond.buildSql("select " + defaultFields.join(", ") + ", " + valueFields.join(", ")
-        + " from AB_ATTRIBUTERELATION join AB_ATTRIBUTE on AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID"
-        + " left join AB_ATTRIBUTE COMBOVAL on " + $AttributeTypes.COMBO.databaseField + " = COMBOVAL.AB_ATTRIBUTEID");
     
     var attributeNameMap = {};
-    var attributeValues = db.table(attributeSql).map(function (row) 
+    
+    var attributeValues = AttributeRelationUtils.getAttributeSqlBuilder(defaultFields.concat(valueFields), pObjectRowId, pObjectType)
+                                                .arrayRow();
+    
+    attributeValues = attributeValues.map(function (row) 
     {
         let attribute = row[0];
         let attrname;
@@ -475,11 +496,11 @@ AttributeRelationUtils.getAllAttributes = function (pObjectRowId, pObjectType, p
  */
 AttributeRelationUtils.selectAttributeValue = function (pAttributeId, pValueMap, pGetViewValue)
 {
-    var sqlSelect = "select ATTRIBUTE_TYPE, DROPDOWNDEFINITION from AB_ATTRIBUTE";
-    var type = db.array(db.ROW, SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
-        .buildSql(sqlSelect)
-    );
+    var type = newSelect("ATTRIBUTE_TYPE, DROPDOWNDEFINITION")
+                    .from("AB_ATTRIBUTE")
+                    .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
+                    .arrayRow();
+                    
     if (!type.length)
         return null;
     
@@ -488,10 +509,10 @@ AttributeRelationUtils.selectAttributeValue = function (pAttributeId, pValueMap,
     var value = pValueMap[field];
     if (pGetViewValue && type[0] == $AttributeTypes.COMBO)
     {
-        value = db.cell(SqlCondition.begin()
-            .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", value)
-            .buildSql("select ATTRIBUTE_NAME from AB_ATTRIBUTE")
-        );
+        value = newSelect("ATTRIBUTE_NAME")
+                    .from("AB_ATTRIBUTE")
+                    .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", value)
+                    .cell();
     }
     else if (pGetViewValue)
         value = AttributeTypeUtil.getAttributeViewValue(type[0], value, type[1]);
@@ -537,28 +558,29 @@ AttributeRelationUtils.insertAttribute = function (pRowId, pObjectType, pAttribu
  * 
  * @param {String} pObjectType the object type
  * @param {String} pConsumer the name of the attribute relation consumer
+ * @param {String[]} pFiltered array of attributeId's which act as a whitelist. (groups are resolves to the childid's)
  */
 AttributeRelationUtils.presetMandatoryAttributes = function (pObjectType, pConsumer, pFiltered)
 {
-    var mandatoryAttributesCondition = SqlCondition.begin()
-                                        .andPrepare("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
-                                        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# <> ?")
-                                        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.GROUP, "# <> ?")
-                                        .and("ATTRIBUTE_ACTIVE = 1")
-                                        .and("MIN_COUNT > 0")
-    
+    var mandatoryAttributesSelect = newSelect("AB_ATTRIBUTE_ID, MIN_COUNT")
+                                    .from("AB_ATTRIBUTEUSAGE")
+                                    .join("AB_ATTRIBUTE", "AB_ATTRIBUTE_ID = AB_ATTRIBUTEID")
+                                    .where("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
+                                    .and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# <> ?")
+                                    .and("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.GROUP, "# <> ?")
+                                    .and("ATTRIBUTE_ACTIVE = 1")
+                                    .and("MIN_COUNT > 0");
+                                    
     if (pFiltered)
     {
         var possibleIds = AttributeUtil.getPossibleAttributes(pObjectType, false, pFiltered);
         if (possibleIds.length > 0)
-            mandatoryAttributesCondition.andIn("AB_ATTRIBUTE.AB_ATTRIBUTEID", possibleIds)
+            mandatoryAttributesSelect.and("AB_ATTRIBUTE.AB_ATTRIBUTEID", possibleIds, SqlBuilder.IN())
         else
             return;
     }
-    
-    var mandatoryAttributes = db.table(mandatoryAttributesCondition.buildSql("select AB_ATTRIBUTE_ID, MIN_COUNT from AB_ATTRIBUTEUSAGE "
-                + "join AB_ATTRIBUTE on AB_ATTRIBUTE_ID = AB_ATTRIBUTEID"));
-    
+
+    var mandatoryAttributes = mandatoryAttributesSelect.table();
     mandatoryAttributes.forEach(function (usage)
     {
         //adding an attribute more than 20 times would be too much (having a min_count > 20 is very unlikely)
@@ -631,21 +653,21 @@ AttributeRelationUtils.validateAttributeCount = function (pRowId, pObjectType, p
             this[row.AB_ATTRIBUTE_ID] = (this[row.AB_ATTRIBUTE_ID] || 0) + 1;
         }, countObj);
     }
-    var attributeCondition = SqlCondition.begin();
-    AttributeUtil.getPossibleAttributes(pObjectType, undefined, pFilteredAttributeIds).forEach(function (attributeId)
-    {
-        this.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", attributeId);
-    }, attributeCondition);
     
-    var usageCondition = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
-        .andSqlCondition(attributeCondition, "1=2");
+    var possibleAttributes = AttributeUtil.getPossibleAttributes(pObjectType, undefined, pFilteredAttributeIds);
+    var minMaxCounts = [];
     
-    //retrieve all min/max counts of the possible attributes
-    var minMaxCounts = db.table(usageCondition.buildSql(
-          "select AB_ATTRIBUTEID, ATTRIBUTE_NAME, MIN_COUNT, MAX_COUNT from AB_ATTRIBUTEUSAGE \
-           join AB_ATTRIBUTE on AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID", "1=2"
-    ));
+    if (possibleAttributes.length > 0)
+    {
+        var minMaxCountsSelect = newSelect("AB_ATTRIBUTEID, ATTRIBUTE_NAME, MIN_COUNT, MAX_COUNT")
+                                    .from("AB_ATTRIBUTEUSAGE")
+                                    .join("AB_ATTRIBUTE", "AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+                                    .where("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", possibleAttributes, SqlBuilder.IN())
+                                    .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
+
+        //retrieve all min/max counts of the possible attributes
+        minMaxCounts = minMaxCountsSelect.table();
+    }
         
     var validationMessage = [];
     minMaxCounts.forEach(function ([attributeId, name, minCount, maxCount])
@@ -689,15 +711,12 @@ AttributeRelationUtils.countAttributeRelations = function (pRowId, pObjectType,
     //unchanged (already stored) row ==> increase count 
     var countObj = {};
 
-    var condition = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTERELATION.OBJECT_ROWID", pRowId)
-        .andPrepareIfSet("AB_ATTRIBUTERELATION.OBJECT_TYPE", pObjectType);
-
-    var storedAttributeRelations = db.table(condition.buildSql(
-        "select AB_ATTRIBUTERELATIONID, AB_ATTRIBUTE_ID from AB_ATTRIBUTERELATION",
-        "1=2"
-    ));
-
+    var storedAttributeRelations = newSelect("AB_ATTRIBUTERELATIONID, AB_ATTRIBUTE_ID")
+                                        .from("AB_ATTRIBUTERELATION")
+                                        .where("AB_ATTRIBUTERELATION.OBJECT_ROWID", pRowId)
+                                        .andIfSet("AB_ATTRIBUTERELATION.OBJECT_TYPE", pObjectType)
+                                        .table();
+                        
     storedAttributeRelations.forEach(function ([storedAttrRelationId, storedAttributeId]) {
         var currentAttributeId = storedAttributeId;
         //merging the data that is stored in the DB and the provided changes
@@ -753,6 +772,7 @@ AttributeRelationUtils.countAttributeRelations = function (pRowId, pObjectType,
  * isGroup = if true, the attribute can have children
  * getDropDownDefinitions = function that returns an array of possible values
  *          for DROPDOWNDEFINITION
+ * singleSelection = if true, the maximal usage count is always 1
  * 
  * The display name is controlled by the keyword 'AttributeType'
  */
@@ -781,6 +801,7 @@ $AttributeTypes.BOOLEAN = {
     toString : function () {return "BOOLEAN";},
     contentType : "BOOLEAN", 
     databaseField : "INT_VALUE",
+    singleSelection : true,
     getViewValue : function (pValue)
         {
             return pValue == "1" ? translate.text("Yes") : translate.text("No");
@@ -825,7 +846,8 @@ $AttributeTypes.VOID = {
     contentType : null,
     databaseField : null,
     isGroup : true,
-    possibleChildren : ["VOID"]
+    possibleChildren : ["VOID"],
+    singleSelection : true
 };
 $AttributeTypes.MEMO = { 
     toString : function () {return "MEMO";},
@@ -911,14 +933,7 @@ function AttributeTypeUtil () {}
  */
 AttributeTypeUtil.getContentType = function (pAttributeType)
 {
-    if (pAttributeType)
-    {
-        pAttributeType = pAttributeType.trim();
-    
-        if (pAttributeType in $AttributeTypes)
-            return $AttributeTypes[pAttributeType].contentType;
-    }
-    return null;
+    return AttributeTypeUtil._getProperty(pAttributeType, "contentType");
 }
 
 /**
@@ -930,13 +945,7 @@ AttributeTypeUtil.getContentType = function (pAttributeType)
  */
 AttributeTypeUtil.isGroupType = function (pAttributeType)
 {
-    if (pAttributeType)
-    {
-        pAttributeType = pAttributeType.trim();
-        if (pAttributeType in $AttributeTypes)
-            return $AttributeTypes[pAttributeType].isGroup || false;
-    }
-    return null;
+    return AttributeTypeUtil._getProperty(pAttributeType, "isGroup", false);
 }
 
 /**
@@ -948,13 +957,7 @@ AttributeTypeUtil.isGroupType = function (pAttributeType)
  */
 AttributeTypeUtil.getDatabaseField = function (pAttributeType)
 {
-    if (pAttributeType)
-    {
-        pAttributeType = pAttributeType.trim();
-        if (pAttributeType in $AttributeTypes)
-            return $AttributeTypes[pAttributeType].databaseField;
-    }
-    return null;
+    return AttributeTypeUtil._getProperty(pAttributeType, "databaseField");
 }
 
 /**
@@ -966,12 +969,41 @@ AttributeTypeUtil.getDatabaseField = function (pAttributeType)
  */
 AttributeTypeUtil.getPossibleChildren = function (pAttributeType)
 {
-    if (pAttributeType)
-    {
-        pAttributeType = pAttributeType.trim();
-        if (pAttributeType in $AttributeTypes)
-            return $AttributeTypes[pAttributeType].possibleChildren;
-    }
+    return AttributeTypeUtil._getProperty(pAttributeType, "possibleChildren");
+}
+
+/**
+ * returns the possible children types for the given attribute type
+ * 
+ * @param {String} pAttributeType the attribute type 
+ *                  (use the values of the AttributeTypes object, e. g. AttributeTypes.TEXT)
+ * @return {String[]} the possible children types
+ */
+AttributeTypeUtil.isSingleSelection = function (pAttributeType)
+{
+    return AttributeTypeUtil._getProperty(pAttributeType, "singleSelection", false);
+}
+
+AttributeTypeUtil.useLookup = function (pAttributeType)
+{
+    return pAttributeType.trim() == $AttributeTypes.OBJECTSELECTION.toString();
+}
+
+/**
+ * function to get a property of an attribute type
+ */
+AttributeTypeUtil._getProperty = function (pAttributeType, pPropertyName, pDefaultValue)
+{
+    if (!pAttributeType)
+        return null;
+    
+    pAttributeType = pAttributeType.trim();
+    if (pAttributeType in $AttributeTypes)
+        if (pPropertyName in $AttributeTypes[pAttributeType])
+            return $AttributeTypes[pAttributeType][pPropertyName];
+        else
+            return pDefaultValue === undefined ? null : pDefaultValue;
+        
     return null;
 }
 
@@ -1042,26 +1074,27 @@ AttributeUsageUtil.insertChildrenUsages = function (pAttributeId, pObjectType)
     var columns = ["AB_ATTRIBUTEUSAGEID", "AB_ATTRIBUTE_ID", "OBJECT_TYPE", "MAX_COUNT"];
     var types = db.getColumnTypes(table, columns);
     
-    var sqlSelect = "select AB_ATTRIBUTEID, ATTRIBUTE_TYPE, "
-            + " (select count(*) from AB_ATTRIBUTEUSAGE where AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID and OBJECT_TYPE = '" 
-            + pObjectType + "') = 0 from AB_ATTRIBUTE";
-    
     var inserts = [];
     _addInserts(pAttributeId, pObjectType);
     db.inserts(inserts);
     
     function _addInserts (pAttributeId, pObjectType)
     {
-        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 attributes = newSelect(["AB_ATTRIBUTEID", "ATTRIBUTE_TYPE",
+                                newSelect("count(*) = 0")
+                                    .from("AB_ATTRIBUTEUSAGE")
+                                    .where("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+                                    .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)])
+                            .from("AB_ATTRIBUTE")
+                            .where("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
+                            .and("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeId)
+                            .table();
+
         attributes.forEach(function (row)
         {
             if (row[2] == "true")
             {
-                let maxCount = row[1].trim() == $AttributeTypes.BOOLEAN || row[1].trim() == $AttributeTypes.VOID
+                let maxCount = AttributeTypeUtil.isSingleSelection(row[1])
                     ? "1"
                     : "";
                 let values = [util.getNewUUID(), row[0], pObjectType, maxCount];
@@ -1082,50 +1115,47 @@ AttributeUsageUtil.insertChildrenUsages = function (pAttributeId, pObjectType)
  */
 AttributeUsageUtil.updateChildrenUsages = function (pAttributeId, pOldObjectType, pNewObjectType)
 {
-    if (!pNewObjectType || !pAttributeId || !pOldObjectType)
+    if (!pNewObjectType || !pAttributeId)
         return;
     
     var table = "AB_ATTRIBUTEUSAGE";
     
-    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 countSubQuery = newSelect("count(*)")
+                            .from("AB_ATTRIBUTEUSAGE")
+                            .where("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pNewObjectType)
+                            .and("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID");
     
-    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 sqlSelect = newSelect(["AB_ATTRIBUTEID", "AB_ATTRIBUTEUSAGEID", countSubQuery])
+                        .from("AB_ATTRIBUTE")
+                        .leftJoin("AB_ATTRIBUTEUSAGE", newWhere()
+                                .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pOldObjectType)
+                                .and("AB_ATTRIBUTEID = AB_ATTRIBUTE_ID"));
     
-    var updateCond = SqlCondition.begin();
+    var updateCond = newWhere();
     
     //it is possible that the new objectType is already in a subordinate attribute 
     //and an update could cause a duplicate entry so one has to be deleted
-    var deleteCond = SqlCondition.begin();
+    var deleteCond = newWhere();
     
     _addUpdateIds(pAttributeId, pOldObjectType);
         
-    if (updateCond.isSet())
-        db.updateData(table, ["OBJECT_TYPE"], null, [pNewObjectType], updateCond.build("1=2"));
-    if (deleteCond.isSet())
-        db.deleteData(table, deleteCond.build("1=2"));
+    updateCond.updateData(true, table, ["OBJECT_TYPE"], null, [pNewObjectType]);
+    deleteCond.deleteData(true, table);
     
     function _addUpdateIds (pAttributeId)
     {
-        var condition = SqlCondition.begin()
-            .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
-            .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeId);
-        var query = sqlSelect.clearWhere().where(condition);
-        var attributes = db.table(query.build());
+        sqlSelect.clearWhere()
+                 .where("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
+                 .and("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeId);
+
+        var attributes = sqlSelect.table();
         
         attributes.forEach(function (row)
         {
             if (row[1] && row[2] != "0")
-                deleteCond.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1]);
+                deleteCond.or("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1]);
             else if (row[1])
-                updateCond.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1]);
+                updateCond.or("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1]);
             _addUpdateIds(row[0]);
         });
     }
@@ -1140,27 +1170,27 @@ AttributeUsageUtil.updateChildrenUsages = function (pAttributeId, pOldObjectType
  */
 AttributeUsageUtil.deleteChildrenUsages = function (pAttributeId, pObjectType)
 {
-    var table = "AB_ATTRIBUTEUSAGE";
+    var attributeSelect = newSelect("AB_ATTRIBUTEID, AB_ATTRIBUTEUSAGEID")
+                            .from("AB_ATTRIBUTE")
+                            .leftJoin("AB_ATTRIBUTEUSAGE", newWhere("AB_ATTRIBUTEID = AB_ATTRIBUTE_ID")
+                                                               .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType));
     
-    var sqlSelect = "select AB_ATTRIBUTEID, AB_ATTRIBUTEUSAGEID "
-        + " from AB_ATTRIBUTE left join AB_ATTRIBUTEUSAGE on AB_ATTRIBUTEID = AB_ATTRIBUTE_ID and OBJECT_TYPE = '" + pObjectType + "'";
-    
-    var deleteCond = SqlCondition.begin();
+    var deleteCond = newWhere().from("AB_ATTRIBUTEUSAGE");
     _addDeleteIds(pAttributeId, pObjectType);
-    if (deleteCond.isSet())
-        db.deleteData(table, deleteCond.build("1=2"));
+    
+    deleteCond.deleteData();
     
     function _addDeleteIds (pAttributeId)
     {
-        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));
+        attributeSelect.clearWhere()
+                       .where("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE, "# != ?")
+                       .and("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", pAttributeId);
+        var attributes = attributeSelect.table();
         
         attributes.forEach(function (row)
         {
             if (row[1])
-                deleteCond.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1])
+                deleteCond.or("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[1])
             _addDeleteIds(row[0]);
         });
     }
@@ -1173,24 +1203,31 @@ AttributeUsageUtil.deleteChildrenUsages = function (pAttributeId, pObjectType)
  */
 AttributeUsageUtil.removeDuplicates = function (pAttributeId)
 {
-    var condition = SqlCondition.begin()
-        .and("exists (select AB_ATTRIBUTEUSAGEID from AB_ATTRIBUTEUSAGE AU where AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AU.AB_ATTRIBUTE_ID "
-            + "and AB_ATTRIBUTEUSAGE.OBJECT_TYPE = AU.OBJECT_TYPE and AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID != AU.AB_ATTRIBUTEUSAGEID)");
-    if (pAttributeId)
-        condition.andPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", pAttributeId);
     
-    var duplicates = db.table(condition.buildSql("select AB_ATTRIBUTEUSAGEID, AB_ATTRIBUTE_ID, OBJECT_TYPE from AB_ATTRIBUTEUSAGE"));
+    
+    var attributeSelect = newSelect("AB_ATTRIBUTEUSAGEID, AB_ATTRIBUTE_ID, OBJECT_TYPE")
+                                .from("AB_ATTRIBUTEUSAGE")
+                                .where(null, newSelect("AB_ATTRIBUTEUSAGEID")
+                                                .from("AB_ATTRIBUTEUSAGE", "USAGEDUP")
+                                                .where("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = USAGEDUP.AB_ATTRIBUTE_ID")
+                                                .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE = USAGEDUP.OBJECT_TYPE")
+                                                .and("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID != USAGEDUP.AB_ATTRIBUTEUSAGEID"), 
+                                        SqlBuilder.EXISTS());
+                                        
+    attributeSelect.andIfSet("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", pAttributeId);
+    
+    var duplicates = attributeSelect.table();
     var usageObj = {};
-    var deleteCond = SqlCondition.begin();
+    var deleteCond = newWhere().from("AB_ATTRIBUTEUSAGE");
     
     duplicates.forEach(function (row)
     {
         if (!(row[1] in this))
             this[row[1]] = {};
         if (row[2] in this[row[1]])
-            deleteCond.orPrepare("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[0]);
+            deleteCond.or("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTEUSAGEID", row[0]);
         this[row[1]][row[2]] = true;
     }, usageObj);
-    if (deleteCond.isSet())
-        db.deleteData("AB_ATTRIBUTEUSAGE", deleteCond.build("1=2"));
+    
+    deleteCond.deleteData();
 }
\ No newline at end of file
-- 
GitLab