Skip to content
Snippets Groups Projects
process.js 21.26 KiB
import("system.logging");
import("system.SQLTYPES");
import("system.datetime");
import("system.db");
import("system.vars");
import("Util_lib");

/**
 * Class containing utility functions for products
 * 
 * @class
 *
 */
function ProductUtils()
{
    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(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";
        }
        else
        {
            throw new Error();//TODO: add message
        }
    }
    
    /**
     * Delivers the stock
     * 
     * @param pPid {String} ProductID
     * 
     * @example productUtils.getStockCount(vars.get("$field.PRODUCTID"))
     * 
     * @result {String} stock count
     */
    this.getStockCount = function( pPid )
    {
        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
        }
    }
    
    /**
     * 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 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 != "")
        {
            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"]);           
        }
        
        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++)
        {
            //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, 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)
            {
                //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];
                }
            }
            
            //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)
    {
        var PriceLists = that.getProductDetails(pPid).PriceLists;
        
        for(var pricelist in PriceLists)
        {
            //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( 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];
                }
            }
        }
        
        //no identical price list found
        return null;        
    }
}

/**
 * Class containing utility functions for Prod2Prod (Parts list)
 * 
 * @param pProductId req ProductID
 * 
 * @class
 *
 */
function Prod2ProdUtils(pProductId)
{
    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()
    {
        var ret = [];
        var childs = _relateChilds();
        
        __push(childs.root);
        
        function __push(pObj)
        {
            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;

                rowdata = [UID, PARENTID].concat(rowdata);
                ret.push(rowdata);
                __push( childs[pObj.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()
    {
        var ret = [];
        var childs = _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 "pProductId" (Constructor parameter).
    * 
    * 
    * @return {[]} [ "DEST_ID" ]
    */
    this.getParentProdIds = function()
    {
        var ret = [];
        var parents = _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.
    *
    */
    function _initProd2ProdData()
    {
        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 ";

            data = db.table(sqlStr);
        }
    }
    
    /* object tree to relate products by DEST_ID / SOURCE_ID.
     * 
     **/
    function _buildTree(pSupervised)
    {
        _initProd2ProdData();

        var tree = { root: {ids: [], sourceid: pProductId } };
        
        if(pSupervised)
            tree = { root: {ids: [], destid: pProductId } };

        for (var i = 0; i < data.length; i++)
        {
            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]
                };
            }
        }
        
        return tree;
    }
    
    function _relateChilds()
    {
        var tree = _buildTree(false);

        __relate("root");
        
        return tree;
        
        
        function __relate(pID)
        {
            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);
                }    
            }
        }
    }
    
    function _relateParents()
    {
        var tree = _buildTree(true);

        __relate("root");
        
        return tree;


        function __relate(pID)
        {
            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);
                }    
            }
        }
    }
}