From 2c4d3967f31a8d1faa986084f3f9acc567cf70c0 Mon Sep 17 00:00:00 2001
From: "S.Listl" <S.Listl@SLISTL.aditosoftware.local>
Date: Wed, 17 Apr 2019 10:07:19 +0200
Subject: [PATCH] Attribute: editing AttributeRelations in tree

---
 .../AttributeRelationTree_entity.aod          |  20 +-
 .../ab_attribute_id/onValueChange.js          |   8 +
 .../entityfields/value/contentTypeProcess.js  |   6 +
 .../value/possibleItemsProcess.js             |  46 +++++
 .../recordcontainers/jdito/contentProcess.js  |  37 +++-
 .../recordcontainers/jdito/onDelete.js        |  10 +
 .../recordcontainers/jdito/onInsert.js        |  31 +++
 .../recordcontainers/jdito/onUpdate.js        |  25 +++
 .../valueproxy/contentTypeProcess.js          |   4 +-
 .../valueproxy/displayValueProcess.js         |   2 +-
 .../entityfields/valueproxy/onValueChange.js  |  29 ++-
 .../valueproxy/possibleItemsProcess.js        |   2 +-
 .../entityfields/valueproxy/valueProcess.js   |  35 +++-
 .../attribute_type/displayValueProcess.js     |   2 +-
 .../attribute_type/valueProcess.js            |   2 +-
 .../AttributeRelationTree.aod                 |   1 +
 .../AttributeRelationTree_view.aod            |  19 +-
 process/Attribute_lib/process.js              | 184 +++++++-----------
 process/Organisation_lib/process.js           |   2 +-
 19 files changed, 315 insertions(+), 150 deletions(-)
 create mode 100644 entity/AttributeRelationTree_entity/entityfields/ab_attribute_id/onValueChange.js
 create mode 100644 entity/AttributeRelationTree_entity/entityfields/value/contentTypeProcess.js
 create mode 100644 entity/AttributeRelationTree_entity/entityfields/value/possibleItemsProcess.js
 create mode 100644 entity/AttributeRelationTree_entity/recordcontainers/jdito/onDelete.js
 create mode 100644 entity/AttributeRelationTree_entity/recordcontainers/jdito/onInsert.js
 create mode 100644 entity/AttributeRelationTree_entity/recordcontainers/jdito/onUpdate.js

diff --git a/entity/AttributeRelationTree_entity/AttributeRelationTree_entity.aod b/entity/AttributeRelationTree_entity/AttributeRelationTree_entity.aod
index 9d39900010b..3b5d625b75b 100644
--- a/entity/AttributeRelationTree_entity/AttributeRelationTree_entity.aod
+++ b/entity/AttributeRelationTree_entity/AttributeRelationTree_entity.aod
@@ -10,9 +10,6 @@
     <entityField>
       <name>PARENT_ID</name>
     </entityField>
-    <entityField>
-      <name>TITLE</name>
-    </entityField>
     <entityField>
       <name>UID</name>
     </entityField>
@@ -84,10 +81,15 @@
       <name>AB_ATTRIBUTE_ID</name>
       <title>Attribute</title>
       <consumer>SpecificAttribute</consumer>
+      <onValueChange>%aditoprj%/entity/AttributeRelationTree_entity/entityfields/ab_attribute_id/onValueChange.js</onValueChange>
     </entityField>
     <entityField>
       <name>VALUE</name>
       <title>Value</title>
+      <contentTypeProcess>%aditoprj%/entity/AttributeRelationTree_entity/entityfields/value/contentTypeProcess.js</contentTypeProcess>
+      <resolution>DAY</resolution>
+      <mandatory v="true" />
+      <possibleItemsProcess>%aditoprj%/entity/AttributeRelationTree_entity/entityfields/value/possibleItemsProcess.js</possibleItemsProcess>
     </entityField>
     <entityConsumer>
       <name>SpecificAttribute</name>
@@ -105,19 +107,23 @@
         </entityParameter>
       </children>
     </entityConsumer>
-    <entityField>
-      <name>ATTRIBUTE_TYPE</name>
-    </entityField>
   </entityFields>
   <recordContainers>
     <jDitoRecordContainer>
       <name>jdito</name>
       <jDitoRecordAlias>Data_alias</jDitoRecordAlias>
       <contentProcess>%aditoprj%/entity/AttributeRelationTree_entity/recordcontainers/jdito/contentProcess.js</contentProcess>
+      <onInsert>%aditoprj%/entity/AttributeRelationTree_entity/recordcontainers/jdito/onInsert.js</onInsert>
+      <onUpdate>%aditoprj%/entity/AttributeRelationTree_entity/recordcontainers/jdito/onUpdate.js</onUpdate>
+      <onDelete>%aditoprj%/entity/AttributeRelationTree_entity/recordcontainers/jdito/onDelete.js</onDelete>
       <recordFields>
         <element>UID.value</element>
         <element>PARENT_ID.value</element>
-        <element>TITLE.value</element>
+        <element>VALUE.value</element>
+        <element>VALUE.displayValue</element>
+        <element>AB_ATTRIBUTE_ID.value</element>
+        <element>AB_ATTRIBUTE_ID.displayValue</element>
+        <element>ATTRIBUTE_TYPE.value</element>
       </recordFields>
     </jDitoRecordContainer>
   </recordContainers>
diff --git a/entity/AttributeRelationTree_entity/entityfields/ab_attribute_id/onValueChange.js b/entity/AttributeRelationTree_entity/entityfields/ab_attribute_id/onValueChange.js
new file mode 100644
index 00000000000..1cebe1b3fea
--- /dev/null
+++ b/entity/AttributeRelationTree_entity/entityfields/ab_attribute_id/onValueChange.js
@@ -0,0 +1,8 @@
+import("system.vars");
+import("system.neon");
+
+if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT || 
+    vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW) 
+{
+    neon.setFieldValue("$field.VALUE", null);
+}
\ No newline at end of file
diff --git a/entity/AttributeRelationTree_entity/entityfields/value/contentTypeProcess.js b/entity/AttributeRelationTree_entity/entityfields/value/contentTypeProcess.js
new file mode 100644
index 00000000000..55d32c91b70
--- /dev/null
+++ b/entity/AttributeRelationTree_entity/entityfields/value/contentTypeProcess.js
@@ -0,0 +1,6 @@
+import("system.vars");
+import("system.result");
+import("Attribute_lib");
+
+var attributeType = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
+result.string(AttributeTypeUtil.getContentType(attributeType));
\ No newline at end of file
diff --git a/entity/AttributeRelationTree_entity/entityfields/value/possibleItemsProcess.js b/entity/AttributeRelationTree_entity/entityfields/value/possibleItemsProcess.js
new file mode 100644
index 00000000000..da3691199cd
--- /dev/null
+++ b/entity/AttributeRelationTree_entity/entityfields/value/possibleItemsProcess.js
@@ -0,0 +1,46 @@
+import("system.translate");
+import("system.db");
+import("system.result");
+import("system.vars");
+import("Attribute_lib");
+import("Sql_lib");
+
+var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
+var attrType = AttributeUtil.getAttributeType(attributeId);
+
+if (attrType == $AttributeTypes.COMBO)
+{
+    var valueSql = SqlCondition.begin()
+        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_ACTIVE", "1")
+        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_PARENT_ID", attributeId)
+        .andPrepare("AB_ATTRIBUTE.ATTRIBUTE_TYPE", $AttributeTypes.COMBOVALUE)
+        .buildSql("select AB_ATTRIBUTEID, ATTRIBUTE_NAME from AB_ATTRIBUTE", "1=2", "order by ATTRIBUTE_NAME");
+    var valueList = db.table(valueSql);
+    
+    result.object(valueList);
+}
+else if (attrType == $AttributeTypes.BOOLEAN)
+{
+    result.object([
+        ["1", translate.text("Yes")],
+        ["0", translate.text("No")]
+    ]);
+}
+
+//TODO this is a workaround for keywords, when it's possible to use the consumer remove this
+else if (attrType == $AttributeTypes.KEYWORD)
+{
+    var attrKeywordSelect = "select KEYWORD_CONTAINER from AB_ATTRIBUTE";
+    attrKeywordSelect = SqlCondition.begin()
+        .andPrepareVars("AB_ATTRIBUTE.AB_ATTRIBUTEID", "$field.AB_ATTRIBUTE_ID")
+        .buildSql(attrKeywordSelect);
+    
+    var sql = SqlCondition.begin()
+        .andPrepare("AB_KEYWORD_ENTRY.CONTAINER", db.cell(attrKeywordSelect))
+        .buildSql("select AB_KEYWORD_ENTRY.KEYID, AB_KEYWORD_ENTRY.TITLE from AB_KEYWORD_ENTRY");
+    var keywords = db.table(sql).map(function (row)
+    {
+        return [row[0], translate.text(row[1])];
+    });
+    result.object(keywords);
+}
\ No newline at end of file
diff --git a/entity/AttributeRelationTree_entity/recordcontainers/jdito/contentProcess.js b/entity/AttributeRelationTree_entity/recordcontainers/jdito/contentProcess.js
index 2ea0d933bf9..d887ea8db65 100644
--- a/entity/AttributeRelationTree_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/AttributeRelationTree_entity/recordcontainers/jdito/contentProcess.js
@@ -8,9 +8,42 @@ var objectType = vars.get("$param.ObjectType_param");
 var rowId = vars.get("$param.ObjectRowId_param");
 var attributeObj = {};
 var allAttributes = [];
-var sqlSelect = "select AB_ATTRIBUTEID, ATTRIBUTE_PARENT_ID, ATTRIBUTE_NAME from AB_ATTRIBUTE";
+var sqlSelect = "select AB_ATTRIBUTEID, ATTRIBUTE_PARENT_ID, ATTRIBUTE_NAME, ATTRIBUTE_NAME, ATTRIBUTE_NAME, ATTRIBUTE_NAME, ATTRIBUTE_TYPE from AB_ATTRIBUTE";
+
+
+
+var attrCond = SqlCondition.begin()
+    .andPrepare("AB_ATTRIBUTERELATION.OBJECT_ROWID", rowId);
+if (objectType != null)
+    attrCond.andPrepare("AB_ATTRIBUTERELATION.OBJECT_TYPE", objectType);
+
+var defaultFields = [
+    "AB_ATTRIBUTERELATIONID",
+    "AB_ATTRIBUTE_ID", 
+    "AB_ATTRIBUTE.ATTRIBUTE_TYPE", 
+    "AB_ATTRIBUTE.KEYWORD_CONTAINER", 
+    "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) 
+{
+    let attributeId = row[1];
+    let attributeName = "";
+    let value = row[AttributeTypeUtil.getTypeColumnIndex(row[2]) + defaultFields.length];
+    let viewValue;
+    if (row[2].trim() == $AttributeTypes.COMBO)
+        viewValue = row[4];
+    else
+        viewValue = AttributeTypeUtil.getAttributeViewValue(row[2].trim(), value, row[3]);
+
+    return [row[0], attributeId, value, viewValue, attributeId, attributeName, row[2]];
+});
 
-var attributeValues = AttributeRelationUtils.getAllAttributes(rowId, objectType, false, true);
 
 _fetchAttributes(attributeValues.map(function (row) {return row[1]}));
 
diff --git a/entity/AttributeRelationTree_entity/recordcontainers/jdito/onDelete.js b/entity/AttributeRelationTree_entity/recordcontainers/jdito/onDelete.js
new file mode 100644
index 00000000000..219317e1077
--- /dev/null
+++ b/entity/AttributeRelationTree_entity/recordcontainers/jdito/onDelete.js
@@ -0,0 +1,10 @@
+import("system.neon");
+import("system.vars");
+import("Sql_lib");
+import("system.db");
+
+if (vars.get("$field.AB_ATTRIBUTE_ID"))
+{
+    db.deleteData("AB_ATTRIBUTERELATION", SqlCondition.begin().andPrepareVars("AB_ATTRIBUTERELATION.AB_ATTRIBUTERELATIONID", "$field.UID").build("1=2"));
+    neon.refresh();
+}
\ No newline at end of file
diff --git a/entity/AttributeRelationTree_entity/recordcontainers/jdito/onInsert.js b/entity/AttributeRelationTree_entity/recordcontainers/jdito/onInsert.js
new file mode 100644
index 00000000000..ba344ff7047
--- /dev/null
+++ b/entity/AttributeRelationTree_entity/recordcontainers/jdito/onInsert.js
@@ -0,0 +1,31 @@
+import("system.datetime");
+import("system.util");
+import("system.db");
+import("system.vars");
+import("Attribute_lib");
+
+var type = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
+var valueField = AttributeTypeUtil.getDatabaseField(type);
+
+if (valueField)
+{
+    var columns = [
+        "AB_ATTRIBUTERELATIONID",
+        "AB_ATTRIBUTE_ID",
+        "OBJECT_ROWID",
+        "OBJECT_TYPE",
+        "DATE_NEW",
+        "USER_NEW",
+        valueField
+    ];
+    var values = [
+        util.getNewUUID(),
+        vars.get("$field.AB_ATTRIBUTE_ID"),
+        vars.get("$param.ObjectRowId_param"),
+        vars.get("$param.ObjectType_param"),
+        String(datetime.date()),
+        vars.get("$sys.user"),
+        vars.get("$field.VALUE")
+    ];
+    db.insertData("AB_ATTRIBUTERELATION", columns, null, values);
+}
\ No newline at end of file
diff --git a/entity/AttributeRelationTree_entity/recordcontainers/jdito/onUpdate.js b/entity/AttributeRelationTree_entity/recordcontainers/jdito/onUpdate.js
new file mode 100644
index 00000000000..ffe8298ae35
--- /dev/null
+++ b/entity/AttributeRelationTree_entity/recordcontainers/jdito/onUpdate.js
@@ -0,0 +1,25 @@
+import("Sql_lib");
+import("system.util");
+import("system.db");
+import("system.vars");
+import("Attribute_lib");
+
+var type = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
+
+var columns = [
+    "AB_ATTRIBUTE_ID"
+];
+var values = [
+    vars.get("$field.AB_ATTRIBUTE_ID")
+];
+
+var dbFields = AttributeTypeUtil.getAllDatabaseFields();
+dbFields.forEach(function (row)
+{
+    columns.push(row);
+    values.push("");
+});
+values[AttributeTypeUtil.getTypeColumnIndex(type) + 1] = vars.get("$field.VALUE");
+
+db.updateData("AB_ATTRIBUTERELATION", columns, null, values, 
+    SqlCondition.begin().andPrepareVars("AB_ATTRIBUTERELATION.AB_ATTRIBUTERELATIONID", "$field.UID"));
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/valueproxy/contentTypeProcess.js b/entity/AttributeRelation_entity/entityfields/valueproxy/contentTypeProcess.js
index 1e7cfe8ada6..55d32c91b70 100644
--- a/entity/AttributeRelation_entity/entityfields/valueproxy/contentTypeProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/valueproxy/contentTypeProcess.js
@@ -2,5 +2,5 @@ import("system.vars");
 import("system.result");
 import("Attribute_lib");
 
-var attrType = AttributeHandler.begin(vars.get("$field.AB_ATTRIBUTE_ID")).getAttributeContentType();
-result.string(attrType);
\ No newline at end of file
+var attributeType = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
+result.string(AttributeTypeUtil.getContentType(attributeType));
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/valueproxy/displayValueProcess.js b/entity/AttributeRelation_entity/entityfields/valueproxy/displayValueProcess.js
index c2cd0971b53..9a11a4c055e 100644
--- a/entity/AttributeRelation_entity/entityfields/valueproxy/displayValueProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/valueproxy/displayValueProcess.js
@@ -4,7 +4,7 @@ import("system.vars");
 import("Attribute_lib");
 import("Keyword_lib");
 
-var attrType = AttributeHandler.begin(vars.get("$field.AB_ATTRIBUTE_ID")).getAttributeType();
+var attrType = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
 var value;
 if (attrType == $AttributeTypes.COMBO)
     value = AttributeUtil.getSimpleAttributeName(vars.get("$field.ID_VALUE"));
diff --git a/entity/AttributeRelation_entity/entityfields/valueproxy/onValueChange.js b/entity/AttributeRelation_entity/entityfields/valueproxy/onValueChange.js
index 5d8c07fa23e..150cb4b05da 100644
--- a/entity/AttributeRelation_entity/entityfields/valueproxy/onValueChange.js
+++ b/entity/AttributeRelation_entity/entityfields/valueproxy/onValueChange.js
@@ -1,10 +1,33 @@
+import("system.neon");
 import("system.vars");
 import("Entity_lib");
 import("Attribute_lib");
 
 var attrValue = vars.exists("$field.valueProxy") ? vars.get("$field.valueProxy") : "";
 attrValue = ProcessHandlingUtils.getOnValidationValue(attrValue);
-var attribute = AttributeHandler.begin(vars.get("$field.AB_ATTRIBUTE_ID"));
 
-if(attrValue != null)
-    attribute.setAttributeValue(attrValue);
\ No newline at end of file
+if (attrValue != null)
+{
+    var attributeType = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
+    var attrField = (function ()
+    {
+        switch (this)
+        {
+            case $AttributeTypes.TEXT:
+                return "$field.CHAR_VALUE";
+            case $AttributeTypes.DATE:
+                return "$field.DATE_VALUE";
+            case $AttributeTypes.NUMBER:
+                return "$field.NUMBER_VALUE";
+            case $AttributeTypes.BOOLEAN:
+                return "$field.INT_VALUE";
+            case $AttributeTypes.COMBO:
+            case $AttributeTypes.KEYWORD:
+                return "$field.ID_VALUE";
+            default:
+                return null;
+        }
+    }).call(attributeType);
+    if (attrField)
+        neon.setFieldValue(attrField, attrValue);
+}
\ No newline at end of file
diff --git a/entity/AttributeRelation_entity/entityfields/valueproxy/possibleItemsProcess.js b/entity/AttributeRelation_entity/entityfields/valueproxy/possibleItemsProcess.js
index 4a7f1e46eda..4f20aef0cd6 100644
--- a/entity/AttributeRelation_entity/entityfields/valueproxy/possibleItemsProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/valueproxy/possibleItemsProcess.js
@@ -6,7 +6,7 @@ import("Attribute_lib");
 import("Sql_lib");
 
 var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
-var attrType = AttributeHandler.begin(attributeId).getAttributeType();
+var attrType = AttributeUtil.getAttributeType(attributeId);
 
 if (attrType == $AttributeTypes.COMBO)
 {
diff --git a/entity/AttributeRelation_entity/entityfields/valueproxy/valueProcess.js b/entity/AttributeRelation_entity/entityfields/valueproxy/valueProcess.js
index bea46eecb18..572e01f1e13 100644
--- a/entity/AttributeRelation_entity/entityfields/valueproxy/valueProcess.js
+++ b/entity/AttributeRelation_entity/entityfields/valueproxy/valueProcess.js
@@ -1,20 +1,39 @@
+import("system.logging");
 import("system.neon");
 import("system.result");
 import("system.vars");
 import("Attribute_lib");
 
-"$field.CHAR_VALUE";
-"$field.DATE_VALUE";
-"$field.NUMBER_VALUE";
-"$field.INT_VALUE";
-"$field.ID_VALUE";
-
 if(vars.get("$sys.recordstate") != neon.OPERATINGSTATE_NEW)
 {
-    var attrField = AttributeHandler.begin(vars.get("$field.AB_ATTRIBUTE_ID")).getAttributeField();
+    var rowId = vars.get("$param.ObjectRowId_param");
+    var objectType = vars.get("$param.ObjectType_param");
+    var attributeId = vars.get("$field.AB_ATTRIBUTE_ID");
+    logging.log(AttributeRelationUtils.getAttribute(attributeId, rowId, objectType))
+    logging.log(AttributeRelationUtils.getAttribute(attributeId, rowId, objectType, true))
+    
+    
+    var attributeType = AttributeUtil.getAttributeType(vars.get("$field.AB_ATTRIBUTE_ID"));
+    var attrField = (function ()
+    {
+        switch (this)
+        {
+            case $AttributeTypes.TEXT:
+                return "$field.CHAR_VALUE";
+            case $AttributeTypes.DATE:
+                return "$field.DATE_VALUE";
+            case $AttributeTypes.NUMBER:
+                return "$field.NUMBER_VALUE";
+            case $AttributeTypes.BOOLEAN:
+                return "$field.INT_VALUE";
+            case $AttributeTypes.COMBO:
+            case $AttributeTypes.KEYWORD:
+                return "$field.ID_VALUE";
+        }
+    }).call(attributeType);
     var value = null;
     if (attrField != null) //load the value from the correct field for the type
-        value = vars.get("$field." + attrField);
+        value = vars.get(attrField);
     
     if(value != null && value != "")
         result.string(value); 
diff --git a/entity/Attribute_entity/entityfields/attribute_type/displayValueProcess.js b/entity/Attribute_entity/entityfields/attribute_type/displayValueProcess.js
index 1b16072cc5a..84b0b0897c6 100644
--- a/entity/Attribute_entity/entityfields/attribute_type/displayValueProcess.js
+++ b/entity/Attribute_entity/entityfields/attribute_type/displayValueProcess.js
@@ -8,7 +8,7 @@ import("KeywordRegistry_basic");
 
 if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW && vars.get("$field.ATTRIBUTE_PARENT_ID") != "")
 {
-    var type = AttributeHandler.begin(vars.get("$field.ATTRIBUTE_PARENT_ID")).getAttributeType();
+    var type = AttributeUtil.getAttributeType(vars.get("$field.ATTRIBUTE_PARENT_ID"));
     if (type == $AttributeTypes.COMBO)
         result.string(KeywordUtils.getViewValue($KeywordRegistry.attributeType(), $AttributeTypes.COMBOVALUE));
 }
\ No newline at end of file
diff --git a/entity/Attribute_entity/entityfields/attribute_type/valueProcess.js b/entity/Attribute_entity/entityfields/attribute_type/valueProcess.js
index 8c60a83409d..28020da1dda 100644
--- a/entity/Attribute_entity/entityfields/attribute_type/valueProcess.js
+++ b/entity/Attribute_entity/entityfields/attribute_type/valueProcess.js
@@ -6,7 +6,7 @@ import("Attribute_lib");
 
 if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW && vars.get("$field.ATTRIBUTE_PARENT_ID") != "")
 {
-    var type = AttributeHandler.begin(vars.get("$field.ATTRIBUTE_PARENT_ID")).getAttributeType();
+    var type = AttributeUtil.getAttributeType(vars.get("$field.ATTRIBUTE_PARENT_ID"));
     if (type == $AttributeTypes.COMBO)
         result.string($AttributeTypes.COMBOVALUE);
 }
\ No newline at end of file
diff --git a/neonContext/AttributeRelationTree/AttributeRelationTree.aod b/neonContext/AttributeRelationTree/AttributeRelationTree.aod
index 9acb4ce97d8..be8de92fa69 100644
--- a/neonContext/AttributeRelationTree/AttributeRelationTree.aod
+++ b/neonContext/AttributeRelationTree/AttributeRelationTree.aod
@@ -2,6 +2,7 @@
 <neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.0" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.0">
   <name>AttributeRelationTree</name>
   <majorModelMode>DISTRIBUTED</majorModelMode>
+  <editview>AttributeRelationTreeEdit_view</editview>
   <entity>AttributeRelationTree_entity</entity>
   <references>
     <neonViewReference>
diff --git a/neonView/AttributeRelationTree_view/AttributeRelationTree_view.aod b/neonView/AttributeRelationTree_view/AttributeRelationTree_view.aod
index 7c11a4e79ab..1eee2f28de6 100644
--- a/neonView/AttributeRelationTree_view/AttributeRelationTree_view.aod
+++ b/neonView/AttributeRelationTree_view/AttributeRelationTree_view.aod
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.1">
   <name>AttributeRelationTree_view</name>
-  <title>Attributes</title>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <layout>
     <boxLayout>
@@ -9,11 +8,21 @@
     </boxLayout>
   </layout>
   <children>
-    <treeViewTemplate>
-      <name>AttributeRelationTree</name>
+    <treeTableViewTemplate>
+      <name>TreeTable</name>
       <parentField>PARENT_ID</parentField>
-      <titleField>TITLE</titleField>
+      <showChildrenCount v="false" />
       <entityField>#ENTITY</entityField>
-    </treeViewTemplate>
+      <columns>
+        <neonTableColumn>
+          <name>840551af-5a99-4965-a96a-ed134efb28a9</name>
+          <entityField>AB_ATTRIBUTE_ID</entityField>
+        </neonTableColumn>
+        <neonTableColumn>
+          <name>7844082c-fd31-4878-9e57-024cb2b2b627</name>
+          <entityField>VALUE</entityField>
+        </neonTableColumn>
+      </columns>
+    </treeTableViewTemplate>
   </children>
 </neonView>
diff --git a/process/Attribute_lib/process.js b/process/Attribute_lib/process.js
index 4833062c4d4..77622496e39 100644
--- a/process/Attribute_lib/process.js
+++ b/process/Attribute_lib/process.js
@@ -155,6 +155,22 @@ AttributeUtil.hasRelations = function (pAttributeId)
     ) == "true";
 }
 
+/**
+ * returns the type of an attribute
+ * 
+ * @param {String} pAttributeId the id of the attribute
+ * 
+ * @result {String} attribute type
+ */
+AttributeUtil.getAttributeType = function (pAttributeId)
+{
+    var attrTypeSelect = "select ATTRIBUTE_TYPE from AB_ATTRIBUTE";
+    attrTypeSelect = SqlCondition.begin()
+        .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeId)
+        .buildSql(attrTypeSelect);
+    return db.cell(attrTypeSelect).trim();
+}
+
 /*********************************************************************************************************************/
 
 /**
@@ -171,25 +187,39 @@ function AttributeRelationUtils () {}
  * @param {String} pAttributeId attribute-id
  * @param {String} pObjectRowId row-id of the dataset
  * @param {String} [pObjectType=null] object-type
+ * @param {String} [pGetViewValue=false] if true the values are resolved and formatted
  * 
  * @return {String|null} the value of the attribute
  */
-AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObjectType)
+AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObjectType, pGetViewValue)
 {
     var attrCond = SqlCondition.begin()
-        .andPrepare("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", pAttributeId)
-        .andPrepare("AB_ATTRIBUTERELATION.OBJECT_ROWID", pObjectRowId);
+        .andPrepare("AB_ATTRIBUTERELATION.OBJECT_ROWID", pObjectRowId)
+        .andPrepare("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", pAttributeId);
     if (pObjectType != null)
-        attrCond.andPrepare("AB_ATTRIBUTERELATION.OBJECT_TYPE", pAttributeId);
+        attrCond.andPrepare("AB_ATTRIBUTERELATION.OBJECT_TYPE", pObjectType);
+    
+    var defaultFields = [
+        "AB_ATTRIBUTE.ATTRIBUTE_TYPE", 
+        "AB_ATTRIBUTE.KEYWORD_CONTAINER", 
+        "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 attributeSql = attrCond.buildSql("select ATTRIBUTE_TYPE, " + AttributeTypeUtil.getAllDatabaseFields().join(", ")
-        + " from AB_ATTRIBUTERELATION join AB_ATTRIBUTE on AB_ATTRIBUTE_ID = AB_ATTRIBUTEID");
     var attributeValues = db.array(db.ROW, attributeSql);
-    if (attributeValues.length == 0)
+    if (!attributeValues.length)
         return null;
     
-    var valueIndex = AttributeTypeUtil.getTypeColumnIndex(attributeValues[0]) + 1;
-    return attributeValues[valueIndex];
+    let value = attributeValues[AttributeTypeUtil.getTypeColumnIndex(attributeValues[0]) + defaultFields.length];
+    if (pGetViewValue && attributeValues[1].trim() == $AttributeTypes.COMBO)
+        value = attributeValues[2];
+    else if (pGetViewValue)
+        value = AttributeTypeUtil.getAttributeViewValue(attributeValues[0].trim(), value, attributeValues[1]);
+    
+    return value;
 }
 
 /**
@@ -197,43 +227,45 @@ AttributeRelationUtils.getAttribute = function (pAttributeId, pObjectRowId, pObj
  * 
  * @param {String} pObjectRowId object rowid
  * @param {String} [pObjectType=null] object-type
- * @param {String} [pResolveNames=false] if true the full attribute names are used instead of the ids
- * @param {String} [pGetUID=false] include the attributeRelation id
+ * @param {String} [pUseAttributeIds=false] if true the ids are used instead of the full attribute names
+ * @param {String} [pUseIdValues=false] if true the values are not resolved or formatted
  * 
- * @return {String[][]} two-dimensional array a row is [attributeId|attributeName, value] or if pGetUID is true, [attriuteRelationId, attributeId|attributeName, value]
+ * @return {String[][]} two-dimensional array a row is [attributeId|attributeName, value]
  */
-AttributeRelationUtils.getAllAttributes = function (pObjectRowId, pObjectType, pResolveNames, pGetUID)
+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 attributeSql = attrCond.buildSql("select AB_ATTRIBUTERELATIONID, AB_ATTRIBUTE_ID, AB_ATTRIBUTE.ATTRIBUTE_TYPE, AB_ATTRIBUTE.KEYWORD_CONTAINER, COMBOVAL.ATTRIBUTE_NAME, " 
-        + AttributeTypeUtil.getAllDatabaseFields().join(", ")
+    var defaultFields = [
+        "AB_ATTRIBUTE_ID", 
+        "AB_ATTRIBUTE.ATTRIBUTE_TYPE", 
+        "AB_ATTRIBUTE.KEYWORD_CONTAINER", 
+        "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) 
     {
-        let attribute = row[1];
-        if (pResolveNames)
+        let attribute = row[0];
+        if (!pUseAttributeIds)
         {
             if (!(attribute in attributeNameMap))
                 attributeNameMap[attribute] = AttributeUtil.getFullAttributeName(attribute);
             attribute = attributeNameMap[attribute];
         }
-        let value;
-        if (row[2].trim() == $AttributeTypes.COMBO)
-            value = row[4];
-        else
-        {
-            value = row[AttributeTypeUtil.getTypeColumnIndex(row[2]) + 5];
-            value = AttributeTypeUtil.getAttributeViewValue(row[2].trim(), value, row[3]);
-        }
-        if (pGetUID)
-            return [row[0], attribute, value];
+        let value = row[AttributeTypeUtil.getTypeColumnIndex(row[1]) + defaultFields.length];
+        if (!pUseIdValues && row[1].trim() == $AttributeTypes.COMBO)
+            value = row[3];
+        else if (!pUseIdValues)
+            value = AttributeTypeUtil.getAttributeViewValue(row[1].trim(), value, row[2]);
+        
         return [attribute, value];
     });
     
@@ -255,81 +287,6 @@ AttributeRelationUtils.setAttribute = function ()
 
 /*********************************************************************************************************************/
 
-/**
- * This is used in the AttributeRelation enitiy to make the work with attributes there
- * easier. You probaly won't need this for anything else.
- * 
- * @param {String} pAttrId the id of the attribute
- * 
- * @class
- */
-function AttributeHandler (pAttrId)   //TODO: find out if this class is necessary, maybe there is a more elegant solution, this could also be static
-{
-    this.attributeId = pAttrId;
-    this._attributeType = null;
-}
-
-/**
- * Returns a new AttributeHandler for the given Attribute
- */
-AttributeHandler.begin = function (pAttrId)
-{
-    return new AttributeHandler(pAttrId);
-}
-
-/**
- * gets the type of the attribute by the attributeId, after the first call the value
- * is stored so that the sql selection is done only once
- * 
- * @return {String} attribute type
- */
-AttributeHandler.prototype.getAttributeType = function () //TODO: maybe the type should be an own field in the entity instead of getting the type from the attribute id
-{
-    if (this._attributeType == null && this.attributeId != null)
-    {
-        var attrTypeSelect = "select ATTRIBUTE_TYPE from AB_ATTRIBUTE";
-        attrTypeSelect = SqlCondition.begin()
-            .andPrepare("AB_ATTRIBUTE.AB_ATTRIBUTEID", this.attributeId)
-            .buildSql(attrTypeSelect);
-        this._attributeType = db.cell(attrTypeSelect).trim();
-    }
-    return this._attributeType;
-}
-
-/**
- * returns the field that belongs to the type of the attribute
- * 
- * @return {String} attribute field
- */
-AttributeHandler.prototype.getAttributeField = function ()
-{
-    return AttributeTypeUtil.getEntityField(this.getAttributeType());
-}
-
-/**
- * returns the content type that belongs to the type of the attribute
- * 
- * @return {String} attribute field
- */
-AttributeHandler.prototype.getAttributeContentType = function ()
-{
-    return AttributeTypeUtil.getContentType(this.getAttributeType());
-}
-
-/**
- * Sets the value to the AttributeField and also return s the value again.
- * 
- * @param pValue the value
- * 
- * @return {Any} the value
- */
-AttributeHandler.prototype.setAttributeValue = function (pValue)
-{
-    var field = "$field." + this.getAttributeField();
-    if (field != null && vars.exists(field))
-        neon.setFieldValue(field, pValue)
-    return pValue;
-}
 
 /**
  * Object for the enumeration and management of all attribute types.
@@ -341,7 +298,6 @@ AttributeHandler.prototype.setAttributeValue = function (pValue)
  * keyword = the key of the corresponding keyword
  * contentType = the value that is returned in the contentType process for the attribute
  * databaseField = the database field that holds values of attributes with the type
- * entityField = the field in the AttributeRelation enity that holds the value of the attribute for that type
  * 
  * The display name is controlled by the keyword 'AttributeType'
  */
@@ -351,15 +307,13 @@ $AttributeTypes.TEXT = {
     toString : function () {return this.keyword},
     keyword : "TEXT",
     contentType : "TEXT", 
-    databaseField : "CHAR_VALUE", 
-    entityField : "CHAR_VALUE"
+    databaseField : "CHAR_VALUE"
 };
 $AttributeTypes.DATE = {
     toString : function () {return this.keyword},
     keyword : "DATE",
     contentType : "DATE", 
-    databaseField : "DATE_VALUE", 
-    entityField : "DATE_VALUE",
+    databaseField : "DATE_VALUE",
     getViewValue : function (pValue)
         {
             return datetime.toDate(pValue, translate.text("dd.MM.yyyy"));
@@ -369,15 +323,13 @@ $AttributeTypes.NUMBER = {
     toString : function () {return this.keyword},
     keyword : "NUMBER",
     contentType : "NUMBER", 
-    databaseField : "NUMBER_VALUE", 
-    entityField : "NUMBER_VALUE"
+    databaseField : "NUMBER_VALUE"
 };
 $AttributeTypes.BOOLEAN = {
     toString : function () {return this.keyword},
     keyword : "BOOLEAN",
     contentType : "BOOLEAN", 
-    databaseField : "INT_VALUE", 
-    entityField : "INT_VALUE",
+    databaseField : "INT_VALUE",
     getViewValue : function (pValue)
         {
             return pValue == "1" ? translate.text("Yes") : translate.text("No");
@@ -387,29 +339,25 @@ $AttributeTypes.COMBO = {
     toString : function () {return this.keyword},
     keyword : "COMBO",
     contentType : "UNKNOWN",
-    databaseField : "ID_VALUE", 
-    entityField : "ID_VALUE"
+    databaseField : "ID_VALUE"
 };
 $AttributeTypes.COMBOVALUE = {
     toString : function () {return this.keyword},
     keyword : "COMBOVALUE",
     contentType : null, 
-    databaseField : null, 
-    entityField : null
+    databaseField : null
 };
 $AttributeTypes.GROUP = {
     toString : function () {return this.keyword},
     keyword : "GROUP",
     contentType : null, 
-    databaseField : null, 
-    entityField : null
+    databaseField : null
 };
 $AttributeTypes.KEYWORD = {
     toString : function () {return this.keyword},
     keyword : "KEYWORD",
     contentType : "UNKNOWN", 
     databaseField : "ID_VALUE", 
-    entityField : "ID_VALUE",
     getViewValue : function (pValue, pKeyword)
         {
             return KeywordUtils.getViewValue(pKeyword, pValue);
diff --git a/process/Organisation_lib/process.js b/process/Organisation_lib/process.js
index 7f7bba96f84..6072424e622 100644
--- a/process/Organisation_lib/process.js
+++ b/process/Organisation_lib/process.js
@@ -121,7 +121,7 @@ OrgUtils.openOrgReport = function(pOrgId)
     });
     histData = ReportData.begin(["ENTRYDATE", "MEDIUM", "LOGIN", "INFO"]).add(histData);
     
-    var attr = AttributeRelationUtils.getAllAttributes(pOrgId, null, true)
+    var attr = AttributeRelationUtils.getAllAttributes(pOrgId)
         .map(function (row) {return row.join(": ")})
         .join("\n");
     
-- 
GitLab