From f2dbac68c270d72da768c82f8f2e9c5edd093bda Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johannes=20H=C3=B6rmann?= <j.hoermann@adito.de>
Date: Thu, 14 Mar 2019 15:21:31 +0000
Subject: [PATCH] Partlist: editable tree table

---
 entity/Address_entity/Address_entity.aod      |  2 +-
 .../attrparentid_param/valueProcess.js        |  2 +-
 .../containername_param/valueProcess.js       |  5 --
 .../recordcontainers/db/conditionProcess.js   |  1 +
 entity/Prod2prod_entity/Prod2prod_entity.aod  | 60 +++++++++++++++++--
 .../alter/children/insert/onActionProcess.js  | 20 +++++++
 .../currentproductid_param/valueProcess.js    |  7 +++
 .../entityfields/dest_id/valueProcess.js      |  6 +-
 .../entityfields/optional/valueProcess.js     | 12 ++--
 .../excludedproducts_param/valueProcess.js    | 56 +++++++++++++++++
 .../source_id/possibleItemsProcess.js         | 30 ----------
 .../entityfields/takeprice/valueProcess.js    |  6 ++
 .../targetcontext/valueProcess.js             |  3 +
 .../recordcontainers/jdito/contentProcess.js  | 34 ++++++-----
 .../recordcontainers/jdito/onDelete.js        |  8 ++-
 .../recordcontainers/jdito/onInsert.js        |  4 +-
 .../recordcontainers/jdito/onUpdate.js        | 25 --------
 entity/Product_entity/Product_entity.aod      | 24 ++++++++
 .../recordcontainers/db/conditionProcess.js   | 20 +++++++
 neonContext/Prod2prod/Prod2prod.aod           |  6 +-
 .../Prod2ProdEdit_view/Prod2ProdEdit_view.aod | 35 +++++++++++
 .../Prod2prodFilter_view.aod                  |  1 +
 .../Prod2prodPreview_view.aod                 | 10 ----
 .../ProductFilter_view/ProductFilter_view.aod |  4 ++
 process/Data_lib/process.js                   |  7 ++-
 process/Product_lib/process.js                | 35 +++++++----
 26 files changed, 303 insertions(+), 120 deletions(-)
 delete mode 100644 entity/Communication_entity/entityfields/keywordmedium/children/containername_param/valueProcess.js
 create mode 100644 entity/Prod2prod_entity/entityfields/alter/children/insert/onActionProcess.js
 create mode 100644 entity/Prod2prod_entity/entityfields/currentproductid_param/valueProcess.js
 create mode 100644 entity/Prod2prod_entity/entityfields/products/children/excludedproducts_param/valueProcess.js
 delete mode 100644 entity/Prod2prod_entity/entityfields/source_id/possibleItemsProcess.js
 create mode 100644 entity/Prod2prod_entity/entityfields/takeprice/valueProcess.js
 create mode 100644 entity/Prod2prod_entity/entityfields/targetcontext/valueProcess.js
 delete mode 100644 entity/Prod2prod_entity/recordcontainers/jdito/onUpdate.js
 create mode 100644 entity/Product_entity/recordcontainers/db/conditionProcess.js
 create mode 100644 neonView/Prod2ProdEdit_view/Prod2ProdEdit_view.aod
 delete mode 100644 neonView/Prod2prodPreview_view/Prod2prodPreview_view.aod

diff --git a/entity/Address_entity/Address_entity.aod b/entity/Address_entity/Address_entity.aod
index bec846d517c..aa37a565812 100644
--- a/entity/Address_entity/Address_entity.aod
+++ b/entity/Address_entity/Address_entity.aod
@@ -160,7 +160,7 @@
       <children>
         <entityParameter>
           <name>ContactType_param</name>
-          <code>%aditoprj%/entity/Address_entity/entityfields/organisationaddressesbycontact/children/contacttype_param/code.js</code>
+          <valueProcess>%aditoprj%/entity/Address_entity/entityfields/organisationaddressesbycontact/children/contacttype_param/valueProcess.js</valueProcess>
           <expose v="true" />
           <description>TODO: expose auf false. aktuell wird der Code nicht ausgeführt, wenn Expose false ist.</description>
         </entityParameter>
diff --git a/entity/Attribute_entity/entityfields/attributechildren/children/attrparentid_param/valueProcess.js b/entity/Attribute_entity/entityfields/attributechildren/children/attrparentid_param/valueProcess.js
index b0b8a8a60f8..f2318723e88 100644
--- a/entity/Attribute_entity/entityfields/attributechildren/children/attrparentid_param/valueProcess.js
+++ b/entity/Attribute_entity/entityfields/attributechildren/children/attrparentid_param/valueProcess.js
@@ -1,4 +1,4 @@
 import("system.vars");
 import("system.result");
 
-result.string(vars.getString("$field.AB_ATTRIBUTEID"));
\ No newline at end of file
+result.string(vars.getString("$field.AB_ATTRIBUTEID"));
diff --git a/entity/Communication_entity/entityfields/keywordmedium/children/containername_param/valueProcess.js b/entity/Communication_entity/entityfields/keywordmedium/children/containername_param/valueProcess.js
deleted file mode 100644
index 8f3681332b3..00000000000
--- a/entity/Communication_entity/entityfields/keywordmedium/children/containername_param/valueProcess.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import("system.result");
-import("Keyword_lib");
-import("KeywordRegistry_basic");
-
-result.string($KeywordRegistry.communicationMedium());
\ No newline at end of file
diff --git a/entity/Person_entity/recordcontainers/db/conditionProcess.js b/entity/Person_entity/recordcontainers/db/conditionProcess.js
index e8302d00414..3d91dc356d8 100644
--- a/entity/Person_entity/recordcontainers/db/conditionProcess.js
+++ b/entity/Person_entity/recordcontainers/db/conditionProcess.js
@@ -1,3 +1,4 @@
+import("system.vars");
 import("system.db");
 import("system.result");
 import("Sql_lib");
diff --git a/entity/Prod2prod_entity/Prod2prod_entity.aod b/entity/Prod2prod_entity/Prod2prod_entity.aod
index 31d3adaf749..f27ce2f8c15 100644
--- a/entity/Prod2prod_entity/Prod2prod_entity.aod
+++ b/entity/Prod2prod_entity/Prod2prod_entity.aod
@@ -33,7 +33,7 @@
       <name>SOURCE_ID</name>
       <documentation>%aditoprj%/entity/Prod2prod_entity/entityfields/source_id/documentation.adoc</documentation>
       <title>Product</title>
-      <possibleItemsProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/source_id/possibleItemsProcess.js</possibleItemsProcess>
+      <consumer>Products</consumer>
     </entityField>
     <entityField>
       <name>TAKEPRICE</name>
@@ -41,6 +41,7 @@
       <title>Take price</title>
       <contentType>BOOLEAN</contentType>
       <possibleItemsProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/takeprice/possibleItemsProcess.js</possibleItemsProcess>
+      <valueProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/takeprice/valueProcess.js</valueProcess>
     </entityField>
     <entityParameter>
       <name>ProductId_param</name>
@@ -60,7 +61,8 @@
     <entityProvider>
       <name>ProductLinks</name>
       <fieldType>DEPENDENCY_IN</fieldType>
-      <recordContainer>jdito</recordContainer>
+      <targetContextField>targetContext</targetContextField>
+      <targetIdField>SOURCE_ID</targetIdField>
       <dependencies>
         <entityDependency>
           <name>4bd06e3b-17a0-483e-a61c-818ff7e86be3</name>
@@ -69,20 +71,69 @@
           <isConsumer v="false" />
         </entityDependency>
       </dependencies>
+      <children>
+        <entityParameter>
+          <name>ProductId_param</name>
+          <expose v="true" />
+        </entityParameter>
+        <entityParameter>
+          <name>CurrentProductId_param</name>
+          <expose v="false" />
+        </entityParameter>
+      </children>
     </entityProvider>
     <entityField>
       <name>PRODUCTCODE</name>
       <title>Product number</title>
     </entityField>
+    <entityField>
+      <name>targetContext</name>
+      <valueProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/targetcontext/valueProcess.js</valueProcess>
+    </entityField>
+    <entityActionGroup>
+      <name>alter</name>
+      <children>
+        <entityActionField>
+          <name>insert</name>
+          <fieldType>ACTION</fieldType>
+          <onActionProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/alter/children/insert/onActionProcess.js</onActionProcess>
+          <isSelectionAction v="true" />
+          <iconId>VAADIN:FILE_TREE_SMALL</iconId>
+        </entityActionField>
+      </children>
+    </entityActionGroup>
+    <entityConsumer>
+      <name>Products</name>
+      <fieldType>DEPENDENCY_OUT</fieldType>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Product_entity</entityName>
+        <fieldName>Products</fieldName>
+      </dependency>
+      <children>
+        <entityParameter>
+          <name>ExcludedProducts_param</name>
+          <valueProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/products/children/excludedproducts_param/valueProcess.js</valueProcess>
+          <expose v="true" />
+        </entityParameter>
+      </children>
+    </entityConsumer>
+    <entityField>
+      <name>PRODUCTID</name>
+    </entityField>
+    <entityParameter>
+      <name>CurrentProductId_param</name>
+      <valueProcess>%aditoprj%/entity/Prod2prod_entity/entityfields/currentproductid_param/valueProcess.js</valueProcess>
+      <expose v="true" />
+      <description>PARAMETER</description>
+    </entityParameter>
   </entityFields>
   <recordContainers>
     <jDitoRecordContainer>
       <name>jdito</name>
       <jDitoRecordAlias>Data_alias</jDitoRecordAlias>
       <contentProcess>%aditoprj%/entity/Prod2prod_entity/recordcontainers/jdito/contentProcess.js</contentProcess>
-      <isSortable v="false" />
       <onInsert>%aditoprj%/entity/Prod2prod_entity/recordcontainers/jdito/onInsert.js</onInsert>
-      <onUpdate>%aditoprj%/entity/Prod2prod_entity/recordcontainers/jdito/onUpdate.js</onUpdate>
       <onDelete>%aditoprj%/entity/Prod2prod_entity/recordcontainers/jdito/onDelete.js</onDelete>
       <recordFields>
         <element>UID.value</element>
@@ -92,6 +143,7 @@
         <element>OPTIONAL.value</element>
         <element>TAKEPRICE.value</element>
         <element>PRODUCTCODE.value</element>
+        <element>PRODUCTID.value</element>
         <element>SOURCE_ID.value</element>
         <element>DEST_ID.displayValue</element>
       </recordFields>
diff --git a/entity/Prod2prod_entity/entityfields/alter/children/insert/onActionProcess.js b/entity/Prod2prod_entity/entityfields/alter/children/insert/onActionProcess.js
new file mode 100644
index 00000000000..9aef9e30447
--- /dev/null
+++ b/entity/Prod2prod_entity/entityfields/alter/children/insert/onActionProcess.js
@@ -0,0 +1,20 @@
+import("system.neon");
+import("system.vars");
+
+if (vars.exists("$local.rows") && vars.get("$local.rows"))
+{
+    var selectedRows = JSON.parse(vars.get("$local.rows"));
+    
+    if (selectedRows.length > 0 && vars.exists("$param.ProductId_param") && vars.getString("$param.ProductId_param"))
+    {
+        var productId = selectedRows[0].PRODUCTID;
+        var params = {
+            "ProductId_param" : vars.getString("$param.ProductId_param"),
+            "CurrentProductId_param" : productId
+        };
+
+        neon.openContext("Prod2prod", "Prod2ProdEdit_view", null, neon.OPERATINGSTATE_NEW, params);
+    }
+    
+    
+}
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/entityfields/currentproductid_param/valueProcess.js b/entity/Prod2prod_entity/entityfields/currentproductid_param/valueProcess.js
new file mode 100644
index 00000000000..10ba60935fa
--- /dev/null
+++ b/entity/Prod2prod_entity/entityfields/currentproductid_param/valueProcess.js
@@ -0,0 +1,7 @@
+import("system.result");
+import("system.vars");
+// default is the ProductId_param
+if (vars.exists("$param.ProductId_param") && vars.get("$param.ProductId_param"))
+{
+    result.string(vars.get("$param.ProductId_param"));
+}
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/entityfields/dest_id/valueProcess.js b/entity/Prod2prod_entity/entityfields/dest_id/valueProcess.js
index c54ef701ced..cdcb38abd09 100644
--- a/entity/Prod2prod_entity/entityfields/dest_id/valueProcess.js
+++ b/entity/Prod2prod_entity/entityfields/dest_id/valueProcess.js
@@ -2,9 +2,9 @@ import("system.result");
 import("system.neon");
 import("system.vars");
 
-if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT)
 {
-    if(vars.exists("$param.ProductId_param") && vars.get("$param.ProductId_param") != null && vars.get("$param.ProductId_param") != "")
-        result.string(vars.getString("$param.ProductId_param"));    
+    if(vars.exists("$param.CurrentProductId_param") && vars.get("$param.CurrentProductId_param"))
+        result.string(vars.getString("$param.CurrentProductId_param"));    
 }
 
diff --git a/entity/Prod2prod_entity/entityfields/optional/valueProcess.js b/entity/Prod2prod_entity/entityfields/optional/valueProcess.js
index 83eed9e7ea6..31b2531af1a 100644
--- a/entity/Prod2prod_entity/entityfields/optional/valueProcess.js
+++ b/entity/Prod2prod_entity/entityfields/optional/valueProcess.js
@@ -1,6 +1,6 @@
-//import("system.vars");
-//import("system.result");
-//import("system.neon");
-//
-//if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
-//    result.string("0");
\ No newline at end of file
+import("system.vars");
+import("system.result");
+import("system.neon");
+
+if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+    result.string("0");
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/entityfields/products/children/excludedproducts_param/valueProcess.js b/entity/Prod2prod_entity/entityfields/products/children/excludedproducts_param/valueProcess.js
new file mode 100644
index 00000000000..cc6b35783c6
--- /dev/null
+++ b/entity/Prod2prod_entity/entityfields/products/children/excludedproducts_param/valueProcess.js
@@ -0,0 +1,56 @@
+import("system.neon");
+import("system.db");
+import("system.vars");
+import("system.result");
+
+var recordstate = vars.get("$sys.recordstate");
+
+var currentPartID = (vars.exists("$param.CurrentProductId_param") && vars.getString("$param.CurrentProductId_param")) ? vars.getString("$param.CurrentProductId_param") : "";
+var productid = vars.getString("$param.ProductId_param");
+
+var destid = productid; //für Insert ermitteln
+var excludeIDs;
+
+if (recordstate == neon.OPERATINGSTATE_EDIT)
+{    
+    //Ausschließen des geöffneten Datensatzes
+    excludeIDs = [productid]
+    _getParentID([productid, currentPartID], excludeIDs);
+    _getChildrenID([productid, currentPartID], excludeIDs);
+}
+else
+{
+    //Ausschließen des geöffneten Datensatzes und des Produkts auf dem wir "stehen"
+    excludeIDs = [productid, currentPartID];
+    
+    //beide Seiten prüfen ob Über- oder Untergeordnetes Produkt
+    _getParentID([excludeIDs[0], excludeIDs[1]], excludeIDs);
+    _getChildrenID([excludeIDs[0], excludeIDs[1]], excludeIDs);
+}
+
+result.object(excludeIDs);
+
+// TODO: remove code duplication, better param naming and using SqlCondition
+function _getParentID(pID, pIDs)
+{
+    //Ermitteln welche Produkte ausgeschlossen werden müssen.
+    cond = typeof(pID) == "object" ? "in  ('" + pID.join("', '") +"')" : " =  '" + pID + "' "   
+    var parents = db.array(db.COLUMN, "select DEST_ID from PROD2PROD where SOURCE_ID " + cond);
+    for ( var i = 0; i < parents.length; i++)
+    {    
+        pIDs.push(parents[i]);
+        _getParentID(parents[i], pIDs)
+    }
+}
+
+function _getChildrenID(pID, pIDs)
+{
+    //Ermitteln welche Produkte ausgeschlossen werden müssen.
+    cond = typeof(pID) == "object" ? "in  ('" + pID.join("', '") +"')" : " =  '" + pID + "' "
+    var children = db.array(db.COLUMN, "select SOURCE_ID from PROD2PROD where DEST_ID " + cond);
+    for ( var i = 0; i < children.length; i++)
+    {    
+        pIDs.push(children[i]);
+        _getChildrenID(children[i], pIDs)
+    }
+}
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/entityfields/source_id/possibleItemsProcess.js b/entity/Prod2prod_entity/entityfields/source_id/possibleItemsProcess.js
deleted file mode 100644
index dbfb722d579..00000000000
--- a/entity/Prod2prod_entity/entityfields/source_id/possibleItemsProcess.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import("system.vars");
-import("system.result");
-import("system.db");
-import("system.neon");
-import("Keyword_lib");
-import("Product_lib");
-import("KeywordRegistry_basic");
-
-var condition = "";
-if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
-{
-    var prodid = vars.get("$field.DEST_ID");
-    var excludeableProds = [prodid];
-    
-    var p2pUtils = new Prod2ProdUtils(prodid);  
-    
-    excludeableProds = excludeableProds.concat(p2pUtils.getPartsListProdIds());
-    excludeableProds = excludeableProds.concat(p2pUtils.getParentProdIds());
-    
-    condition += " where PRODUCTID not in ('" + excludeableProds.join("','") + "')";
-}
-
-var prods = db.table("select PRODUCTID, GROUPCODEID, PRODUCTNAME, PRODUCTCODE from PRODUCT " + condition);
-var res = [];
-for(var i = 0; i < prods.length; i++)
-{
-    res.push([prods[i][0], KeywordUtils.getViewValue($KeywordRegistry.productGroupcode(), prods[i][1]) + " / " + prods[i][2] + " / " + prods[i][3]]);
-}
-
-result.object(res);
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/entityfields/takeprice/valueProcess.js b/entity/Prod2prod_entity/entityfields/takeprice/valueProcess.js
new file mode 100644
index 00000000000..31b2531af1a
--- /dev/null
+++ b/entity/Prod2prod_entity/entityfields/takeprice/valueProcess.js
@@ -0,0 +1,6 @@
+import("system.vars");
+import("system.result");
+import("system.neon");
+
+if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+    result.string("0");
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/entityfields/targetcontext/valueProcess.js b/entity/Prod2prod_entity/entityfields/targetcontext/valueProcess.js
new file mode 100644
index 00000000000..4fad819079c
--- /dev/null
+++ b/entity/Prod2prod_entity/entityfields/targetcontext/valueProcess.js
@@ -0,0 +1,3 @@
+import("system.result");
+
+result.string("Product")
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/recordcontainers/jdito/contentProcess.js b/entity/Prod2prod_entity/recordcontainers/jdito/contentProcess.js
index 7a8d1f20d33..1eca5fb315c 100644
--- a/entity/Prod2prod_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/Prod2prod_entity/recordcontainers/jdito/contentProcess.js
@@ -1,15 +1,21 @@
-import("system.logging");
-import("system.result");
-import("system.vars");
-import("system.db");
-import("system.util");
-import("Product_lib");
-
-var prodid = vars.exists("$param.ProductId_param") 
-             && vars.get("$param.ProductId_param") != null ? vars.get("$param.ProductId_param") : "";
-
-if(prodid != "")
-{
-    var p2pUtils = new Prod2ProdUtils(prodid);
-    result.object(p2pUtils.getPartsListForRecordContainer());
+import("system.neon");
+import("system.result");
+import("system.vars");
+import("system.db");
+import("system.util");
+import("Product_lib");
+
+if (vars.get("$sys.recordstate") != neon.OPERATINGSTATE_NEW)
+{
+    var prodid = vars.exists("$param.ProductId_param") 
+                 && vars.get("$param.ProductId_param") != null ? vars.get("$param.ProductId_param") : "";
+    if(prodid != "")
+    {
+        var p2pUtils = new Prod2ProdUtils(prodid);
+        result.object(p2pUtils.getPartsListForRecordContainer());
+    }
+}
+else
+{
+    result.object([]);
 }
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/recordcontainers/jdito/onDelete.js b/entity/Prod2prod_entity/recordcontainers/jdito/onDelete.js
index e2c6c0ff0e6..75472b57594 100644
--- a/entity/Prod2prod_entity/recordcontainers/jdito/onDelete.js
+++ b/entity/Prod2prod_entity/recordcontainers/jdito/onDelete.js
@@ -1,6 +1,10 @@
+import("system.neon");
 import("system.db");
 import("Sql_lib");
 
 db.deleteData("PROD2PROD", SqlCondition.begin()
-                                       .andPrepareVars("PROD2PROD.PROD2PRODID", "$field.UID", "# = ?")
-                                       .build("1 = 0"));
\ No newline at end of file
+                                       .andPrepareVars("PROD2PROD.PROD2PRODID", "$field.PROD2PRODID")
+                                       .build("1=2"));
+
+// Refresh otherwise the children of the deleted node would be moved to the root.
+neon.refresh()
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/recordcontainers/jdito/onInsert.js b/entity/Prod2prod_entity/recordcontainers/jdito/onInsert.js
index d11203f6249..44ffb3c420f 100644
--- a/entity/Prod2prod_entity/recordcontainers/jdito/onInsert.js
+++ b/entity/Prod2prod_entity/recordcontainers/jdito/onInsert.js
@@ -1,3 +1,4 @@
+import("system.logging");
 import("system.datetime");
 import("system.vars");
 import("system.db");
@@ -9,11 +10,12 @@ var cols = [ "PROD2PRODID"
            , "OPTIONAL"
            , "TAKEPRICE" ];
 
-var vals = [ vars.get("$field.UID")
+var vals = [ vars.get("$field.PROD2PRODID")
            , vars.get("$field.DEST_ID")
            , vars.get("$field.SOURCE_ID")
            , vars.get("$field.QUANTITY")
            , vars.get("$field.OPTIONAL")
            , vars.get("$field.TAKEPRICE") ];
 
+logging.log("INSERT!!!!")
 db.insertData("PROD2PROD", cols, null, vals);
\ No newline at end of file
diff --git a/entity/Prod2prod_entity/recordcontainers/jdito/onUpdate.js b/entity/Prod2prod_entity/recordcontainers/jdito/onUpdate.js
deleted file mode 100644
index 2d421e9b24e..00000000000
--- a/entity/Prod2prod_entity/recordcontainers/jdito/onUpdate.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import("system.datetime");
-import("system.vars");
-import("system.db");
-imp
-
-var cols = [ "DEST_ID"
-           , "SOURCE_ID"
-           , "QUANTITY"
-           , "OPTIONAL"
-           , "TAKEPRICE" ];
-
-var vals = [ vars.get("$field.DEST_ID")
-           , vars.get("$field.SOURCE_ID")
-           , vars.get("$field.QUANTITY")
-           , vars.get("$field.OPTIONAL")
-           , vars.get("$field.TAKEPRICE") ];
-
-
-db.updateData("PROD2PROD", 
-              cols,
-              null,
-              vals, 
-              SqlCondition.begin()
-                          .andPrepareVars("PROD2PROD.PROD2PRODID", "$field.UID", "# = ?")
-                          .build("1 = 0"));
\ No newline at end of file
diff --git a/entity/Product_entity/Product_entity.aod b/entity/Product_entity/Product_entity.aod
index daf1095ab99..bd797c557cb 100644
--- a/entity/Product_entity/Product_entity.aod
+++ b/entity/Product_entity/Product_entity.aod
@@ -334,6 +334,29 @@
         </entityParameter>
       </children>
     </entityConsumer>
+    <entityParameter>
+      <name>ExcludedProducts_param</name>
+      <expose v="true" />
+      <description>PARAMETER</description>
+    </entityParameter>
+    <entityProvider>
+      <name>Products</name>
+      <fieldType>DEPENDENCY_IN</fieldType>
+      <dependencies>
+        <entityDependency>
+          <name>1b12e8ef-74ba-4294-bd2b-9d4afa80798c</name>
+          <entityName>Prod2prod_entity</entityName>
+          <fieldName>Products</fieldName>
+          <isConsumer v="false" />
+        </entityDependency>
+      </dependencies>
+      <children>
+        <entityParameter>
+          <name>ExcludedProducts_param</name>
+          <expose v="true" />
+        </entityParameter>
+      </children>
+    </entityProvider>
     <entityActionField>
       <name>newTask</name>
       <fieldType>ACTION</fieldType>
@@ -348,6 +371,7 @@
       <alias>Data_alias</alias>
       <maximumDbRows v="0" />
       <fromClauseProcess>%aditoprj%/entity/Product_entity/recordcontainers/db/fromClauseProcess.js</fromClauseProcess>
+      <conditionProcess>%aditoprj%/entity/Product_entity/recordcontainers/db/conditionProcess.js</conditionProcess>
       <onDBUpdate>%aditoprj%/entity/Product_entity/recordcontainers/db/onDBUpdate.js</onDBUpdate>
       <onDBDelete>%aditoprj%/entity/Product_entity/recordcontainers/db/onDBDelete.js</onDBDelete>
       <linkInformation>
diff --git a/entity/Product_entity/recordcontainers/db/conditionProcess.js b/entity/Product_entity/recordcontainers/db/conditionProcess.js
new file mode 100644
index 00000000000..4d4a75621f1
--- /dev/null
+++ b/entity/Product_entity/recordcontainers/db/conditionProcess.js
@@ -0,0 +1,20 @@
+import("system.logging");
+import("system.result");
+import("system.vars");
+import("system.db");
+import("Sql_lib");
+
+var productCond = SqlCondition.begin()
+
+if (vars.exists("$param.ExcludedProducts_param") && vars.get("$param.ExcludedProducts_param"))
+{
+    var excludedIds = JSON.parse(vars.get("$param.ExcludedProducts_param"));
+
+    excludedIds.forEach(function(pId) 
+    {
+        this.andPrepare("PRODUCT.PRODUCTID", pId, "# <> ?");
+    }, productCond);
+}
+
+//TODO: use a preparedCondition when available #1030812 #1034026
+result.string(db.translateCondition(productCond.build("1 = 1")));
\ No newline at end of file
diff --git a/neonContext/Prod2prod/Prod2prod.aod b/neonContext/Prod2prod/Prod2prod.aod
index 7573a9c94d9..6e83065bd65 100644
--- a/neonContext/Prod2prod/Prod2prod.aod
+++ b/neonContext/Prod2prod/Prod2prod.aod
@@ -5,7 +5,7 @@
   <comment>Prod2Prod is mainly used for the parts list of products</comment>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <filterview>Prod2prodFilter_view</filterview>
-  <preview>Prod2prodPreview_view</preview>
+  <editview>Prod2ProdEdit_view</editview>
   <entity>Prod2prod_entity</entity>
   <references>
     <neonViewReference>
@@ -13,8 +13,8 @@
       <view>Prod2prodFilter_view</view>
     </neonViewReference>
     <neonViewReference>
-      <name>2a037997-7a55-4005-956e-fccf12ccc9d2</name>
-      <view>Prod2prodPreview_view</view>
+      <name>428b22a1-427f-4547-a478-964442078bc1</name>
+      <view>Prod2ProdEdit_view</view>
     </neonViewReference>
   </references>
 </neonContext>
diff --git a/neonView/Prod2ProdEdit_view/Prod2ProdEdit_view.aod b/neonView/Prod2ProdEdit_view/Prod2ProdEdit_view.aod
new file mode 100644
index 00000000000..90875ab7fb7
--- /dev/null
+++ b/neonView/Prod2ProdEdit_view/Prod2ProdEdit_view.aod
@@ -0,0 +1,35 @@
+<?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.0.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.0.1">
+  <name>Prod2ProdEdit_view</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <layout>
+    <boxLayout>
+      <name>layout</name>
+    </boxLayout>
+  </layout>
+  <children>
+    <genericViewTemplate>
+      <name>linkData</name>
+      <editMode v="true" />
+      <entityField>#ENTITY</entityField>
+      <fields>
+        <entityFieldLink>
+          <name>a522d552-60cd-4b79-827f-64bb9b551f57</name>
+          <entityField>SOURCE_ID</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>868f8e9b-c863-4515-8aec-0b3d8c7657b5</name>
+          <entityField>QUANTITY</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>79c10c6e-d84e-4147-b4a6-fb283cc3521e</name>
+          <entityField>OPTIONAL</entityField>
+        </entityFieldLink>
+        <entityFieldLink>
+          <name>7fb15e31-bb91-47a8-9ae4-15cc1734d7ee</name>
+          <entityField>TAKEPRICE</entityField>
+        </entityFieldLink>
+      </fields>
+    </genericViewTemplate>
+  </children>
+</neonView>
diff --git a/neonView/Prod2prodFilter_view/Prod2prodFilter_view.aod b/neonView/Prod2prodFilter_view/Prod2prodFilter_view.aod
index c458b6242b9..4ed00e1c074 100644
--- a/neonView/Prod2prodFilter_view/Prod2prodFilter_view.aod
+++ b/neonView/Prod2prodFilter_view/Prod2prodFilter_view.aod
@@ -12,6 +12,7 @@
     <treetableViewTemplate>
       <name>partlist</name>
       <parentField>PARENTID</parentField>
+      <favoriteActionGroup1>alter</favoriteActionGroup1>
       <titleField>PRODUCTCODE</titleField>
       <descriptionField>QUANTITY</descriptionField>
       <entityField>#ENTITY</entityField>
diff --git a/neonView/Prod2prodPreview_view/Prod2prodPreview_view.aod b/neonView/Prod2prodPreview_view/Prod2prodPreview_view.aod
deleted file mode 100644
index 26f5a6c4d46..00000000000
--- a/neonView/Prod2prodPreview_view/Prod2prodPreview_view.aod
+++ /dev/null
@@ -1,10 +0,0 @@
-<?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.0.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.0.1">
-  <name>Prod2prodPreview_view</name>
-  <majorModelMode>DISTRIBUTED</majorModelMode>
-  <layout>
-    <boxLayout>
-      <name>layout</name>
-    </boxLayout>
-  </layout>
-</neonView>
diff --git a/neonView/ProductFilter_view/ProductFilter_view.aod b/neonView/ProductFilter_view/ProductFilter_view.aod
index 487ef718482..9a94df104cc 100644
--- a/neonView/ProductFilter_view/ProductFilter_view.aod
+++ b/neonView/ProductFilter_view/ProductFilter_view.aod
@@ -38,6 +38,10 @@
           <name>bf58edbf-0708-41a7-a092-ebc87a60c3c2</name>
           <entityField>IMAGE</entityField>
         </neonTableColumn>
+        <neonTableColumn>
+          <name>913381d1-91cc-4ec7-b732-b35ddab01b90</name>
+          <entityField>test</entityField>
+        </neonTableColumn>
         <neonTableColumn>
           <name>ee627d12-c60c-48c2-a86e-2a144f5853e6</name>
           <entityField>PRODUCTCODE</entityField>
diff --git a/process/Data_lib/process.js b/process/Data_lib/process.js
index f61e69c016b..fe628720ef1 100644
--- a/process/Data_lib/process.js
+++ b/process/Data_lib/process.js
@@ -375,8 +375,10 @@ DataTree.prototype.toArray = function(pMaxDepth)
     var uidMap = {};
     __push(this.rootId, tree[this.rootId], 0);
     
-    function __push(pParent, pNode, pDepth) {   
-        for (var i = 0; i < pNode.ids.length; i++) {
+    function __push(pParent, pNode, pDepth) {
+        if (pNode != undefined)
+        {
+            for (var i = 0; i < pNode.ids.length; i++) {
             if (pNode.parent == that.baseId)
             {
                 // set UUID to "" if it is a root-node
@@ -388,6 +390,7 @@ DataTree.prototype.toArray = function(pMaxDepth)
             
             if (pMaxDepth == undefined || pDepth < pMaxDepth)
                 __push(nextUid, tree[pNode.ids[i]], pDepth+1);
+            }
         }
     }
     
diff --git a/process/Product_lib/process.js b/process/Product_lib/process.js
index 99a29735c11..03b268f9d67 100644
--- a/process/Product_lib/process.js
+++ b/process/Product_lib/process.js
@@ -1,3 +1,4 @@
+import("system.logging");
 import("system.util");
 import("system.SQLTYPES");
 import("system.datetime");
@@ -427,7 +428,8 @@ Prod2ProdUtils.prototype.getPartsListObject = function() {
  *                    , "QUANTITY"
  *                    , "OPTIONAL"
  *                    , "TAKEPRICE"
- *                    , "PRODUCTCODE"] ]
+ *                    , "PRODUCTCODE"
+ *                    , "PRODUCTID"] ]
  */
 Prod2ProdUtils.prototype.getPartsListForRecordContainer = function() {
     var tree = this._relateChilds();
@@ -488,7 +490,7 @@ Prod2ProdUtils.prototype.getParentProdIds = function() {
 */
 Prod2ProdUtils.prototype._initProd2ProdData = function() {
     if (this.data == undefined) {
-        this.data = db.table("select SOURCE_ID, DEST_ID, PROD2PRODID, QUANTITY, OPTIONAL, TAKEPRICE, PRODUCTCODE, SOURCE_ID, DEST_ID "
+        this.data = db.table("select SOURCE_ID, DEST_ID, PROD2PRODID, QUANTITY, OPTIONAL, TAKEPRICE, PRODUCTCODE, PRODUCTID, SOURCE_ID, DEST_ID "
                     + "from PROD2PROD join PRODUCT on PROD2PROD.SOURCE_ID = PRODUCTID "
                     + "order by PRODUCTCODE ");
     }
@@ -500,7 +502,7 @@ Prod2ProdUtils.prototype._initProd2ProdData = function() {
 Prod2ProdUtils.prototype._buildTree = function(supervised) {
     this._initProd2ProdData();
     var productId = this.productId;
-    
+        
     var tree = DataTree.begin(this.productId).addArray(this.data,
         function(pUid, pNode)
         {
@@ -522,6 +524,7 @@ Prod2ProdUtils.prototype._buildTree = function(supervised) {
                     pNode["optional"] = pNode.data[2];
                     pNode["takeprice"] = pNode.data[3];
                     pNode["productcode"] = pNode.data[4];
+                    pNode["productid"] = pNode.data[5];
                 }
             }
         }
@@ -538,11 +541,14 @@ Prod2ProdUtils.prototype._relateChilds = function() {
 
     function __relate(id) {
         var treeObject = tree.getTreeObject();
-        for (var treeId in treeObject) {
-            if (treeObject[treeId].destid == treeObject[id].sourceid && treeObject[id].ids.indexOf(treeId) == -1) { 
-                treeObject[id].ids.push(treeId);
-                __relate(treeId);
-            }    
+        if (treeObject[id] != undefined)
+        {
+            for (var treeId in treeObject) {
+                if (treeObject[treeId].destid == treeObject[id].sourceid && treeObject[id].ids.indexOf(treeId) == -1) { 
+                    treeObject[id].ids.push(treeId);
+                    __relate(treeId);
+                }    
+            }
         }
     }
 }
@@ -557,11 +563,14 @@ Prod2ProdUtils.prototype._relateParents = function() {
 
     function __relate(id) {
         var treeObject = tree.getTreeObject();
-        for (var treeId in treeObject) {
-            if (treeObject[treeId].sourceid == treeObject[id].destid && treeObject[id].ids.indexOf(treeId) == -1) {   
-                treeObject[id].ids.push(treeId);
-                __relate(treeId);
-            }    
+        if (treeObject[id] != undefined)
+        {
+            for (var treeId in treeObject) {
+                if (treeObject[treeId].sourceid == treeObject[id].destid && treeObject[id].ids.indexOf(treeId) == -1) {   
+                    treeObject[id].ids.push(treeId);
+                    __relate(treeId);
+                }    
+            }
         }
     }
 }
\ No newline at end of file
-- 
GitLab