diff --git a/process/Product_lib/process.js b/process/Product_lib/process.js
index 52762bab20ef5d2d574e85f166617b88183af42a..426130db1e4c12702208784035ab41164faff9d6 100644
--- a/process/Product_lib/process.js
+++ b/process/Product_lib/process.js
@@ -1,525 +1,525 @@
-import("system.util");
-import("system.SQLTYPES");
-import("system.datetime");
-import("system.db");
-import("system.vars");
-import("system.translate");
-import("Util_lib");
-import("Sql_lib");
-import("Keyword_lib");
-
-
-/**
- * utility functions for products
- * Do not create an instance of this!
- * 
- * @class
- */
-function ProductUtils() {}
-
-/**
- * Delivers the currently valid product price 
- * 
- * @param {String} pid ProductID
- * @param {String} buySell possible values: PP, SP
- * 
- * @example productUtils.getCurrentProductPrice(vars.get("$field.PRODUCTID"), "PP")
- * 
- * @return {String} currently valid product price
- */
-ProductUtils.getCurrentProductPrice = function(pid, buySell) {
-    if (pid != undefined && pid != "" && buySell != undefined && buySell != "")
-    {
-        var today = datetime.clearTime(vars.get("sys.date"), "utc");
-        var actualPriceCondition = SqlCondition.begin()
-                    .andPrepare("PRODUCTPRICE.BUYSELL", buySell)
-                    .andPrepare("PRODUCTPRICE.PRODUCT_ID", pid)
-                    .andPrepare("PRODUCTPRICE.CURRENCY", 1) // TODO: warum ist Currency hardgecoded auf 1??
-                    .andPrepare("PRODUCTPRICE.VALID_FROM", today, "# <= ?")
-                    .andSqlCondition(SqlCondition.begin()
-                        .orPrepare("PRODUCTPRICE.VALID_TO", today, "# >= ?")
-                        .or("PRODUCTPRICE.VALID_TO is null"), "1 = 2");
-                            
-        var productPriceData = db.array(db.ROW, actualPriceCondition.buildSelect("select PRICE, CURRENCY from PRODUCTPRICE", "1 = 2", "order by VALID_FROM desc"));
-        
-        if (productPriceData[0] && productPriceData[1])
-            return  productPriceData[0] + " " + KeywordUtils.get("CURRENCY", productPriceData[1])[1];
-        else
-            return "";
-    } else {
-        throw new Error(); // TODO: add message
-    }
-}
-
-/**
- * Delivers the stock
- * 
- * @param {String} pid ProductID
- * 
- * @example productUtils.getStockCount(vars.get("$field.PRODUCTID"))
- * 
- * @return {String} stock count
- */
-ProductUtils.getStockCount = function(pid) {
-    if (pid != undefined && pid != "")
-    {
-        var sum = db.cell(SqlCondition.begin()
-                                      .andPrepare("STOCK.PRODUCT_ID", pid)
-                                      .buildSelect("select sum(QUANTITY * IN_OUT) from STOCK"));
-        
-        if (sum == "")
-            sum = "0";
-
-        return sum;
-    }
-    else
-    {
-        throw new Error();//TODO: add message
-    }
-}
-
-/**
- * Delivers metadata and price lists of the passed product. 
- * If parameter "priceListFilter" is passed valid price lists and the 
- * current price list to use for offer/order are delivered.
- * 
- * @param {String} pid req ProductID
- * @param {Object} priceListFilter opt { currency: "currencyValue", quantity: "quantityValue", relationId: "relationIdValue (for custom price lists)" }
- * 
- * @example //Product_entity, Field: PRODUCT_ID, Process: onValueChange
- *          var pid = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRODUCT_ID"));
- *          var curr = vars.exists("$param.Currency_param") ? vars.get("$param.Currency_param") : "";
- *          var relid = vars.exists("$param.RelationId_param") ? vars.get("$param.RelationId_param") : "";
- *          var pUtils = new ProductUtils();
- *          var PriceListFilter = { currency: curr, quantity: vars.get("$field.QUANTITY"), relationId: relid };
- *          var ProductDetails = pUtils.getProductDetails(pid, PriceListFilter);
- * 
- * @return {Object} { <br>
- *                   productId: "productid" <br>
- *                   , productName: "product name" <br>
- *                   , groupCode: "keyvalue of keyword 'GROUPCODE'" <br>
- *                   , unit: "keyvalue of keyword 'UNIT'" <br>
- *                   , PriceLists: {$pricelistid$ { <br>
- *                          priceListId: "pricelistid" <br>
- *                          , relationId: "relationid" when filled -> custom price list <br>
- *                          , priceList: "keyvalue of keyword 'PRICELIST'" <br>
- *                          , price: "price" <br>
- *                          , vat: "vat" <br>
- *                          , validFrom: TIMESTAMP <br>
- *                          , validTo: TIMESTAMP <br>
- *                          , buySell: "SP" / "PP" <br>
- *                          , fromQuantity: "fromquantity" <br>
- *                          , currency: "keyvalue of keyword 'CURRENCY'" <br>
- *                      } } <br>
- *                   , CurrentValidPriceLists: {$pricelistid$ { <br>
- *                          priceListId: "pricelistid" <br>
- *                          , relationId: "relationid" when filled -> custom price list <br>
- *                          , priceList: "keyvalue of keyword 'PRICELIST'" <br>
- *                          , price: "price" <br>
- *                          , vat: "vat" <br>
- *                          , validFrom: TIMESTAMP <br>
- *                          , validTo: TIMESTAMP <br>
- *                          , buySell: "SP" / "PP" <br>
- *                          , fromQuantity: "fromquantity" <br>
- *                          , currency: "keyvalue of keyword 'CURRENCY'" <br>
- *                      } } <br>
- *                   , PriceListToUse: {$pricelistid$ { <br>
- *                          priceListId: "pricelistid" <br>
- *                          , relationId: "relationid" when filled -> custom price list <br>
- *                          , priceList: "keyvalue of keyword 'PRICELIST'" <br>
- *                          , price: "price" <br>
- *                          , vat: "vat" <br>
- *                          , validFrom: TIMESTAMP <br>
- *                          , validTo: TIMESTAMP <br>
- *                          , buySell: "SP" / "PP" <br>
- *                          , fromQuantity: "fromquantity" <br>
- *                          , currency: "keyvalue of keyword 'CURRENCY'" <br>
- *                      } } <br>
- *               }
- */
-ProductUtils.getProductDetails = function(pid, priceListFilter)
-{
-    var ProductDetails = {};
-
-    var cols = [];
-    var colsProduct = ["PRODUCT.PRODUCTID", "PRODUCT.PRODUCTNAME", "PRODUCT.GROUPCODEID", "PRODUCT.UNIT"];
-    cols = cols.concat(colsProduct);
-
-    var joins = [];
-    var conditions = ["1=1"];
-    var orderby = ["PRODUCTID"];
-    var sqltypes = [];
-
-    //PriceList (all)
-    var colsPricelistAll = ["allPP.PRODUCTPRICEID", "allPP.RELATION_ID", "allPP.PRICELIST", "allPP.PRICE", "allPP.VAT"
-                        , "allPP.VALID_FROM", "allPP.VALID_TO", "allPP.BUYSELL", "allPP.FROMQUANTITY", "allPP.CURRENCY"];
-
-    cols = cols.concat(colsPricelistAll);
-    joins.push(" left join PRODUCTPRICE allPP on allPP.PRODUCT_ID = PRODUCTID ");
-
-    //PriceList (currently valid)
-    var validPriceLists = false;
-    if (priceListFilter != undefined 
-        && priceListFilter.currency != undefined && priceListFilter.currency != "" 
-        && priceListFilter.quantity != undefined && priceListFilter.quantity != "")
-    {
-        validPriceLists = true;
-        var colsPricelistValid = ["validPP.PRODUCTPRICEID", "validPP.RELATION_ID", "validPP.PRICELIST", "validPP.PRICE", "validPP.VAT"
-                        , "validPP.VALID_FROM", "validPP.VALID_TO", "validPP.BUYSELL", "validPP.FROMQUANTITY", "validPP.CURRENCY"];
-
-        cols = cols.concat(colsPricelistValid);
-        joins.push(" left join PRODUCTPRICE validPP on validPP.PRODUCT_ID = PRODUCTID "
-                    + " and validPP.CURRENCY = " + priceListFilter.currency 
-                    + " and validPP.VALID_FROM <= ? and (validPP.VALID_TO > ? or validPP.VALID_TO is null) "
-                    + " and validPP.FROMQUANTITY <= " + priceListFilter.quantity
-                    + " and (validPP.RELATION_ID = '" + priceListFilter.relationId + "' or (validPP.RELATION_ID is null and validPP.BUYSELL = 'SP')) ");
-
-        sqltypes.push([datetime.date().toString(), SQLTYPES.TIMESTAMP]);
-        sqltypes.push([datetime.date().toString(), SQLTYPES.TIMESTAMP]);
-        orderby = orderby.concat(["validPP.VALID_FROM desc, validPP.FROMQUANTITY desc"]);           
-    }
-
-    var ProductDataSql = "select " + cols.join(", ")
-               + " from PRODUCT "
-               + joins.join(" ")
-               + " where PRODUCTID = '" + pid + "' "
-               + " and " + conditions.join(" and ")
-               + " order by " + orderby.join(", ");
-
-    var ProductData = db.table([ProductDataSql, sqltypes]);
-
-    for (var i = 0; i < ProductData.length; i++)
-    {
-        //Product
-        if (ProductDetails.productId == undefined)
-        {
-            ProductDetails = {
-                            productId: ProductData[i][0]
-                            , productName: ProductData[i][1]
-                            , groupCode: ProductData[i][2]
-                            , unit: ProductData[i][3]
-                            , PriceLists: {}
-                            , CurrentValidPriceLists: {}
-                            , PriceListToUse: null
-                        };
-        }
-        //Pricelist (all)
-        var colIdx = colsProduct.length;
-        if (ProductData[i][colIdx] != "" && ProductDetails.PriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
-        {
-            ProductDetails.PriceLists[ProductData[i][colIdx]] = _getPriceListObject();
-        }
-
-        //Pricelist (currently valid)
-        colIdx = colsProduct.length + colsPricelistAll.length;
-        if (validPriceLists)
-        {
-            if (ProductData[i][colIdx] != "" && ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
-            {
-                ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] = _getPriceListObject();
-            }
-        }
-    }
-
-    if (validPriceLists)
-        ProductDetails.PriceListToUse = _getPriceListToUse(ProductDetails.CurrentValidPriceLists, priceListFilter);
-
-    return ProductDetails;
-
-    function _getPriceListObject() {
-        return {
-                priceListId: ProductData[i][colIdx++]
-                , relationId: ProductData[i][colIdx++]
-                , priceList: ProductData[i][colIdx++]
-                , price: ProductData[i][colIdx++]
-                , vat: ProductData[i][colIdx++]
-                , validFrom: ProductData[i][colIdx++]
-                , validTo: ProductData[i][colIdx++]
-                , buySell: ProductData[i][colIdx++]
-                , fromQuantity: ProductData[i][colIdx++]
-                , currency: ProductData[i][colIdx++]
-        };
-    }
-
-    //price list to use for offer/order
-    function _getPriceListToUse(priceLists, priceListFilter) {
-        for (var list in priceLists) {
-            //custom price (defined in Org -> Conditions)
-            if (priceListFilter.relationId != "" && priceListFilter.relationId == priceLists[list].relationId) {
-                return priceLists[list];
-            }
-            //customer deposited price list (defined by Attribute)
-            if (priceListFilter.priceList != "" && priceListFilter.priceList == priceLists[list].priceList) {
-                return priceLists[list];
-            }
-            //default price list
-            if (priceLists[list].priceList == "1") {
-                return priceLists[list];
-            }
-        }
-
-        //no valid price list found
-        return null;
-    }
-}
-
-/**
- * Checks if there is already an existing price list identical to the passed price list 
- * 
- * @param {String} pid ProductID
- * @param {Object} priceList { <br>
- *                                  priceList: "keyvalue of keyword 'PRICELIST'" <br>
- *                                  , validFrom: TIMESTAMP <br>
- *                                  , validTo: TIMESTAMP <br>
- *                                  , buySell: "SP" / "PP" <br>
- *                                  , fromQuantity: "fromquantity" <br>
- *                                  , currency: "keyvalue of keyword 'CURRENCY'" <br>
- *                             }
- * 
- * @example //Productprice_entity, Field: PRICELIST, Process: onValidation
- *          var pUtils = new ProductUtils();
- *          var priceList = {
- *                          priceList: ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRICELIST"))
- *                          , fromQuantity: vars.get("$field.FROMQUANTITY")
- *                          , buySell: vars.get("$field.BUYSELL")
- *                          , currency: vars.get("$field.CURRENCY")
- *                          , validFrom: vars.get("$field.VALID_FROM")
- *                          , validTo: vars.get("$field.VALID_TO")
- *                      };
- *
- *          var identicalPriceList = pUtils.checkForIndenticalPriceLists(vars.get("$field.PRODUCT_ID"), priceList);
- *          if (identicalPriceList != null)
- *          {
- *              result.string(translate.text("Identical price list found!"));
- *          }
- * 
- * @return {Object | null} null if no identical price list was found, otherwise the found price list
- */
-ProductUtils.checkForIndenticalPriceLists = function(pid, priceList) {
-    var PriceLists = this.getProductDetails(pid).PriceLists;
-
-    for (var pricelist in PriceLists) {
-        //equal price list
-        //equal fromquantity
-        //equal currency
-        //equal pp/sp
-        if (priceList.priceList == PriceLists[pricelist].priceList 
-            && priceList.fromQuantity == PriceLists[pricelist].fromQuantity 
-            && priceList.buySell == PriceLists[pricelist].buySell
-            && priceList.currency == PriceLists[pricelist].currency) {
-            
-            //identical validFrom & validTo
-            // OR NOT [ validFrom_new <= validFrom & validTo_new <= validTo
-            //        OR validFrom_new >= validFrom & validTo_new >= validTo
-            //        OR validFrom_new < validFrom & validTo_new > validTo
-            // ]
-            if (priceList.validFrom == PriceLists[pricelist].validFrom && priceList.validTo == PriceLists[pricelist].validTo
-                || ! (priceList.validFrom <= PriceLists[pricelist].validFrom && priceList.validTo <= PriceLists[pricelist].validTo
-                       || priceList.validFrom >= PriceLists[pricelist].validFrom && priceList.validTo >= PriceLists[pricelist].validTo
-                       || priceList.validFrom < PriceLists[pricelist].validFrom && priceList.validTo > PriceLists[pricelist].validTo)) {
-                //identical price list found
-                return PriceLists[pricelist];
-            }
-        }
-    }
-
-    //no identical price list found
-    return null;        
-}
-
-/**
- * Class containing utility functions for Prod2Prod (Parts list)
- * 
- * @param {String} productId req ProductID
- * 
- * @class
- *
- */
-function Prod2ProdUtils(productId) {    
-    this.productId = productId;
-    this.data;
-}
-
-/**
- * Delivers an Object containing parts list structure for passed product "productId" (Constructor parameter)
- * 
- * @return {Object} { $prod2prodid$ { <br>
- *                       ids: [ Array containing child Prod2ProdIds for passed product "pProductId" (Constructor parameter) ] <br>
- *                       , rowdata: [ "PROD2PRODID", "DEST_ID", "SOURCE_ID", "QUANTITY", "OPTIONAL", "TAKEPRICE" ] from DB-Table PROD2PROD <br>
- *                       , destid: "Parent ProductID" <br>
- *                       , sourceid: "Child ProductID" <br>
- *                       , quantity: "Quantity" <br>
- *                       , optional: "1" = not optional, "0" = optional (for easier calculation) <br>
- *                       , takeprice: "Y" = price, "N" = no price <br>
- *                  } }
- */
-Prod2ProdUtils.prototype.getPartsListObject = function() {
-    return this._relateChilds();
-}
-
-/**
- * Delivers a 2D-Array for RecordContainer of Entity "Prod2prod_entity" 
- * containing parts list for passed product "productId" (Constructor parameter).
- * 
- * It is necessary to generate a specifically UID for the RecordContainer because 
- * the same data record can be listed several times. Therefore the primary key "PROD2PRODID"
- * can not be used for UID because this must be unique.
- * 
- * @return {String[][]} [ ["UID"
- *                    , "PARENTID" (equals "DEST_ID")
- *                    , "PROD2PRODID"
- *                    , "DEST_ID"
- *                    , "SOURCE_ID"
- *                    , "QUANTITY"
- *                    , "OPTIONAL"
- *                    , "TAKEPRICE"
- *                    , "PRODUCTCODE"] ]
- */
-Prod2ProdUtils.prototype.getPartsListForRecordContainer = function() {
-    var ret = [];
-    var childs = this._relateChilds();
-
-    __push(childs.root);
-
-    function __push(obj) {
-        for (var i = 0; i < obj.ids.length; i++) {
-            var rowdata = childs[obj.ids[i]].rowdata;
-            var UID = util.getNewUUID();
-            var PARENTID = childs[obj.ids[i]].destid;
-
-            rowdata = [UID, PARENTID].concat(rowdata);
-            ret.push(rowdata);
-            __push(childs[obj.ids[i]]);
-        }
-    }
-
-    return ret;
-}
-
-/**
-* Delivers an Array containing productids of the parts list 
-* for passed product "productId" (Constructor parameter).
-* 
-* 
-* @return {String[]} [ "SOURCE_ID" ]
-*/
-Prod2ProdUtils.prototype.getPartsListProdIds = function() {
-    var ret = [];
-    var childs = this._relateChilds();
-
-    __push(childs.root);
-
-    return ret;
-
-    function __push(pObj) {
-        for (var i = 0; i < pObj.ids.length; i++) {
-            ret.push(childs[pObj.ids[i]].sourceid);
-            __push(childs[pObj.ids[i]]);
-        }
-    }
-}
-
-/**
-* Delivers an Array containing productids of the parent list
-* for passed product "productId" (Constructor parameter).
-* 
-* 
-* @return {String[]} [ "DEST_ID" ]
-*/
-Prod2ProdUtils.prototype.getParentProdIds = function() {
-    var ret = [];
-    var parents = this._relateParents();
-
-    __push(parents.root);
-
-    return ret;
-
-    function __push(pObj) {
-        for (var i = 0; i < pObj.ids.length; i++) {
-            ret.push(parents[pObj.ids[i]].destid);
-            __push(parents[pObj.ids[i]]);
-        }
-    }
-}
-
-/** 
-* Function to initalize class variable "data" containing complete Prod2Prod-Data.<br>
-* It guarantees a unique load of data per instance.
-*
-* @ignore
-*/
-Prod2ProdUtils.prototype._initProd2ProdData = function() {
-    if (this.data == undefined) {
-        var sqlStr  = "select PROD2PRODID, DEST_ID, SOURCE_ID, QUANTITY, OPTIONAL, TAKEPRICE, PRODUCTCODE "
-                    + "from PROD2PROD join PRODUCT on PROD2PROD.SOURCE_ID = PRODUCTID "
-                    + "order by PRODUCTCODE ";
-
-        this.data = db.table(sqlStr);
-    }
-}
-
-/* object tree to relate products by DEST_ID / SOURCE_ID.
- * 
- **/
-Prod2ProdUtils.prototype._buildTree = function(supervised) {
-    this._initProd2ProdData();
-
-    var tree = { root: {ids: [], sourceid: this.productId } };
-
-    if (supervised)
-        tree = { root: {ids: [], destid: this.productId } };
-
-    for (var i = 0; i < this.data.length; i++) {
-        var prod2prodid = this.data[i][0];
-        if (tree[prod2prodid] == undefined) {
-            tree[prod2prodid] = {
-                ids: [] 
-                , rowdata: this.data[i].slice(0)//copy to get NativeArray for concatenation
-                , destid: this.data[i][1]
-                , sourceid: this.data[i][2] 
-                , quantity: this.data[i][3]
-                , optional: this.data[i][4]
-                , takeprice: this.data[i][5]
-                , productcode: this.data[i][6]
-            };
-        }
-    }
-
-    return tree;
-}
-
-Prod2ProdUtils.prototype._relateChilds = function() {
-    var tree = this._buildTree(false);
-
-    __relate("root");
-
-    return tree;
-
-
-    function __relate(id) {
-        for (var treeId in tree) {
-            if (tree[treeId].destid == tree[id].sourceid && tree[id].ids.indexOf(treeId) == -1) {   
-                tree[id].ids.push(treeId);
-                __relate(treeId);
-            }    
-        }
-    }
-}
-
-Prod2ProdUtils.prototype._relateParents = function() {
-    var tree = this._buildTree(true);
-
-    __relate("root");
-
-    return tree;
-
-
-    function __relate(id) {
-        for (var treeId in tree) {
-            if (tree[treeId].sourceid == tree[id].destid && tree[id].ids.indexOf(treeId) == -1) {   
-                tree[id].ids.push(treeId);
-                __relate(treeId);
-            }    
-        }
-    }
+import("system.util");
+import("system.SQLTYPES");
+import("system.datetime");
+import("system.db");
+import("system.vars");
+import("system.translate");
+import("Util_lib");
+import("Sql_lib");
+import("Keyword_lib");
+
+
+/**
+ * utility functions for products
+ * Do not create an instance of this!
+ * 
+ * @class
+ */
+function ProductUtils() {}
+
+/**
+ * Delivers the currently valid product price 
+ * 
+ * @param {String} pid ProductID
+ * @param {String} buySell possible values: PP, SP
+ * 
+ * @example productUtils.getCurrentProductPrice(vars.get("$field.PRODUCTID"), "PP")
+ * 
+ * @return {String} currently valid product price
+ */
+ProductUtils.getCurrentProductPrice = function(pid, buySell) {
+    if (pid != undefined && pid != "" && buySell != undefined && buySell != "")
+    {
+        var today = datetime.clearTime(vars.get("sys.date"), "utc");
+        var actualPriceCondition = SqlCondition.begin()
+                    .andPrepare("PRODUCTPRICE.BUYSELL", buySell)
+                    .andPrepare("PRODUCTPRICE.PRODUCT_ID", pid)
+                    .andPrepare("PRODUCTPRICE.CURRENCY", 1) // TODO: warum ist Currency hardgecoded auf 1??
+                    .andPrepare("PRODUCTPRICE.VALID_FROM", today, "# <= ?")
+                    .andSqlCondition(SqlCondition.begin()
+                        .orPrepare("PRODUCTPRICE.VALID_TO", today, "# >= ?")
+                        .or("PRODUCTPRICE.VALID_TO is null"), "1 = 2");
+                            
+        var productPriceData = db.array(db.ROW, actualPriceCondition.buildSelect("select PRICE, CURRENCY from PRODUCTPRICE", "1 = 2", "order by VALID_FROM desc"));
+        
+        if (productPriceData[0] && productPriceData[1])
+            return  productPriceData[0] + " " + KeywordUtils.get("CURRENCY", productPriceData[1])[1];
+        else
+            return "";
+    } else {
+        throw new Error(); // TODO: add message
+    }
+}
+
+/**
+ * Delivers the stock
+ * 
+ * @param {String} pid ProductID
+ * 
+ * @example productUtils.getStockCount(vars.get("$field.PRODUCTID"))
+ * 
+ * @return {String} stock count
+ */
+ProductUtils.getStockCount = function(pid) {
+    if (pid != undefined && pid != "")
+    {
+        var sum = db.cell(SqlCondition.begin()
+                                      .andPrepare("STOCK.PRODUCT_ID", pid)
+                                      .buildSelect("select sum(QUANTITY * IN_OUT) from STOCK"));
+        
+        if (sum == "")
+            sum = "0";
+
+        return sum;
+    }
+    else
+    {
+        throw new Error();//TODO: add message
+    }
+}
+
+/**
+ * Delivers metadata and price lists of the passed product. 
+ * If parameter "priceListFilter" is passed valid price lists and the 
+ * current price list to use for offer/order are delivered.
+ * 
+ * @param {String} pid req ProductID
+ * @param {Object} priceListFilter opt { currency: "currencyValue", quantity: "quantityValue", relationId: "relationIdValue (for custom price lists)" }
+ * 
+ * @example //Product_entity, Field: PRODUCT_ID, Process: onValueChange
+ *          var pid = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRODUCT_ID"));
+ *          var curr = vars.exists("$param.Currency_param") ? vars.get("$param.Currency_param") : "";
+ *          var relid = vars.exists("$param.RelationId_param") ? vars.get("$param.RelationId_param") : "";
+ *          var pUtils = new ProductUtils();
+ *          var PriceListFilter = { currency: curr, quantity: vars.get("$field.QUANTITY"), relationId: relid };
+ *          var ProductDetails = pUtils.getProductDetails(pid, PriceListFilter);
+ * 
+ * @return {Object} { <br>
+ *                   productId: "productid" <br>
+ *                   , productName: "product name" <br>
+ *                   , groupCode: "keyvalue of keyword 'GROUPCODE'" <br>
+ *                   , unit: "keyvalue of keyword 'UNIT'" <br>
+ *                   , PriceLists: {$pricelistid$ { <br>
+ *                          priceListId: "pricelistid" <br>
+ *                          , relationId: "relationid" when filled -> custom price list <br>
+ *                          , priceList: "keyvalue of keyword 'PRICELIST'" <br>
+ *                          , price: "price" <br>
+ *                          , vat: "vat" <br>
+ *                          , validFrom: TIMESTAMP <br>
+ *                          , validTo: TIMESTAMP <br>
+ *                          , buySell: "SP" / "PP" <br>
+ *                          , fromQuantity: "fromquantity" <br>
+ *                          , currency: "keyvalue of keyword 'CURRENCY'" <br>
+ *                      } } <br>
+ *                   , CurrentValidPriceLists: {$pricelistid$ { <br>
+ *                          priceListId: "pricelistid" <br>
+ *                          , relationId: "relationid" when filled -> custom price list <br>
+ *                          , priceList: "keyvalue of keyword 'PRICELIST'" <br>
+ *                          , price: "price" <br>
+ *                          , vat: "vat" <br>
+ *                          , validFrom: TIMESTAMP <br>
+ *                          , validTo: TIMESTAMP <br>
+ *                          , buySell: "SP" / "PP" <br>
+ *                          , fromQuantity: "fromquantity" <br>
+ *                          , currency: "keyvalue of keyword 'CURRENCY'" <br>
+ *                      } } <br>
+ *                   , PriceListToUse: {$pricelistid$ { <br>
+ *                          priceListId: "pricelistid" <br>
+ *                          , relationId: "relationid" when filled -> custom price list <br>
+ *                          , priceList: "keyvalue of keyword 'PRICELIST'" <br>
+ *                          , price: "price" <br>
+ *                          , vat: "vat" <br>
+ *                          , validFrom: TIMESTAMP <br>
+ *                          , validTo: TIMESTAMP <br>
+ *                          , buySell: "SP" / "PP" <br>
+ *                          , fromQuantity: "fromquantity" <br>
+ *                          , currency: "keyvalue of keyword 'CURRENCY'" <br>
+ *                      } } <br>
+ *               }
+ */
+ProductUtils.getProductDetails = function(pid, priceListFilter)
+{
+    var ProductDetails = {};
+
+    var cols = [];
+    var colsProduct = ["PRODUCT.PRODUCTID", "PRODUCT.PRODUCTNAME", "PRODUCT.GROUPCODEID", "PRODUCT.UNIT"];
+    cols = cols.concat(colsProduct);
+
+    var joins = [];
+    var conditions = ["1=1"];
+    var orderby = ["PRODUCTID"];
+    var sqltypes = [];
+
+    //PriceList (all)
+    var colsPricelistAll = ["allPP.PRODUCTPRICEID", "allPP.RELATION_ID", "allPP.PRICELIST", "allPP.PRICE", "allPP.VAT"
+                        , "allPP.VALID_FROM", "allPP.VALID_TO", "allPP.BUYSELL", "allPP.FROMQUANTITY", "allPP.CURRENCY"];
+
+    cols = cols.concat(colsPricelistAll);
+    joins.push(" left join PRODUCTPRICE allPP on allPP.PRODUCT_ID = PRODUCTID ");
+
+    //PriceList (currently valid)
+    var validPriceLists = false;
+    if (priceListFilter != undefined 
+        && priceListFilter.currency != undefined && priceListFilter.currency != "" 
+        && priceListFilter.quantity != undefined && priceListFilter.quantity != "")
+    {
+        validPriceLists = true;
+        var colsPricelistValid = ["validPP.PRODUCTPRICEID", "validPP.RELATION_ID", "validPP.PRICELIST", "validPP.PRICE", "validPP.VAT"
+                        , "validPP.VALID_FROM", "validPP.VALID_TO", "validPP.BUYSELL", "validPP.FROMQUANTITY", "validPP.CURRENCY"];
+
+        cols = cols.concat(colsPricelistValid);
+        joins.push(" left join PRODUCTPRICE validPP on validPP.PRODUCT_ID = PRODUCTID "
+                    + " and validPP.CURRENCY = " + priceListFilter.currency 
+                    + " and validPP.VALID_FROM <= ? and (validPP.VALID_TO > ? or validPP.VALID_TO is null) "
+                    + " and validPP.FROMQUANTITY <= " + priceListFilter.quantity
+                    + " and (validPP.RELATION_ID = '" + priceListFilter.relationId + "' or (validPP.RELATION_ID is null and validPP.BUYSELL = 'SP')) ");
+
+        sqltypes.push([datetime.date().toString(), SQLTYPES.TIMESTAMP]);
+        sqltypes.push([datetime.date().toString(), SQLTYPES.TIMESTAMP]);
+        orderby = orderby.concat(["validPP.VALID_FROM desc, validPP.FROMQUANTITY desc"]);           
+    }
+
+    var ProductDataSql = "select " + cols.join(", ")
+               + " from PRODUCT "
+               + joins.join(" ")
+               + " where PRODUCTID = '" + pid + "' "
+               + " and " + conditions.join(" and ")
+               + " order by " + orderby.join(", ");
+
+    var ProductData = db.table([ProductDataSql, sqltypes]);
+
+    for (var i = 0; i < ProductData.length; i++)
+    {
+        //Product
+        if (ProductDetails.productId == undefined)
+        {
+            ProductDetails = {
+                            productId: ProductData[i][0]
+                            , productName: ProductData[i][1]
+                            , groupCode: ProductData[i][2]
+                            , unit: ProductData[i][3]
+                            , PriceLists: {}
+                            , CurrentValidPriceLists: {}
+                            , PriceListToUse: null
+                        };
+        }
+        //Pricelist (all)
+        var colIdx = colsProduct.length;
+        if (ProductData[i][colIdx] != "" && ProductDetails.PriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
+        {
+            ProductDetails.PriceLists[ProductData[i][colIdx]] = _getPriceListObject();
+        }
+
+        //Pricelist (currently valid)
+        colIdx = colsProduct.length + colsPricelistAll.length;
+        if (validPriceLists)
+        {
+            if (ProductData[i][colIdx] != "" && ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
+            {
+                ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] = _getPriceListObject();
+            }
+        }
+    }
+
+    if (validPriceLists)
+        ProductDetails.PriceListToUse = _getPriceListToUse(ProductDetails.CurrentValidPriceLists, priceListFilter);
+
+    return ProductDetails;
+
+    function _getPriceListObject() {
+        return {
+                priceListId: ProductData[i][colIdx++]
+                , relationId: ProductData[i][colIdx++]
+                , priceList: ProductData[i][colIdx++]
+                , price: ProductData[i][colIdx++]
+                , vat: ProductData[i][colIdx++]
+                , validFrom: ProductData[i][colIdx++]
+                , validTo: ProductData[i][colIdx++]
+                , buySell: ProductData[i][colIdx++]
+                , fromQuantity: ProductData[i][colIdx++]
+                , currency: ProductData[i][colIdx++]
+        };
+    }
+
+    //price list to use for offer/order
+    function _getPriceListToUse(priceLists, priceListFilter) {
+        for (var list in priceLists) {
+            //custom price (defined in Org -> Conditions)
+            if (priceListFilter.relationId != "" && priceListFilter.relationId == priceLists[list].relationId) {
+                return priceLists[list];
+            }
+            //customer deposited price list (defined by Attribute)
+            if (priceListFilter.priceList != "" && priceListFilter.priceList == priceLists[list].priceList) {
+                return priceLists[list];
+            }
+            //default price list
+            if (priceLists[list].priceList == "1") {
+                return priceLists[list];
+            }
+        }
+
+        //no valid price list found
+        return null;
+    }
+}
+
+/**
+ * Checks if there is already an existing price list identical to the passed price list 
+ * 
+ * @param {String} pid ProductID
+ * @param {Object} priceList { <br>
+ *                                  priceList: "keyvalue of keyword 'PRICELIST'" <br>
+ *                                  , validFrom: TIMESTAMP <br>
+ *                                  , validTo: TIMESTAMP <br>
+ *                                  , buySell: "SP" / "PP" <br>
+ *                                  , fromQuantity: "fromquantity" <br>
+ *                                  , currency: "keyvalue of keyword 'CURRENCY'" <br>
+ *                             }
+ * 
+ * @example //Productprice_entity, Field: PRICELIST, Process: onValidation
+ *          var pUtils = new ProductUtils();
+ *          var priceList = {
+ *                          priceList: ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRICELIST"))
+ *                          , fromQuantity: vars.get("$field.FROMQUANTITY")
+ *                          , buySell: vars.get("$field.BUYSELL")
+ *                          , currency: vars.get("$field.CURRENCY")
+ *                          , validFrom: vars.get("$field.VALID_FROM")
+ *                          , validTo: vars.get("$field.VALID_TO")
+ *                      };
+ *
+ *          var identicalPriceList = pUtils.checkForIndenticalPriceLists(vars.get("$field.PRODUCT_ID"), priceList);
+ *          if (identicalPriceList != null)
+ *          {
+ *              result.string(translate.text("Identical price list found!"));
+ *          }
+ * 
+ * @return {Object | null} null if no identical price list was found, otherwise the found price list
+ */
+ProductUtils.checkForIndenticalPriceLists = function(pid, priceList) {
+    var PriceLists = this.getProductDetails(pid).PriceLists;
+
+    for (var pricelist in PriceLists) {
+        //equal price list
+        //equal fromquantity
+        //equal currency
+        //equal pp/sp
+        if (priceList.priceList == PriceLists[pricelist].priceList 
+            && priceList.fromQuantity == PriceLists[pricelist].fromQuantity 
+            && priceList.buySell == PriceLists[pricelist].buySell
+            && priceList.currency == PriceLists[pricelist].currency) {
+            
+            //identical validFrom & validTo
+            // OR NOT [ validFrom_new <= validFrom & validTo_new <= validTo
+            //        OR validFrom_new >= validFrom & validTo_new >= validTo
+            //        OR validFrom_new < validFrom & validTo_new > validTo
+            // ]
+            if (priceList.validFrom == PriceLists[pricelist].validFrom && priceList.validTo == PriceLists[pricelist].validTo
+                || ! (priceList.validFrom <= PriceLists[pricelist].validFrom && priceList.validTo <= PriceLists[pricelist].validTo
+                       || priceList.validFrom >= PriceLists[pricelist].validFrom && priceList.validTo >= PriceLists[pricelist].validTo
+                       || priceList.validFrom < PriceLists[pricelist].validFrom && priceList.validTo > PriceLists[pricelist].validTo)) {
+                //identical price list found
+                return PriceLists[pricelist];
+            }
+        }
+    }
+
+    //no identical price list found
+    return null;        
+}
+
+/**
+ * Class containing utility functions for Prod2Prod (Parts list)
+ * 
+ * @param {String} productId req ProductID
+ * 
+ * @class
+ *
+ */
+function Prod2ProdUtils(productId) {    
+    this.productId = productId;
+    this.data = undefined;
+}
+
+/**
+ * Delivers an Object containing parts list structure for passed product "productId" (Constructor parameter)
+ * 
+ * @return {Object} { $prod2prodid$ { <br>
+ *                       ids: [ Array containing child Prod2ProdIds for passed product "pProductId" (Constructor parameter) ] <br>
+ *                       , rowdata: [ "PROD2PRODID", "DEST_ID", "SOURCE_ID", "QUANTITY", "OPTIONAL", "TAKEPRICE" ] from DB-Table PROD2PROD <br>
+ *                       , destid: "Parent ProductID" <br>
+ *                       , sourceid: "Child ProductID" <br>
+ *                       , quantity: "Quantity" <br>
+ *                       , optional: "1" = not optional, "0" = optional (for easier calculation) <br>
+ *                       , takeprice: "Y" = price, "N" = no price <br>
+ *                  } }
+ */
+Prod2ProdUtils.prototype.getPartsListObject = function() {
+    return this._relateChilds();
+}
+
+/**
+ * Delivers a 2D-Array for RecordContainer of Entity "Prod2prod_entity" 
+ * containing parts list for passed product "productId" (Constructor parameter).
+ * 
+ * It is necessary to generate a specifically UID for the RecordContainer because 
+ * the same data record can be listed several times. Therefore the primary key "PROD2PRODID"
+ * can not be used for UID because this must be unique.
+ * 
+ * @return {String[][]} [ ["UID"
+ *                    , "PARENTID" (equals "DEST_ID")
+ *                    , "PROD2PRODID"
+ *                    , "DEST_ID"
+ *                    , "SOURCE_ID"
+ *                    , "QUANTITY"
+ *                    , "OPTIONAL"
+ *                    , "TAKEPRICE"
+ *                    , "PRODUCTCODE"] ]
+ */
+Prod2ProdUtils.prototype.getPartsListForRecordContainer = function() {
+    var ret = [];
+    var childs = this._relateChilds();
+
+    __push(childs.root);
+
+    function __push(obj) {
+        for (var i = 0; i < obj.ids.length; i++) {
+            var rowdata = childs[obj.ids[i]].rowdata;
+            var UID = util.getNewUUID();
+            var PARENTID = childs[obj.ids[i]].destid;
+
+            rowdata = [UID, PARENTID].concat(rowdata);
+            ret.push(rowdata);
+            __push(childs[obj.ids[i]]);
+        }
+    }
+
+    return ret;
+}
+
+/**
+* Delivers an Array containing productids of the parts list 
+* for passed product "productId" (Constructor parameter).
+* 
+* 
+* @return {String[]} [ "SOURCE_ID" ]
+*/
+Prod2ProdUtils.prototype.getPartsListProdIds = function() {
+    var ret = [];
+    var childs = this._relateChilds();
+
+    __push(childs.root);
+
+    return ret;
+
+    function __push(pObj) {
+        for (var i = 0; i < pObj.ids.length; i++) {
+            ret.push(childs[pObj.ids[i]].sourceid);
+            __push(childs[pObj.ids[i]]);
+        }
+    }
+}
+
+/**
+* Delivers an Array containing productids of the parent list
+* for passed product "productId" (Constructor parameter).
+* 
+* 
+* @return {String[]} [ "DEST_ID" ]
+*/
+Prod2ProdUtils.prototype.getParentProdIds = function() {
+    var ret = [];
+    var parents = this._relateParents();
+
+    __push(parents.root);
+
+    return ret;
+
+    function __push(pObj) {
+        for (var i = 0; i < pObj.ids.length; i++) {
+            ret.push(parents[pObj.ids[i]].destid);
+            __push(parents[pObj.ids[i]]);
+        }
+    }
+}
+
+/** 
+* Function to initalize class variable "data" containing complete Prod2Prod-Data.<br>
+* It guarantees a unique load of data per instance.
+*
+* @ignore
+*/
+Prod2ProdUtils.prototype._initProd2ProdData = function() {
+    if (this.data == undefined) {
+        var sqlStr  = "select PROD2PRODID, DEST_ID, SOURCE_ID, QUANTITY, OPTIONAL, TAKEPRICE, PRODUCTCODE "
+                    + "from PROD2PROD join PRODUCT on PROD2PROD.SOURCE_ID = PRODUCTID "
+                    + "order by PRODUCTCODE ";
+
+        this.data = db.table(sqlStr);
+    }
+}
+
+/* object tree to relate products by DEST_ID / SOURCE_ID.
+ * 
+ **/
+Prod2ProdUtils.prototype._buildTree = function(supervised) {
+    this._initProd2ProdData();
+
+    var tree = { root: {ids: [], sourceid: this.productId } };
+
+    if (supervised)
+        tree = { root: {ids: [], destid: this.productId } };
+
+    for (var i = 0; i < this.data.length; i++) {
+        var prod2prodid = this.data[i][0];
+        if (tree[prod2prodid] == undefined) {
+            tree[prod2prodid] = {
+                ids: [] 
+                , rowdata: this.data[i].slice(0)//copy to get NativeArray for concatenation
+                , destid: this.data[i][1]
+                , sourceid: this.data[i][2] 
+                , quantity: this.data[i][3]
+                , optional: this.data[i][4]
+                , takeprice: this.data[i][5]
+                , productcode: this.data[i][6]
+            };
+        }
+    }
+
+    return tree;
+}
+
+Prod2ProdUtils.prototype._relateChilds = function() {
+    var tree = this._buildTree(false);
+
+    __relate("root");
+
+    return tree;
+
+
+    function __relate(id) {
+        for (var treeId in tree) {
+            if (tree[treeId].destid == tree[id].sourceid && tree[id].ids.indexOf(treeId) == -1) {   
+                tree[id].ids.push(treeId);
+                __relate(treeId);
+            }    
+        }
+    }
+}
+
+Prod2ProdUtils.prototype._relateParents = function() {
+    var tree = this._buildTree(true);
+
+    __relate("root");
+
+    return tree;
+
+
+    function __relate(id) {
+        for (var treeId in tree) {
+            if (tree[treeId].sourceid == tree[id].destid && tree[id].ids.indexOf(treeId) == -1) {   
+                tree[id].ids.push(treeId);
+                __relate(treeId);
+            }    
+        }
+    }
 }
\ No newline at end of file