From a3df894372f816a871a9c2777c43d3b5a06378ae Mon Sep 17 00:00:00 2001
From: Johannes Hoermann <j.hoermann@adito.de>
Date: Thu, 29 Nov 2018 15:24:32 +0100
Subject: [PATCH] Product_lib refactoring

---
 .../entityfields/product_id/onValueChange.js  |   3 +-
 .../entityfields/quantity/onValueChange.js    |   3 +-
 .../currentpurchaseprice/valueProcess.js      |   2 -
 .../currentsalesprice/valueProcess.js         |   2 -
 .../entityfields/pricelist/onValidation.js    |   3 +-
 .../entityfields/stockcount/valueProcess.js   |   4 +-
 others/guide/how to write JDito code.adoc     |   9 +
 process/OfferOrder_lib/process.js             |   6 +-
 process/Product_lib/process.js                | 945 +++++++++---------
 9 files changed, 487 insertions(+), 490 deletions(-)

diff --git a/entity/Offeritem_entity/entityfields/product_id/onValueChange.js b/entity/Offeritem_entity/entityfields/product_id/onValueChange.js
index 7efabfffbaa..76010b13297 100644
--- a/entity/Offeritem_entity/entityfields/product_id/onValueChange.js
+++ b/entity/Offeritem_entity/entityfields/product_id/onValueChange.js
@@ -10,10 +10,9 @@ if(pid != "")
     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);
+    var ProductDetails = ProductUtils.getProductDetails(pid, PriceListFilter);
     
     if(ProductDetails.productId != undefined)
     {
diff --git a/entity/Offeritem_entity/entityfields/quantity/onValueChange.js b/entity/Offeritem_entity/entityfields/quantity/onValueChange.js
index 1bd2ecab5dd..6e32e2ec1c9 100644
--- a/entity/Offeritem_entity/entityfields/quantity/onValueChange.js
+++ b/entity/Offeritem_entity/entityfields/quantity/onValueChange.js
@@ -11,10 +11,9 @@ if(pid != "" && newQuantity != "")
     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: newQuantity, relationId: relid };
     
-    var ProductDetails = pUtils.getProductDetails(pid, PriceListFilter);
+    var ProductDetails = ProductUtils.getProductDetails(pid, PriceListFilter);
     
     if(ProductDetails.productId != undefined && ProductDetails.PriceListToUse != null)
     {
diff --git a/entity/Productprice_entity/entityfields/currentpurchaseprice/valueProcess.js b/entity/Productprice_entity/entityfields/currentpurchaseprice/valueProcess.js
index 352a05d657d..13cf295450b 100644
--- a/entity/Productprice_entity/entityfields/currentpurchaseprice/valueProcess.js
+++ b/entity/Productprice_entity/entityfields/currentpurchaseprice/valueProcess.js
@@ -4,7 +4,5 @@ import("Product_lib");
 
 if(vars.exists("$param.ProductId_param") && vars.get("$param.ProductId_param") != "")
 {
-    var ProductUtils = new ProductUtils();
-
     result.string( ProductUtils.getCurrentProductPrice(vars.get("$param.ProductId_param"), "PP") );
 }
\ No newline at end of file
diff --git a/entity/Productprice_entity/entityfields/currentsalesprice/valueProcess.js b/entity/Productprice_entity/entityfields/currentsalesprice/valueProcess.js
index efdb4d2a649..3ba7d9974be 100644
--- a/entity/Productprice_entity/entityfields/currentsalesprice/valueProcess.js
+++ b/entity/Productprice_entity/entityfields/currentsalesprice/valueProcess.js
@@ -4,7 +4,5 @@ import("Product_lib");
 
 if(vars.exists("$param.ProductId_param") && vars.get("$param.ProductId_param") != "")
 {
-    var ProductUtils = new ProductUtils();
-
     result.string( ProductUtils.getCurrentProductPrice(vars.get("$param.ProductId_param"), "SP") );
 }
\ No newline at end of file
diff --git a/entity/Productprice_entity/entityfields/pricelist/onValidation.js b/entity/Productprice_entity/entityfields/pricelist/onValidation.js
index 280ee4e3e23..281a0788814 100644
--- a/entity/Productprice_entity/entityfields/pricelist/onValidation.js
+++ b/entity/Productprice_entity/entityfields/pricelist/onValidation.js
@@ -5,7 +5,6 @@ import("Product_lib");
 import("Util_lib");
 import("Entity_lib");
 
-var pUtils = new ProductUtils();
 var priceList = {
                 priceList: ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRICELIST"))
                 , fromQuantity: vars.get("$field.FROMQUANTITY")
@@ -15,7 +14,7 @@ var priceList = {
                 , validTo: vars.get("$field.VALID_TO")
             };
 
-var identicalPriceList = pUtils.checkForIndenticalPriceLists(vars.get("$field.PRODUCT_ID"), priceList);
+var identicalPriceList = ProductUtils.checkForIndenticalPriceLists(vars.get("$field.PRODUCT_ID"), priceList);
 if(identicalPriceList != null)
 {
     result.string(translate.text("Identical price list found!"));
diff --git a/entity/Stock_entity/entityfields/stockcount/valueProcess.js b/entity/Stock_entity/entityfields/stockcount/valueProcess.js
index ca5d66d8992..9e9accfc754 100644
--- a/entity/Stock_entity/entityfields/stockcount/valueProcess.js
+++ b/entity/Stock_entity/entityfields/stockcount/valueProcess.js
@@ -4,7 +4,5 @@ import("Product_lib");
 
 if(vars.exists("$param.ProductId_param") && vars.get("$param.ProductId_param") != "")
 {
-    var pUtils = new ProductUtils();
-
-    result.string( pUtils.getStockCount(vars.get("$param.ProductId_param")) );
+    result.string( ProductUtils.getStockCount(vars.get("$param.ProductId_param")) );
 }
\ No newline at end of file
diff --git a/others/guide/how to write JDito code.adoc b/others/guide/how to write JDito code.adoc
index 388d3299ff0..e305c950e91 100644
--- a/others/guide/how to write JDito code.adoc	
+++ b/others/guide/how to write JDito code.adoc	
@@ -135,6 +135,15 @@ SqlCondition.prototype.and = function(cond){<5>
 }
 ----
 
+=== private functions ===
+
+Private functions would be possible but make everything much more complicate.
+So just start your functions / methods name with a _ if you need private methods.
+
+--> do not use functions which start with a _ outside of the class!
+
+----
+
 <1> JS-Doc comment: http://usejsdoc.org/
 <2> use the correct form for optional/required parameters: http://usejsdoc.org/tags-param.html
 <3> examples are useful on more complex functions
diff --git a/process/OfferOrder_lib/process.js b/process/OfferOrder_lib/process.js
index 96bc504cb13..67d5e33fa6f 100644
--- a/process/OfferOrder_lib/process.js
+++ b/process/OfferOrder_lib/process.js
@@ -398,9 +398,7 @@ ItemUtils.prototype.insertPartsList = function(columns, table, productId, assign
     
     var rootProdId = productId;
     if(rootProdId != "")
-    {
-        var pUtils = new ProductUtils();
-        
+    {        
         var p2pUtils = new Prod2ProdUtils(rootProdId);
         var partsList = p2pUtils.getPartsListObject();
         
@@ -428,7 +426,7 @@ ItemUtils.prototype.insertPartsList = function(columns, table, productId, assign
             var p2pid = partsListObj.ids[i];
             var P2pObject = partsList[p2pid];
             var prodid = partsList[p2pid].sourceid;
-            var ProductDetails = pUtils.getProductDetails(prodid, { currency: currency, quantity: P2pObject.quantity, relationId: relationId } )
+            var ProductDetails = ProductUtils.getProductDetails(prodid, { currency: currency, quantity: P2pObject.quantity, relationId: relationId } )
 
             var price = "";
             var vat = "";
diff --git a/process/Product_lib/process.js b/process/Product_lib/process.js
index fd59d1bfe57..44d609e5e9a 100644
--- a/process/Product_lib/process.js
+++ b/process/Product_lib/process.js
@@ -4,548 +4,547 @@ import("system.db");
 import("system.vars");
 import("Util_lib");
 
+
 /**
- * Class containing utility functions for products
+ * utility functions for products
  * 
+ * Do not create an instance of this!
  * @class
- *
  */
-function ProductUtils()
+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 )
 {
-    var that = this;
-    /**
-     * Delivers the currently valid product price 
-     * 
-     * @param pPid {String} ProductID
-     * @param pBuySell {String} possible values: PP, SP
-     * 
-     * @example productUtils.getCurrentProductPrice(vars.get("$field.PRODUCTID"), "PP")
-     * 
-     * @result {String} currently valid product price
-     */
-    this.getCurrentProductPrice = function( pPid, pBuySell )
+    if(pid != undefined && pid != "" && buySell != undefined && buySell != "")
     {
-        if(pPid != undefined && pPid != "" && pBuySell != undefined && pBuySell != "")
-        {
-            var actualpriceSelect = "select PRICE from PRODUCTPRICE \n\
-                                    where BUYSELL = '" + pBuySell + "' and PRODUCT_ID = '" + pPid + "' and CURRENCY = 1 \n\
-                                    and VALID_FROM <= ? and (VALID_TO >= ? or VALID_TO is null) \n\
-                                    order by VALID_FROM desc";
-
-            var today = datetime.today();
-            var sqltypes = [ [today.toString(), SQLTYPES.TIMESTAMP], [today.toString(), SQLTYPES.TIMESTAMP] ];
-            var actualprice = db.cell([actualpriceSelect, sqltypes]);
-
-            if(actualprice != "")
-                return actualprice;
-            else
-                return "0.00";
-        }
+        var actualpriceSelect = "select PRICE from PRODUCTPRICE \n\
+                                where BUYSELL = '" + buySell + "' and PRODUCT_ID = '" + pid + "' and CURRENCY = 1 \n\
+                                and VALID_FROM <= ? and (VALID_TO >= ? or VALID_TO is null) \n\
+                                order by VALID_FROM desc";
+
+        var today = datetime.today();
+        var sqltypes = [ [today.toString(), SQLTYPES.TIMESTAMP], [today.toString(), SQLTYPES.TIMESTAMP] ];
+        var actualprice = db.cell([actualpriceSelect, sqltypes]);
+
+        if(actualprice != "")
+            return actualprice;
         else
-        {
-            throw new Error();//TODO: add message
-        }
+            return "0.00";
     }
-    
-    /**
-     * Delivers the stock
-     * 
-     * @param pPid {String} ProductID
-     * 
-     * @example productUtils.getStockCount(vars.get("$field.PRODUCTID"))
-     * 
-     * @result {String} stock count
-     */
-    this.getStockCount = function( pPid )
+    else
     {
-        if(pPid != undefined && pPid != "")
-        {
-            var sum = db.cell("select sum(QUANTITY * IN_OUT) from STOCK where PRODUCT_ID = '" + pPid + "'");
-            if(sum == "")
-                sum = "0";
-            
-            return sum;
-        }
-        else
-        {
-            throw new Error();//TODO: add message
-        }
+        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("select sum(QUANTITY * IN_OUT) from STOCK where PRODUCT_ID = '" + pid + "'");
+        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} pPriceListFilter 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"]);           
     }
-    
-    /**
-     * Delivers metadata and price lists of the passed product. 
-     * If parameter "pPriceListFilter" is passed valid price lists and the 
-     * current price list to use for offer/order are delivered.
-     * 
-     * @param pPid {String} req ProductID
-     * @param pPriceListFilter {Object} 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>
-     *               }
-     */
-    this.getProductDetails = function( pPid, pPriceListFilter )
+
+    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++)
     {
-        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(pPriceListFilter != undefined 
-            && pPriceListFilter.currency != undefined && pPriceListFilter.currency != "" 
-            && pPriceListFilter.quantity != undefined && pPriceListFilter.quantity != "")
+        //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
         {
-            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 = " + pPriceListFilter.currency 
-                        + " and validPP.VALID_FROM <= ? and (validPP.VALID_TO > ? or validPP.VALID_TO is null) "
-                        + " and validPP.FROMQUANTITY <= " + pPriceListFilter.quantity
-                        + " and (validPP.RELATION_ID = '" + pPriceListFilter.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"]);           
+            ProductDetails.PriceLists[ProductData[i][colIdx]] = _getPriceListObject();
         }
-        
-        var ProductDataSql = "select " + cols.join(", ")
-                   + " from PRODUCT "
-                   + joins.join(" ")
-                   + " where PRODUCTID = '" + pPid + "' "
-                   + " and " + conditions.join(" and ")
-                   + " order by " + orderby.join(", ");
-        
-        var ProductData = db.table([ProductDataSql, sqltypes]);
-        
-        for(var i = 0; i < ProductData.length; i++)
+
+        //Pricelist (currently valid)
+        colIdx = colsProduct.length + colsPricelistAll.length;
+        if(validPriceLists)
         {
-            //Product
-            if(ProductDetails.productId == undefined)
+            if(ProductData[i][colIdx] != "" && ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
             {
-                ProductDetails = {
-                                productId: ProductData[i][0]
-                                , productName: ProductData[i][1]
-                                , groupCode: ProductData[i][2]
-                                , unit: ProductData[i][3]
-                                , PriceLists: {}
-                                , CurrentValidPriceLists: {}
-                                , PriceListToUse: null
-                            };
+                ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] = _getPriceListObject();
             }
-            //Pricelist (all)
-            var colIdx = colsProduct.length;
-            if(ProductData[i][colIdx] != "" && ProductDetails.PriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
+        }
+    }
+
+    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)
             {
-                ProductDetails.PriceLists[ProductData[i][colIdx]] = _getPriceListObject();
+                return priceLists[list];
             }
-           
-            //Pricelist (currently valid)
-            colIdx = colsProduct.length + colsPricelistAll.length;
-            if(validPriceLists)
+            //customer deposited price list (defined by Attribute)
+            if(priceListFilter.priceList != "" && priceListFilter.priceList == priceLists[list].priceList)
             {
-                if(ProductData[i][colIdx] != "" && ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] == undefined) //Pricelist found
-                {
-                    ProductDetails.CurrentValidPriceLists[ProductData[i][colIdx]] = _getPriceListObject();
-                }
+                return priceLists[list];
             }
-        }
-        
-        if(validPriceLists)
-            ProductDetails.PriceListToUse = _getPriceListToUse(ProductDetails.CurrentValidPriceLists, pPriceListFilter);
-            
-        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(pPriceLists, pPriceListFilter)
-        {
-            for(var list in pPriceLists)
+            //default price list
+            if(priceLists[list].priceList == "1")
             {
-                //custom price (defined in Org -> Conditions)
-                if(pPriceListFilter.relationId != "" && pPriceListFilter.relationId == pPriceLists[list].relationId)
-                {
-                    return pPriceLists[list];
-                }
-                //customer deposited price list (defined by Attribute)
-                if(pPriceListFilter.priceList != "" && pPriceListFilter.priceList == pPriceLists[list].priceList)
-                {
-                    return pPriceLists[list];
-                }
-                //default price list
-                if(pPriceLists[list].priceList == "1")
-                {
-                    return pPriceLists[list];
-                }
+                return priceLists[list];
             }
-            
-            //no valid price list found
-            return null;
         }
+
+        //no valid price list found
+        return null;
     }
-    
-    /**
-     * Checks if there is already an existing price list identical to the passed price list 
-     * 
-     * @param pPid {String} req ProductID
-     * @param pPriceList {Object} req { <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
-     */
-    this.checkForIndenticalPriceLists = function(pPid, pPriceList)
+}
+
+/**
+ * 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 = that.getProductDetails(pid).PriceLists;
+
+    for(var pricelist in PriceLists)
     {
-        var PriceLists = that.getProductDetails(pPid).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 )
+
         {
-            //equal price list
-            //equal fromquantity
-            //equal currency
-            //equal pp/sp
-            if( pPriceList.priceList == PriceLists[pricelist].priceList 
-                && pPriceList.fromQuantity == PriceLists[pricelist].fromQuantity 
-                && pPriceList.buySell == PriceLists[pricelist].buySell
-                && pPriceList.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 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( pPriceList.validFrom == PriceLists[pricelist].validFrom && pPriceList.validTo == PriceLists[pricelist].validTo
-                    || ! ( pPriceList.validFrom <= PriceLists[pricelist].validFrom && pPriceList.validTo <= PriceLists[pricelist].validTo
-                           || pPriceList.validFrom >= PriceLists[pricelist].validFrom && pPriceList.validTo >= PriceLists[pricelist].validTo
-                           || pPriceList.validFrom < PriceLists[pricelist].validFrom && pPriceList.validTo > PriceLists[pricelist].validTo ) 
-                )
-                {
-                    //identical price list found
-                    return PriceLists[pricelist];
-                }
+                //identical price list found
+                return PriceLists[pricelist];
             }
         }
-        
-        //no identical price list found
-        return null;        
     }
+
+    //no identical price list found
+    return null;        
 }
 
 /**
  * Class containing utility functions for Prod2Prod (Parts list)
  * 
- * @param pProductId req ProductID
+ * @param {String} productId req ProductID
  * 
  * @class
  *
  */
-function Prod2ProdUtils(pProductId)
+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()
 {
-    var data;
-    
-    /**
-     * Delivers an Object containing parts list structure for passed product "pProductId" (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>
-     *                  } }
-     */
-    this.getPartsListObject = function()
-    {
-        return _relateChilds();
-    }
-    
-    /**
-     * Delivers a 2D-Array for RecordContainer of Entity "Prod2prod_entity" 
-     * containing parts list for passed product "pProductId" (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 {[ [] ]} [ ["UID"
-     *                    , "PARENTID" (equals "DEST_ID")
-     *                    , "PROD2PRODID"
-     *                    , "DEST_ID"
-     *                    , "SOURCE_ID"
-     *                    , "QUANTITY"
-     *                    , "OPTIONAL"
-     *                    , "TAKEPRICE"] ]
-     */
-    this.getPartsListForRecordContainer = 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 {[ [] ]} [ ["UID"
+ *                    , "PARENTID" (equals "DEST_ID")
+ *                    , "PROD2PRODID"
+ *                    , "DEST_ID"
+ *                    , "SOURCE_ID"
+ *                    , "QUANTITY"
+ *                    , "OPTIONAL"
+ *                    , "TAKEPRICE"] ]
+ */
+Prod2ProdUtils.prototype.getPartsListForRecordContainer = function()
+{
+    var ret = [];
+    var childs = this._relateChilds();
+
+    __push(childs.root);
+
+    function __push(obj)
     {
-        var ret = [];
-        var childs = _relateChilds();
-        
-        __push(childs.root);
-        
-        function __push(pObj)
+        for(var i = 0; i < obj.ids.length; i++)
         {
-            for(var i = 0; i < pObj.ids.length; i++)
-            {
-                var rowdata = childs[pObj.ids[i]].rowdata;
-                var UID = util.getNewUUID();
-                var PARENTID = childs[pObj.ids[i]].destid;
+            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[pObj.ids[i]] );
-            }
+            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 "pProductId" (Constructor parameter).
-    * 
-    * 
-    * @return {[]} [ "SOURCE_ID" ]
-    */
-    this.getPartsListProdIds = function()
+
+    return ret;
+}
+
+/**
+* Delivers an Array containing productids of the parts list 
+* for passed product "productId" (Constructor parameter).
+* 
+* 
+* @return {[]} [ "SOURCE_ID" ]
+*/
+Prod2ProdUtils.prototype.getPartsListProdIds = function()
+{
+    var ret = [];
+    var childs = this._relateChilds();
+
+    __push(childs.root);
+
+    return ret;
+
+    function __push(pObj)
     {
-        var ret = [];
-        var childs = _relateChilds();
-        
-        __push(childs.root);
-        
-        return ret;
-        
-        function __push(pObj)
+        for(var i = 0; i < pObj.ids.length; i++)
         {
-            for(var i = 0; i < pObj.ids.length; i++)
-            {
-                ret.push(childs[pObj.ids[i]].sourceid);
-                __push( childs[pObj.ids[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 "pProductId" (Constructor parameter).
-    * 
-    * 
-    * @return {[]} [ "DEST_ID" ]
-    */
-    this.getParentProdIds = function()
+}
+
+/**
+* Delivers an Array containing productids of the parent list
+* for passed product "productId" (Constructor parameter).
+* 
+* 
+* @return {[]} [ "DEST_ID" ]
+*/
+Prod2ProdUtils.prototype.getParentProdIds = function()
+{
+    var ret = [];
+    var parents = this._relateParents();
+
+    __push(parents.root);
+
+    return ret;
+
+    function __push(pObj)
     {
-        var ret = [];
-        var parents = _relateParents();
-        
-        __push(parents.root);
-
-        return ret;
-        
-        function __push(pObj)
+        for(var i = 0; i < pObj.ids.length; i++)
         {
-            for(var i = 0; i < pObj.ids.length; i++)
-            {
-                ret.push(parents[pObj.ids[i]].destid);
-                __push( parents[pObj.ids[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.
-    *
-    */
-    function _initProd2ProdData()
+}
+
+/** 
+* Function to initalize class variable "data" containing complete Prod2Prod-Data.<br>
+* It guarantees a unique load of data per instance.
+*
+*/
+Prod2ProdUtils.prototype._initProd2ProdData = function()
+{
+    if(this.data == undefined)
     {
-        if(data == undefined)
-        {
-            var sqlStr  = "select PROD2PRODID, DEST_ID, SOURCE_ID, QUANTITY, OPTIONAL, TAKEPRICE "
-                        + "from PROD2PROD join PRODUCT on PROD2PROD.SOURCE_ID = PRODUCTID "
-                        + "order by PRODUCTCODE ";
+        var sqlStr  = "select PROD2PRODID, DEST_ID, SOURCE_ID, QUANTITY, OPTIONAL, TAKEPRICE "
+                    + "from PROD2PROD join PRODUCT on PROD2PROD.SOURCE_ID = PRODUCTID "
+                    + "order by PRODUCTCODE ";
 
-            data = db.table(sqlStr);
-        }
+        this.data = db.table(sqlStr);
     }
-    
-    /* object tree to relate products by DEST_ID / SOURCE_ID.
-     * 
-     **/
-    function _buildTree(pSupervised)
-    {
-        _initProd2ProdData();
+}
+
+/* object tree to relate products by DEST_ID / SOURCE_ID.
+ * 
+ **/
+Prod2ProdUtils.prototype._buildTree = function(supervised)
+{
+    this._initProd2ProdData();
 
-        var tree = { root: {ids: [], sourceid: pProductId } };
-        
-        if(pSupervised)
-            tree = { root: {ids: [], destid: pProductId } };
+    var tree = { root: {ids: [], sourceid: this.productId } };
 
-        for (var i = 0; i < data.length; i++)
+    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 )   
         {
-            var prod2prodid = data[i][0];
-            if ( tree[prod2prodid] == undefined )   
-            {
-                tree[prod2prodid] = {
-                    ids: [] 
-                    , rowdata: data[i].slice(0)//copy to get NativeArray for concatenation
-                    , destid: data[i][1]
-                    , sourceid: data[i][2] 
-                    , quantity: data[i][3]
-                    , optional: data[i][4]
-                    , takeprice: data[i][5]
-                };
-            }
+            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]
+            };
         }
-        
-        return tree;
     }
-    
-    function _relateChilds()
+
+    return tree;
+}
+
+Prod2ProdUtils.prototype._relateChilds = function()
+{
+    var tree = this._buildTree(false);
+
+    __relate("root");
+
+    return tree;
+
+
+    function __relate(id)
     {
-        var tree = _buildTree(false);
-
-        __relate("root");
-        
-        return tree;
-        
-        
-        function __relate(pID)
+        for ( var treeId in tree )
         {
-            for ( var id in tree )
-            {
-                if ( tree[id].destid == tree[pID].sourceid && tree[pID].ids.indexOf(id) == -1 )
-                {   
-                    tree[pID].ids.push(id);
-                    __relate(id);
-                }    
-            }
+            if ( tree[treeId].destid == tree[id].sourceid && tree[id].ids.indexOf(treeId) == -1 )
+            {   
+                tree[id].ids.push(treeId);
+                __relate(treeId);
+            }    
         }
     }
-    
-    function _relateParents()
-    {
-        var tree = _buildTree(true);
+}
+
+Prod2ProdUtils.prototype._relateParents = function()
+{
+    var tree = this._buildTree(true);
 
-        __relate("root");
-        
-        return tree;
+    __relate("root");
 
+    return tree;
 
-        function __relate(pID)
+
+    function __relate(id)
+    {
+        for ( var treeId in tree )
         {
-            for ( var id in tree )
-            {
-                if ( tree[id].sourceid == tree[pID].destid && tree[pID].ids.indexOf(id) == -1 )
-                {   
-                    tree[pID].ids.push(id);
-                    __relate(id);
-                }    
-            }
+            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
-- 
GitLab