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