diff --git a/aliasDefinition/Data_alias/indexsearchgroups/org/affectedIds.js b/aliasDefinition/Data_alias/indexsearchgroups/org/affectedIds.js index ae199b2018a9652efde2cbe29362e0e1f0e0b58c..59126ad58df30299bc21dcb629b3fef2b781a50e 100644 --- a/aliasDefinition/Data_alias/indexsearchgroups/org/affectedIds.js +++ b/aliasDefinition/Data_alias/indexsearchgroups/org/affectedIds.js @@ -2,12 +2,13 @@ import("system.db"); import("system.result"); import("system.vars"); import("IndexSearch_lib"); -var indexHelper, infoContainer, onUpdFn, tableName, res; +import("Sql_lib"); + +var infoContainer, onUpdFn, tableName, res; -indexHelper = new IndexsearchUtils(); tableName = vars.get("$local.table"); idValue = vars.get("$local.idvalue"); -infoContainer = indexHelper.createAffectedInfoContainer(idValue, null, vars.get("$local.action") +infoContainer = IndexsearchUtils.createAffectedInfoContainer(idValue, null, vars.get("$local.action") ,function (){return vars.get("$local.columns")} ,function (){return vars.get("$local.oldvalues")} ,function (){return vars.get("$local.values")}); @@ -21,16 +22,16 @@ switch (tableName) res = db.array(db.COLUMN, "select RELATION.RELATIONID from RELATION where RELATION.PERS_ID is null and RELATION.ORG_ID = '" + idValue + "'"); break; case "ADDRESS": - res = indexHelper.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ + res = IndexsearchUtils.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ return db.array(db.COLUMN, ["select ADDRESS.RELATION_ID from ADDRESS where ADDRESS.ADDRESSID = ?", [ - [id, db.getColumnTypes("ADDRESS", ["ADDRESSID"])[0]] + [id, SqlUtils.getSingleColumnType("ADDRESS", ["ADDRESSID"])] ]]); }); break; case "COMM": - res = indexHelper.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ + res = IndexsearchUtils.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ return db.array(db.COLUMN, ["select ADDRESS.RELATION_ID from COMM where COMMID = ?", [ - [id, db.getColumnTypes("COMM", ["COMMID"])[0]] + [id, SqlUtils.getSingleColumnType("COMM", ["COMMID"])] ]]); }); break; diff --git a/aliasDefinition/Data_alias/indexsearchgroups/pers/affectedIds.js b/aliasDefinition/Data_alias/indexsearchgroups/pers/affectedIds.js index a87cc105b4964eab1b273f808b2586b93b041a24..80d3d35764628599b1812c367844a7433711b413 100644 --- a/aliasDefinition/Data_alias/indexsearchgroups/pers/affectedIds.js +++ b/aliasDefinition/Data_alias/indexsearchgroups/pers/affectedIds.js @@ -2,12 +2,13 @@ import("system.db"); import("system.result"); import("system.vars"); import("IndexSearch_lib"); -var indexHelper, infoContainer, onUpdFn, tableName, res; +import("Sql_lib"); + +var infoContainer, onUpdFn, tableName, res; -indexHelper = new IndexsearchUtils(); tableName = vars.get("$local.table"); idValue = vars.get("$local.idvalue"); -infoContainer = indexHelper.createAffectedInfoContainer(idValue, null, vars.get("$local.action") +infoContainer = IndexsearchUtils.createAffectedInfoContainer(idValue, null, vars.get("$local.action") ,function (){return vars.get("$local.columns")} ,function (){return vars.get("$local.oldvalues")} ,function (){return vars.get("$local.values")}); @@ -24,16 +25,16 @@ switch (tableName) res = db.array(db.COLUMN, "select RELATION.RELATIONID from RELATION where RELATION.PERS_ID is not null and RELATION.ORG_ID = '" + idValue + "'"); break; case "ADDRESS": - res = indexHelper.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ + res = IndexsearchUtils.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ return db.array(db.COLUMN, ["select ADDRESS.RELATION_ID from ADDRESS where ADDRESS.ADDRESSID = ?", [ - [id, db.getColumnTypes("ADDRESS", ["ADDRESSID"])[0]] + [id, SqlUtils.getSingleColumnType("ADDRESS", ["ADDRESSID"])] ]]); }); break; case "COMM": - res = indexHelper.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ + res = IndexsearchUtils.getAffectedIdValues("RELATION_ID", infoContainer, function (id){ return db.array(db.COLUMN, ["select ADDRESS.RELATION_ID from COMM where COMMID = ?", [ - [id, db.getColumnTypes("COMM", ["COMMID"])[0]] + [id, SqlUtils.getSingleColumnType("COMM", ["COMMID"])] ]]); }); break; diff --git a/entity/Comm_entity/entityfields/addr/onValidation.js b/entity/Comm_entity/entityfields/addr/onValidation.js index 97fccc1a01aeaa1794aae039d5bf8f8d00ddca22..f7e031675a3de8e05eb86384e2ee943f5ab9de88 100644 --- a/entity/Comm_entity/entityfields/addr/onValidation.js +++ b/entity/Comm_entity/entityfields/addr/onValidation.js @@ -7,6 +7,7 @@ import("system.mail"); import("Keyword_lib"); import("Comm_lib"); import("Util_lib"); +import("Entity_lib"); var kwdUtil = new KeywordUtils(); var kwd = kwdUtil.createKeyword("COMM.MEDIUM"); @@ -15,7 +16,7 @@ var commCategory = kwd.getPropForKey(commMedium, "contentType", true);//TODO: ma var fn = CommValidationUtil.makeValidationFn(commCategory); if (fn != null){ - var commAddr = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.ADDR")); + var commAddr = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.ADDR")); var additional = CommValidationUtil.getExtensionsBlueprint(); additional.countryCode = vars.get("$param.RelationsMainCountry_param");//TODO: try to use users language first and then the companies var res = fn.call(null, commAddr, additional); diff --git a/entity/Contract_entity/entityfields/contractcode/onValidation.js b/entity/Contract_entity/entityfields/contractcode/onValidation.js index e357339a3f7c17483b1c7188c54a1c547174f19a..8d2de85d894a32c92d53fd9e16cffb2b1fd153fb 100644 --- a/entity/Contract_entity/entityfields/contractcode/onValidation.js +++ b/entity/Contract_entity/entityfields/contractcode/onValidation.js @@ -3,8 +3,12 @@ import("system.result"); import("system.vars"); import("system.db"); import("Util_lib"); +import("Entity_lib"); -var codeCount = db.cell("select count(CONTRACTCODE) from CONTRACT where CONTRACTCODE = '" + ProcessHandlingUtil.getOnValidationValue(vars.get("$field.CONTRACTCODE")) + "'" +var contractCode, codeCount; + +contractCode = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.CONTRACTCODE")); +codeCount = db.cell("select count(CONTRACTCODE) from CONTRACT where CONTRACTCODE = '" + contractCode + "'" + " and CONTRACTID <> '" + vars.get("$field.CONTRACTID") + "'"); if(codeCount > 0) { diff --git a/entity/Contract_entity/entityfields/contractdue/onValidation.js b/entity/Contract_entity/entityfields/contractdue/onValidation.js index d269674beab0906856f8e8bc83afe6ffe48c73f9..eec165e723c8199c6314723ff0e928d2f734a3ec 100644 --- a/entity/Contract_entity/entityfields/contractdue/onValidation.js +++ b/entity/Contract_entity/entityfields/contractdue/onValidation.js @@ -3,10 +3,10 @@ import("system.translate"); import("system.vars"); import("Date_lib"); import("Util_lib"); +import("Entity_lib"); -var dateUtils = new DateUtils(); -var cDue = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.CONTRACTDUE")); +var cDue = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.CONTRACTDUE")); -if (dateUtils.validateBeginnBeforeEnd(vars.get("$field.CONTRACTSTART"), cDue) === false || dateUtils.validateBeginnBeforeEnd(cDue, vars.get("$field.CONTRACTEND")) === false) { +if (DateUtils.validateBeginnBeforeEnd(vars.get("$field.CONTRACTSTART"), cDue) === false || DateUtils.validateBeginnBeforeEnd(cDue, vars.get("$field.CONTRACTEND")) === false) { result.string(translate.text("The next due date must be after the start of the contract and before the expiry of the contract!")); } diff --git a/entity/Contract_entity/entityfields/contractend/onValidation.js b/entity/Contract_entity/entityfields/contractend/onValidation.js index 8bb7dc15d6b77c65124e37372351a6c158d526e6..c2b570b75775ae1f669ba1a8e7ad9b716e17f2eb 100644 --- a/entity/Contract_entity/entityfields/contractend/onValidation.js +++ b/entity/Contract_entity/entityfields/contractend/onValidation.js @@ -2,10 +2,10 @@ import("system.result"); import("system.vars"); import("Date_lib"); import("Util_lib"); +import("Entity_lib"); -var dateUtils = new DateUtils(); -var cEnd = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.CONTRACTEND")); +var cEnd = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.CONTRACTEND")); var cDue = vars.get("$field.CONTRACTDUE"); -if (dateUtils.validateBeginnBeforeEnd(vars.get("$field.CONTRACTSTART"), cEnd) === false) - result.string(dateUtils.getValidationFailString()); \ No newline at end of file +if (DateUtils.validateBeginnBeforeEnd(vars.get("$field.CONTRACTSTART"), cEnd) === false) + result.string(DateUtils.getValidationFailString()); \ No newline at end of file diff --git a/entity/Contract_entity/entityfields/contractend/onValueChange.js b/entity/Contract_entity/entityfields/contractend/onValueChange.js index ae1ba0c5ca77f89b745d574da6d9f0c44d7013e1..b411c425e4e4a1a21d063b31fec6d81c61e9c913 100644 --- a/entity/Contract_entity/entityfields/contractend/onValueChange.js +++ b/entity/Contract_entity/entityfields/contractend/onValueChange.js @@ -1,7 +1,8 @@ import("system.vars"); import("Util_lib"); +import("Entity_lib"); -var cEnd = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.CONTRACTEND")); +var cEnd = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.CONTRACTEND")); var cDue = vars.get("$field.CONTRACTDUE"); if (cDue > cEnd && cDue != "") diff --git a/entity/Contract_entity/entityfields/contractstart/onValidation.js b/entity/Contract_entity/entityfields/contractstart/onValidation.js index 4ebb606eceef9c7f65fe763bb1ac5f1c62430f39..f7e82b3fd8a66a24591a7fb75a849799a3755ad4 100644 --- a/entity/Contract_entity/entityfields/contractstart/onValidation.js +++ b/entity/Contract_entity/entityfields/contractstart/onValidation.js @@ -3,10 +3,10 @@ import("system.result"); import("system.vars"); import("Date_lib"); import("Util_lib"); +import("Entity_lib"); -var dateUtils = new DateUtils(); -var cStart = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.CONTRACTSTART")); +var cStart = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.CONTRACTSTART")); var cDue = vars.get("$field.CONTRACTDUE"); -if (dateUtils.validateBeginnBeforeEnd(cStart, vars.get("$field.CONTRACTEND")) === false) - result.string(dateUtils.getValidationFailString()); \ No newline at end of file +if (DateUtils.validateBeginnBeforeEnd(cStart, vars.get("$field.CONTRACTEND")) === false) + result.string(DateUtils.getValidationFailString()); \ No newline at end of file diff --git a/entity/Contract_entity/entityfields/contractstart/onValueChange.js b/entity/Contract_entity/entityfields/contractstart/onValueChange.js index 2e58cfbebf5c3583bb3f18ffcc1a3d9e80b542e5..f2e29fdda8f9227616cd3dc7f3879867b2bfbaa8 100644 --- a/entity/Contract_entity/entityfields/contractstart/onValueChange.js +++ b/entity/Contract_entity/entityfields/contractstart/onValueChange.js @@ -1,7 +1,8 @@ import("system.vars"); import("Util_lib"); +import("Entity_lib"); -var cStart = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.CONTRACTSTART")); +var cStart = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.CONTRACTSTART")); var cDue = vars.get("$field.CONTRACTDUE"); if (cDue < cStart && cDue != "") diff --git a/entity/Offer_entity/Offer_entity.aod b/entity/Offer_entity/Offer_entity.aod index d159a36c18531a46cfa2bbf9f4ae93895ea75c3a..fb017884871b067886006739a58eb23b9aed8941 100644 --- a/entity/Offer_entity/Offer_entity.aod +++ b/entity/Offer_entity/Offer_entity.aod @@ -3,6 +3,7 @@ <name>Offer_entity</name> <title>Offer</title> <majorModelMode>DISTRIBUTED</majorModelMode> + <documentation>%aditoprj%/entity/Offer_entity/documentation.adoc</documentation> <grantUpdateProcess>%aditoprj%/entity/Offer_entity/grantUpdateProcess.js</grantUpdateProcess> <grantDeleteProcess>%aditoprj%/entity/Offer_entity/grantDeleteProcess.js</grantDeleteProcess> <recordContainerType>DB</recordContainerType> @@ -37,6 +38,7 @@ </entityField> <entityField> <name>OFFERCODE</name> + <documentation>%aditoprj%/entity/Offer_entity/entityfields/offercode/documentation.adoc</documentation> <title>Offer number</title> <tableName>OFFER</tableName> <columnName>OFFERCODE</columnName> @@ -69,6 +71,7 @@ </entityField> <entityField> <name>RELATION_ID</name> + <documentation>%aditoprj%/entity/Offer_entity/entityfields/relation_id/documentation.adoc</documentation> <title>Contact / Company</title> <tableName>OFFER</tableName> <columnName>RELATION_ID</columnName> @@ -182,6 +185,7 @@ </entityField> <entityField> <name>TotalGross</name> + <documentation>%aditoprj%/entity/Offer_entity/entityfields/totalgross/documentation.adoc</documentation> <title>Total gross</title> <state>READONLY</state> <valueProcess>%aditoprj%/entity/Offer_entity/entityfields/totalgross/valueProcess.js</valueProcess> @@ -222,6 +226,7 @@ </entityActionField> <entityField> <name>VERSNR</name> + <documentation>%aditoprj%/entity/Offer_entity/entityfields/versnr/documentation.adoc</documentation> <title>Vers. no.</title> <tableName>OFFER</tableName> <columnName>VERSNR</columnName> @@ -230,6 +235,7 @@ </entityField> <entityField> <name>OFFER_ID</name> + <documentation>%aditoprj%/entity/Offer_entity/entityfields/offer_id/documentation.adoc</documentation> </entityField> <entityFieldGroup> <name>OfferCode_VersNr</name> diff --git a/entity/Offer_entity/documentation.adoc b/entity/Offer_entity/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..c11de6b80022e744fb25f0e547aa69c66eeb3d00 --- /dev/null +++ b/entity/Offer_entity/documentation.adoc @@ -0,0 +1,15 @@ += OFFER + +:hardbreaks: + +Entity contains meta data of the Offer module. +It provides data from database table _OFFER_. + +== Actions + +* _Copy offer_: All values, offeritems included, are copyed and inserted in a new offer (new offer code, versnr. _1_). +* _New offer version_: All values, offeritems included, are copyed and inserted in a **new offer version** (versnr. + _1_). + +== Submodules + +* Offeritem \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/newoffer/onActionProcess.js b/entity/Offer_entity/entityfields/newoffer/onActionProcess.js index 8d44f51deacd1ece4560b02cdb57803cb104cdfa..46d81aed97b3bd2e86b855020476b9a11e0618b1 100644 --- a/entity/Offer_entity/entityfields/newoffer/onActionProcess.js +++ b/entity/Offer_entity/entityfields/newoffer/onActionProcess.js @@ -2,9 +2,9 @@ import("system.logging"); import("system.vars"); import("system.neon"); import("Util_lib"); +import("Neon_lib"); import("OfferOrder_lib"); -var CMUtils = new CopyModuleUtils(); var offUtils = new OfferUtils(); var InputMapping = { @@ -24,6 +24,6 @@ var InputMapping = { } } -var ModulesMapping = CMUtils.copyModule(InputMapping); +var ModulesMapping = CopyModuleUtils.copyModule(InputMapping); -CMUtils.openNewModules("Offer_context", ModulesMapping); \ No newline at end of file +CopyModuleUtils.openNewModules("Offer_context", ModulesMapping); \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/newofferversion/onActionProcess.js b/entity/Offer_entity/entityfields/newofferversion/onActionProcess.js index 60eaab09684e3967b9b0c72763695ccb5a88464c..7482f38129f497c9040811e120752578a9498a44 100644 --- a/entity/Offer_entity/entityfields/newofferversion/onActionProcess.js +++ b/entity/Offer_entity/entityfields/newofferversion/onActionProcess.js @@ -2,9 +2,9 @@ import("system.logging"); import("system.vars"); import("system.neon"); import("Util_lib"); +import("Neon_lib"); import("OfferOrder_lib"); -var CMUtils = new CopyModuleUtils(); var offUtils = new OfferUtils(); var InputMapping = { @@ -24,6 +24,6 @@ var InputMapping = { } } -var ModulesMapping = CMUtils.copyModule(InputMapping); +var ModulesMapping = CopyModuleUtils.copyModule(InputMapping); -CMUtils.openNewModules("Offer_context", ModulesMapping); \ No newline at end of file +CopyModuleUtils.openNewModules("Offer_context", ModulesMapping); \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/offer_id/documentation.adoc b/entity/Offer_entity/entityfields/offer_id/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..45c579ffee22ce5cfa74223c23851653a2bb8750 --- /dev/null +++ b/entity/Offer_entity/entityfields/offer_id/documentation.adoc @@ -0,0 +1,4 @@ += OFFER_ID +:hardbreaks: + +This field contains link information to previous offer version. \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/offercode/documentation.adoc b/entity/Offer_entity/entityfields/offercode/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..019d2f6339229de4bf8ba5b485bcba702cb0c6cb --- /dev/null +++ b/entity/Offer_entity/entityfields/offercode/documentation.adoc @@ -0,0 +1,5 @@ += OFFERCODE +:hardbreaks: + +Unique identification number of the offer. +It's automatically generated from the starting number _1000_. \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/offercode/onValidation.js b/entity/Offer_entity/entityfields/offercode/onValidation.js index c2f733568d6821237b66fdd31a38a894370a3d18..63c353b420b443050d33dc23602eda47a08da9c8 100644 --- a/entity/Offer_entity/entityfields/offercode/onValidation.js +++ b/entity/Offer_entity/entityfields/offercode/onValidation.js @@ -3,11 +3,12 @@ import("system.result"); import("system.neon"); import("OfferOrder_lib"); import("Util_lib"); +import("Entity_lib"); var OfferUtils = new OfferUtils(); if( vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW - && !OfferUtils.validateOfferNumber(ProcessHandlingUtil.getOnValidationValue(vars.get("$field.OFFERCODE"))) ) + && !OfferUtils.validateOfferNumber(ProcessHandlingUtils.getOnValidationValue(vars.get("$field.OFFERCODE"))) ) { vars.set( "$field.OFFERCODE", SalesprojectUtils.getNextOfferNumber().toString() ); } \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/offerdate/valueProcess.js b/entity/Offer_entity/entityfields/offerdate/valueProcess.js index af2c1f09cc605dfe6664dd0c9829026fc6785993..aeddd594f71497dedb45c4c73862b6c4dd78d440 100644 --- a/entity/Offer_entity/entityfields/offerdate/valueProcess.js +++ b/entity/Offer_entity/entityfields/offerdate/valueProcess.js @@ -5,7 +5,6 @@ import("Date_lib"); if(vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW && vars.get("$this.value") == "") { - var DateUtils = new DateUtils(); result.string(DateUtils.getTodayUTC()); } else diff --git a/entity/Offer_entity/entityfields/relation_id/documentation.adoc b/entity/Offer_entity/entityfields/relation_id/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..e978c88ef79f2554ac4731f7d4c3eb17aa50ea8b --- /dev/null +++ b/entity/Offer_entity/entityfields/relation_id/documentation.adoc @@ -0,0 +1,10 @@ += RELATION_ID +:hardbreaks: + +Over this field the company or contact person of customer is linked. + +== onValueChange + +If set in the selected company or contact person, the following field value are taken over: + +* LANGUAGE \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/relation_id/onValueChange.js b/entity/Offer_entity/entityfields/relation_id/onValueChange.js index 4c3220606c52acb4eb2ee09d69427060bbd987e8..9e8a557d9112443d5bb050c2df798542b21dbbd4 100644 --- a/entity/Offer_entity/entityfields/relation_id/onValueChange.js +++ b/entity/Offer_entity/entityfields/relation_id/onValueChange.js @@ -1,8 +1,9 @@ import("system.vars"); import("system.db"); import("Util_lib"); +import("Entity_lib"); -var relid = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.RELATION_ID")); +var relid = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.RELATION_ID")); if(relid != "") { var relData = db.array(db.ROW, "select LANGUAGE from RELATION where RELATIONID = '" + relid + "'"); diff --git a/entity/Offer_entity/entityfields/totalgross/documentation.adoc b/entity/Offer_entity/entityfields/totalgross/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..c727abed66886d38bd46c6df6e1045b82666eb30 --- /dev/null +++ b/entity/Offer_entity/entityfields/totalgross/documentation.adoc @@ -0,0 +1,6 @@ += TotalGross +:hardbreaks: + +Contains the total gross of the offer (net). +This is calculated as follows: _NET_ + _VAT_. +Both are calculated in *_Offeritem_entity_* at _onDBInsert_, _onDBUpdate_ and _onDBDelete_. \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/versnr/documentation.adoc b/entity/Offer_entity/entityfields/versnr/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..0d9cdfe8dd6e5f417025786617c8df67e43d9520 --- /dev/null +++ b/entity/Offer_entity/entityfields/versnr/documentation.adoc @@ -0,0 +1,8 @@ += VERSNR +:hardbreaks: + +Additional to the offercode there's a version number. +By default it contains the value _1_. + +A new offer version can be created over the action _newOfferVersion_. +Then the new value is the old one incremented by _1_. \ No newline at end of file diff --git a/entity/Offeritem_entity/Offeritem_entity.aod b/entity/Offeritem_entity/Offeritem_entity.aod index f9636b1e8709caf576c908bcba3680a4458bf93e..dfa1a79e76789c678181e5b94f12d8387578636b 100644 --- a/entity/Offeritem_entity/Offeritem_entity.aod +++ b/entity/Offeritem_entity/Offeritem_entity.aod @@ -3,6 +3,7 @@ <name>Offeritem_entity</name> <title>Offeritem</title> <majorModelMode>DISTRIBUTED</majorModelMode> + <documentation>%aditoprj%/entity/Offeritem_entity/documentation.adoc</documentation> <grantCreateProcess>%aditoprj%/entity/Offeritem_entity/grantCreateProcess.js</grantCreateProcess> <grantUpdateProcess>%aditoprj%/entity/Offeritem_entity/grantUpdateProcess.js</grantUpdateProcess> <grantDeleteProcess>%aditoprj%/entity/Offeritem_entity/grantDeleteProcess.js</grantDeleteProcess> @@ -117,15 +118,16 @@ </entityField> <entityField> <name>PRODUCT_ID</name> + <documentation>%aditoprj%/entity/Offeritem_entity/entityfields/product_id/documentation.adoc</documentation> <title>Article</title> <tableName>OFFERITEM</tableName> <columnName>PRODUCT_ID</columnName> <possibleItemsProcess>%aditoprj%/entity/Offeritem_entity/entityfields/product_id/possibleItemsProcess.js</possibleItemsProcess> - <onValidation>%aditoprj%/entity/Offeritem_entity/entityfields/product_id/onValidation.js</onValidation> <onValueChange>%aditoprj%/entity/Offeritem_entity/entityfields/product_id/onValueChange.js</onValueChange> </entityField> <entityField> <name>QUANTITY</name> + <documentation>%aditoprj%/entity/Offeritem_entity/entityfields/quantity/documentation.adoc</documentation> <title>Quantity</title> <tableName>OFFERITEM</tableName> <columnName>QUANTITY</columnName> @@ -180,6 +182,7 @@ </entityParameter> <entityField> <name>TotalPrice</name> + <documentation>%aditoprj%/entity/Offeritem_entity/entityfields/totalprice/documentation.adoc</documentation> <title>Sum</title> <contentType>NUMBER</contentType> <outputFormat>#,##0.00</outputFormat> diff --git a/entity/Offeritem_entity/documentation.adoc b/entity/Offeritem_entity/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..a5283456a0759e860a058690aeb402321c2779ec --- /dev/null +++ b/entity/Offeritem_entity/documentation.adoc @@ -0,0 +1,21 @@ += OFFERITEM +:hardbreaks: + +Entity contains products that belong to the linked offer. +It provides data from database table _OFFERITEM_ by a JDito record container. + +== onDBInsert + +If a new offer item is to be added, the parts list of the corresponding product +is inserted at the same time. A new item is created for each product. +The net sum in the linked offer is updated. + +== onDBUpdate + +The net sum in the linked offer is updated. + + +== onDBDelete + +Before deletion of an item, its subordinated items are also deleted. +The net sum in the linked offer is updated. \ No newline at end of file diff --git a/entity/Offeritem_entity/entityfields/product_id/documentation.adoc b/entity/Offeritem_entity/entityfields/product_id/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..5d6ffbd8dd98f0a218cec7b0d8a0228b1e65b94e --- /dev/null +++ b/entity/Offeritem_entity/entityfields/product_id/documentation.adoc @@ -0,0 +1,14 @@ += PRODUCT_ID +:hardbreaks: + +Contains link to selected product for this item. + +== onValueChange + +The following field are set with the values from the selected product: + +* GROUPCODEID +* UNIT +* ITEMNAME +* PRICE & VAT: Values from the current valid price list defined to this product. + For futher informations see the comments in method __getPriceListToUse_ in _Product_lib_. \ No newline at end of file diff --git a/entity/Offeritem_entity/entityfields/product_id/onValidation.js b/entity/Offeritem_entity/entityfields/product_id/onValidation.js deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/entity/Offeritem_entity/entityfields/product_id/onValueChange.js b/entity/Offeritem_entity/entityfields/product_id/onValueChange.js index 196b4ab169ea3cbfdb3f6e1feb6d341ce6a008ae..252419a862f0599a975424643fd12bd2a60f1958 100644 --- a/entity/Offeritem_entity/entityfields/product_id/onValueChange.js +++ b/entity/Offeritem_entity/entityfields/product_id/onValueChange.js @@ -3,8 +3,9 @@ import("system.vars"); import("system.neon"); import("Product_lib"); import("Util_lib"); +import("Entity_lib"); -var pid = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.PRODUCT_ID")); +var pid = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRODUCT_ID")); if(pid != "") { var curr = vars.exists("$param.Currency_param") ? vars.get("$param.Currency_param") : ""; diff --git a/entity/Offeritem_entity/entityfields/quantity/documentation.adoc b/entity/Offeritem_entity/entityfields/quantity/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..5f7419c93c2913f83e1e1ab76f14dd1f03e49d2c --- /dev/null +++ b/entity/Offeritem_entity/entityfields/quantity/documentation.adoc @@ -0,0 +1,9 @@ += QUANTITY +:hardbreaks: + +Quantity of selected product in this item. + +== onValueChange + +The fields PRICE & VAT are set by changing quantity when a product has already been selected. +The reason for this is that you can define a quantity as of which price lists are valid. \ No newline at end of file diff --git a/entity/Offeritem_entity/entityfields/quantity/onValueChange.js b/entity/Offeritem_entity/entityfields/quantity/onValueChange.js index d605b64c6f8ad78ba76189a0333d426011f7e337..aedc968cc70beb0c78061c5d2848f9fcc89caed3 100644 --- a/entity/Offeritem_entity/entityfields/quantity/onValueChange.js +++ b/entity/Offeritem_entity/entityfields/quantity/onValueChange.js @@ -3,9 +3,10 @@ import("system.vars"); import("system.neon"); import("Product_lib"); import("Util_lib"); +import("Entity_lib"); var pid = vars.get("$field.PRODUCT_ID"); -var newQuantity = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.QUANTITY")); +var newQuantity = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.QUANTITY")); if(pid != "" && newQuantity != "") { var curr = vars.exists("$param.Currency_param") ? vars.get("$param.Currency_param") : ""; diff --git a/entity/Offeritem_entity/entityfields/totalprice/documentation.adoc b/entity/Offeritem_entity/entityfields/totalprice/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..481fb639fb88ae3d95aa88fa8cf0cee9160dc2e5 --- /dev/null +++ b/entity/Offeritem_entity/entityfields/totalprice/documentation.adoc @@ -0,0 +1,5 @@ += TotalPrice +:hardbreaks: + +Contains the total price of the item. +This is calculated as follows: _QUANTITY_ * _PRICE_ * (100 - _DISCOUNT_) / 100 \ No newline at end of file diff --git a/entity/Prod2prod_entity/Prod2prod_entity.aod b/entity/Prod2prod_entity/Prod2prod_entity.aod index 5966912c271c770f8c6f9f04cb4b9bfc3f8e9a5b..ce05f40bd6b8211c3c09efc9b048a706a6fbf6c1 100644 --- a/entity/Prod2prod_entity/Prod2prod_entity.aod +++ b/entity/Prod2prod_entity/Prod2prod_entity.aod @@ -2,6 +2,7 @@ <entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.0.5" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.0.5"> <name>Prod2prod_entity</name> <majorModelMode>DISTRIBUTED</majorModelMode> + <documentation>%aditoprj%/entity/Prod2prod_entity/documentation.adoc</documentation> <externalOpenAction>%aditoprj%/entity/Prod2prod_entity/externalOpenAction.js</externalOpenAction> <recordContainerType>JDITO</recordContainerType> <alias>Data_alias</alias> @@ -49,6 +50,7 @@ </entityField> <entityField> <name>DEST_ID</name> + <documentation>%aditoprj%/entity/Prod2prod_entity/entityfields/dest_id/documentation.adoc</documentation> <tableName></tableName> <columnName></columnName> <fieldName>DEST_ID</fieldName> @@ -81,6 +83,7 @@ </entityField> <entityField> <name>SOURCE_ID</name> + <documentation>%aditoprj%/entity/Prod2prod_entity/entityfields/source_id/documentation.adoc</documentation> <title>Product</title> <tableName></tableName> <columnName></columnName> @@ -90,6 +93,7 @@ </entityField> <entityField> <name>TAKEPRICE</name> + <documentation>%aditoprj%/entity/Prod2prod_entity/entityfields/takeprice/documentation.adoc</documentation> <title>Price</title> <tableName></tableName> <columnName></columnName> diff --git a/entity/Prod2prod_entity/documentation.adoc b/entity/Prod2prod_entity/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..59bbbae97ad3a477420b71e111fac26c5dada832 --- /dev/null +++ b/entity/Prod2prod_entity/documentation.adoc @@ -0,0 +1,34 @@ += Prod2prod_entity +:hardbreaks: + +Entity contains links between products. +It provides data from database table _PROD2PROD_. + +The record container loads the parts list of the passed product. +This means which sub-products this product consists of. + +A possible **definiton** for a parts list is: + +[quote, McGraw-Hill Dictionary of Scientific & Technical Terms, 6E. S.v. "parts list." Retrieved November 26 2018 from https://encyclopedia2.thefreedictionary.com/parts+list] +____ +One or more printed sheets showing a manufacturer's parts or assemblies of an end item by illustration or a numerical listing of part numbers and names; it does not outline any assembly, maintenance, or operating instructions, and it may or may not have a price list cover sheet. +____ + +Database table _PROD2PROD_ contains the two foreign key columns _DEST_ID_ and _SOURCE_ID_. +Both contain a _PRODUCTID_. The following visualization clarifies the structure of a parts list. + +++++ +<img src="data:image/png;base64,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" style="cursor:pointer;max-width:100%;" onclick="(function(img){if(img.wnd!=null&&!img.wnd.closed){img.wnd.focus();}else{var r=function(evt){if(evt.data=='ready'&&evt.source==img.wnd){img.wnd.postMessage(decodeURIComponent(img.getAttribute('src')),'*');window.removeEventListener('message',r);}};window.addEventListener('message',r);img.wnd=window.open('https://www.draw.io/?client=1&lightbox=1&edit=_blank');}})(this);"/> +++++ + +//// +How to edit this diagram: +1. copy complete <img> tag from above +2. paste it into text editor and save it as a html file +3. open html file with browser +4. click on the diagram +5. at the bottom of the page you can start edit mode +6. edit diagram on draw.io +7. get <img> tag of the diagram with File -> Embed -> Image +8. replace tag above +//// \ No newline at end of file diff --git a/entity/Prod2prod_entity/entityfields/dest_id/documentation.adoc b/entity/Prod2prod_entity/entityfields/dest_id/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..34ed7bcc2d426001e59818f56d45016fdb521c6e --- /dev/null +++ b/entity/Prod2prod_entity/entityfields/dest_id/documentation.adoc @@ -0,0 +1,7 @@ += DEST_ID +:hardbreaks: + +This column contains link information (foreign key) to the product to which it is a part of (parent). + +== Preset New +The system defaults to the passed value of the parameter _ProductId_param_. \ No newline at end of file diff --git a/entity/Prod2prod_entity/entityfields/optional/documentation.adoc b/entity/Prod2prod_entity/entityfields/optional/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..3ca64e8047d84398535b3cfa2d20919c5ecd4e25 --- /dev/null +++ b/entity/Prod2prod_entity/entityfields/optional/documentation.adoc @@ -0,0 +1,4 @@ += OPTIONAL +:hardbreaks: + +Flag for offer / order. The value is taken over in offer / order. \ No newline at end of file diff --git a/entity/Prod2prod_entity/entityfields/source_id/documentation.adoc b/entity/Prod2prod_entity/entityfields/source_id/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..59dcd2e0f4c01785c34f531b3f43bd54235841f6 --- /dev/null +++ b/entity/Prod2prod_entity/entityfields/source_id/documentation.adoc @@ -0,0 +1,8 @@ += SOURCE_ID +:hardbreaks: + +This column contains link information (foreign key) to the partial product. (child) + +== value list +Listed are all products which are **not already part of** parts list. +This means each product recursively appears in _DEST_ID_ or _SOURCE_ID_ of the current product passed by parameter _ProductId_param_ **is excluded**. \ No newline at end of file diff --git a/entity/Prod2prod_entity/entityfields/takeprice/documentation.adoc b/entity/Prod2prod_entity/entityfields/takeprice/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..eb617876677f893ff5d953b92f7cd5cff145f60e --- /dev/null +++ b/entity/Prod2prod_entity/entityfields/takeprice/documentation.adoc @@ -0,0 +1,4 @@ += TAKEPRICE +:hardbreaks: + +Flag for offer / order. If set the current valid price list to this product is taken over in offer / order. \ No newline at end of file diff --git a/entity/Product_entity/Product_entity.aod b/entity/Product_entity/Product_entity.aod index 439bbabafd0959b155035cc79d979774c422fcfe..c0010e3e41a6303993fb2d67197ca7e828b0ff60 100644 --- a/entity/Product_entity/Product_entity.aod +++ b/entity/Product_entity/Product_entity.aod @@ -3,6 +3,7 @@ <name>Product_entity</name> <title>Product</title> <majorModelMode>DISTRIBUTED</majorModelMode> + <documentation>%aditoprj%/entity/Product_entity/documentation.adoc</documentation> <recordContainerType>DB</recordContainerType> <iconId>VAADIN:HAMMER</iconId> <alias>Data_alias</alias> @@ -60,6 +61,7 @@ </entityField> <entityField> <name>PRODUCTCODE</name> + <documentation>%aditoprj%/entity/Product_entity/entityfields/productcode/documentation.adoc</documentation> <title>Product number</title> <tableName>PRODUCT</tableName> <columnName>PRODUCTCODE</columnName> diff --git a/entity/Product_entity/documentation.adoc b/entity/Product_entity/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..ccd3865898d44c3d0ebd4b3bd46633d4f84b8bf1 --- /dev/null +++ b/entity/Product_entity/documentation.adoc @@ -0,0 +1,12 @@ += PRODUCT + +:hardbreaks: + +Entity contains meta data of the Product module. +It provides data from database table _PRODUCT_. + +== Submodules + +* Parts list (Prod2Prod) +* Product price +* Stock \ No newline at end of file diff --git a/entity/Product_entity/entityfields/productcode/documentation.adoc b/entity/Product_entity/entityfields/productcode/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..e3dd7742201c099ba3c7af84b92d80b5c9444479 --- /dev/null +++ b/entity/Product_entity/entityfields/productcode/documentation.adoc @@ -0,0 +1,4 @@ += PRODUCTCODE +:hardbreaks: + +Unique identification number of the product. (max. 30 characters, not automatically generated) \ No newline at end of file diff --git a/entity/Product_entity/entityfields/productcode/onValidation.js b/entity/Product_entity/entityfields/productcode/onValidation.js index b72108499db871aa2345dd05649f4cf0178edbc5..1dc5b4f996393023dd1f9e0f4070aad491cf515b 100644 --- a/entity/Product_entity/entityfields/productcode/onValidation.js +++ b/entity/Product_entity/entityfields/productcode/onValidation.js @@ -3,8 +3,12 @@ import("system.result"); import("system.vars"); import("system.db"); import("Util_lib"); +import("Entity_lib"); -var codeCount = db.cell("select count(PRODUCTCODE) from PRODUCT where PRODUCTCODE = '" + ProcessHandlingUtil.getOnValidationValue(vars.get("$field.PRODUCTCODE")) + "'" +var codeCount, productCode; + +productCode = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRODUCTCODE")); +codeCount = db.cell("select count(PRODUCTCODE) from PRODUCT where PRODUCTCODE = '" + productCode + "'" + " and PRODUCTID <> '" + vars.get("$field.PRODUCTID") + "'"); if(codeCount > 0) { diff --git a/entity/Productprice_entity/Productprice_entity.aod b/entity/Productprice_entity/Productprice_entity.aod index 0897e500a76be11b22381413ef3a4c312199ea08..039922c708f81775e5285feb0d1fd9cc8af6af88 100644 --- a/entity/Productprice_entity/Productprice_entity.aod +++ b/entity/Productprice_entity/Productprice_entity.aod @@ -135,9 +135,11 @@ </entityField> <entityField> <name>PRICELIST</name> + <documentation>%aditoprj%/entity/Productprice_entity/entityfields/pricelist/documentation.adoc</documentation> <title>Price list</title> <tableName>PRODUCTPRICE</tableName> <columnName>PRICELIST</columnName> + <mandatoryProcess>%aditoprj%/entity/Productprice_entity/entityfields/pricelist/mandatoryProcess.js</mandatoryProcess> <possibleItemsProcess>%aditoprj%/entity/Productprice_entity/entityfields/pricelist/possibleItemsProcess.js</possibleItemsProcess> <state>AUTO</state> <stateProcess>%aditoprj%/entity/Productprice_entity/entityfields/pricelist/stateProcess.js</stateProcess> diff --git a/entity/Productprice_entity/documentation.adoc b/entity/Productprice_entity/documentation.adoc index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cb6558ccb5adeee75778e6e5e83b152b94f590c7 100644 --- a/entity/Productprice_entity/documentation.adoc +++ b/entity/Productprice_entity/documentation.adoc @@ -0,0 +1,15 @@ += PRODUCTPRICE + +:hardbreaks: + +Entity contains price lists for the linked product. +It provides data from database table _PRODUCTPRICE_. + +== New price list via product + +By creation via product you have to select a price list type using the field _PRICELIST_. + +== New price list via organisation (custom price list) + +By creation via an organisation you don't have do select a price list type because +it's a custom price list \-> field _RELATION_ID_ is filled automatically by parameter _Relation_Id_param_. \ No newline at end of file diff --git a/entity/Productprice_entity/entityfields/pricelist/documentation.adoc b/entity/Productprice_entity/entityfields/pricelist/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..777173dda73190f975c75f5475f8f7a31b4d7bfb --- /dev/null +++ b/entity/Productprice_entity/entityfields/pricelist/documentation.adoc @@ -0,0 +1,18 @@ += PRICELIST + +:hardbreaks: + +Field indicates the type of the price list. + +== onValidation + +There's a **verficiation** for current pricelist whether an **identical price list already exists** for product. +The following criteria will be checked in function _ProductUtils.checkForIndenticalPriceLists()_: + +* Identical price list type +* Identical from quantity +* Identical currency +* Identical purchase price/sales price +* Idcentical Valid from and valid to OR + +If all criteria are fulfilled, price list can not be saved. \ No newline at end of file diff --git a/entity/Productprice_entity/entityfields/pricelist/mandatoryProcess.js b/entity/Productprice_entity/entityfields/pricelist/mandatoryProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..6db2c72b17a7335446e477540a3117ae5704fa35 --- /dev/null +++ b/entity/Productprice_entity/entityfields/pricelist/mandatoryProcess.js @@ -0,0 +1,7 @@ +import("system.vars"); +import("system.result"); + +if(vars.get("$field.RELATION_ID") != "") + result.string("false"); +else + result.string("true"); \ 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 4927c81bd892cc93dbb387a3f8a1407982471e41..280ee4e3e23eed2cd040d924b6db4a9d482935a0 100644 --- a/entity/Productprice_entity/entityfields/pricelist/onValidation.js +++ b/entity/Productprice_entity/entityfields/pricelist/onValidation.js @@ -3,10 +3,11 @@ import("system.result"); import("system.vars"); import("Product_lib"); import("Util_lib"); +import("Entity_lib"); var pUtils = new ProductUtils(); var priceList = { - priceList: ProcessHandlingUtil.getOnValidationValue(vars.get("$field.PRICELIST")) + priceList: ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRICELIST")) , fromQuantity: vars.get("$field.FROMQUANTITY") , buySell: vars.get("$field.BUYSELL") , currency: vars.get("$field.CURRENCY") diff --git a/entity/Productprice_entity/entityfields/valid_from/onValidation.js b/entity/Productprice_entity/entityfields/valid_from/onValidation.js index 74fa861f9bb6db8bcef42cc1ee335bd281fadac6..5ebbf1a7dccee4b0a58c46b478596bf326697184 100644 --- a/entity/Productprice_entity/entityfields/valid_from/onValidation.js +++ b/entity/Productprice_entity/entityfields/valid_from/onValidation.js @@ -2,9 +2,9 @@ import("system.result"); import("system.vars"); import("Date_lib"); import("Util_lib"); +import("Entity_lib"); -var dateUtils = new DateUtils(); -var cStart = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.VALID_FROM")); +var cStart = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.VALID_FROM")); -if (dateUtils.validateBeginnBeforeEnd(cStart, vars.get("$field.VALID_TO")) === false) - result.string(dateUtils.getValidationFailString()); \ No newline at end of file +if (DateUtils.validateBeginnBeforeEnd(cStart, vars.get("$field.VALID_TO")) === false) + result.string(DateUtils.getValidationFailString()); \ No newline at end of file diff --git a/entity/Productprice_entity/entityfields/valid_to/onValidation.js b/entity/Productprice_entity/entityfields/valid_to/onValidation.js index de9f15393fcf97ff4efb4edd960f4aea2a268d72..3ada2dd1e76e6dceb2b4c1dbf169bf91e0125579 100644 --- a/entity/Productprice_entity/entityfields/valid_to/onValidation.js +++ b/entity/Productprice_entity/entityfields/valid_to/onValidation.js @@ -2,9 +2,9 @@ import("system.result"); import("system.vars"); import("Date_lib"); import("Util_lib"); +import("Entity_lib"); -var dateUtils = new DateUtils(); -var cEnd = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.VALID_TO")); +var cEnd = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.VALID_TO")); -if (dateUtils.validateBeginnBeforeEnd(vars.get("$field.VALID_FROM"), cEnd) === false) - result.string(dateUtils.getValidationFailString()); \ No newline at end of file +if (DateUtils.validateBeginnBeforeEnd(vars.get("$field.VALID_FROM"), cEnd) === false) + result.string(DateUtils.getValidationFailString()); \ No newline at end of file diff --git a/entity/SalesprojectCompetition_entity/entityfields/reason/onValidation.js b/entity/SalesprojectCompetition_entity/entityfields/reason/onValidation.js index 6a13d6a1ca6838c022617f2face7ebe539efab78..dde86e982a4f580311473d37bdb9ee9c61c8dd77 100644 --- a/entity/SalesprojectCompetition_entity/entityfields/reason/onValidation.js +++ b/entity/SalesprojectCompetition_entity/entityfields/reason/onValidation.js @@ -2,8 +2,9 @@ import("system.result"); import("system.vars"); import("system.translate"); import("Util_lib"); +import("Entity_lib"); -var reason = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.REASON")); +var reason = ProcessHandlingUtils.getOnValidationValue(vars.get("$field.REASON")); if (!vars.getString("$field.DATE_CANCELLED") && reason) { result.string(translate.text("A reason is only possible if a date is given.")) diff --git a/entity/Salesproject_entity/entityfields/enddate/valueProcess.js b/entity/Salesproject_entity/entityfields/enddate/valueProcess.js index b27871853b658ad34b7de4e3e674d0a627c1617b..857933a9839949add7347dcccb862b6b40601dec 100644 --- a/entity/Salesproject_entity/entityfields/enddate/valueProcess.js +++ b/entity/Salesproject_entity/entityfields/enddate/valueProcess.js @@ -5,7 +5,6 @@ import("Date_lib"); if(vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW && vars.get("$this.value") == "") { - var DateUtils = new DateUtils(); result.string(DateUtils.getDateIncrementedByYears(DateUtils.getTodayUTC(), 1)); } else diff --git a/entity/Salesproject_entity/entityfields/projectcode/onValidation.js b/entity/Salesproject_entity/entityfields/projectcode/onValidation.js index d98945cc17aabeb18d7213d3ebb8e6c408d0e6ac..1f7657c4feda5d0e61dd89fe7dccffa1043efb85 100644 --- a/entity/Salesproject_entity/entityfields/projectcode/onValidation.js +++ b/entity/Salesproject_entity/entityfields/projectcode/onValidation.js @@ -3,9 +3,10 @@ import("system.result"); import("system.neon"); import("Salesproject_lib"); import("Util_lib"); +import("Entity_lib"); if( vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW - && !Salesproject.validateProjectNumber(ProcessHandlingUtil.getOnValidationValue(vars.get("$field.PROJECTCODE"))) ) + && !Salesproject.validateProjectNumber(ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PROJECTCODE"))) ) { vars.set( "$field.PROJECTCODE", Salesproject.getNextProjectNumber().toString("1 = 1") ); } \ No newline at end of file diff --git a/entity/Salesproject_entity/entityfields/startdate/valueProcess.js b/entity/Salesproject_entity/entityfields/startdate/valueProcess.js index af2c1f09cc605dfe6664dd0c9829026fc6785993..aeddd594f71497dedb45c4c73862b6c4dd78d440 100644 --- a/entity/Salesproject_entity/entityfields/startdate/valueProcess.js +++ b/entity/Salesproject_entity/entityfields/startdate/valueProcess.js @@ -5,7 +5,6 @@ import("Date_lib"); if(vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW && vars.get("$this.value") == "") { - var DateUtils = new DateUtils(); result.string(DateUtils.getTodayUTC()); } else diff --git a/entity/Stock_entity/Stock_entity.aod b/entity/Stock_entity/Stock_entity.aod index 918140f62110b5d809333a1b3a4c7f677f4ee9d3..dfae4c28d432d6336ce785d7b4d71155351ba1a1 100644 --- a/entity/Stock_entity/Stock_entity.aod +++ b/entity/Stock_entity/Stock_entity.aod @@ -3,6 +3,7 @@ <name>Stock_entity</name> <title>Stock</title> <majorModelMode>DISTRIBUTED</majorModelMode> + <documentation>%aditoprj%/entity/Stock_entity/documentation.adoc</documentation> <recordContainerType>DB</recordContainerType> <alias>Data_alias</alias> <conditionProcess>%aditoprj%/entity/Stock_entity/conditionProcess.js</conditionProcess> diff --git a/entity/Stock_entity/documentation.adoc b/entity/Stock_entity/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..988a6942455abacabdd88bb4170f27b36ed446e5 --- /dev/null +++ b/entity/Stock_entity/documentation.adoc @@ -0,0 +1,6 @@ += STOCK + +:hardbreaks: + +Entity contains stock for the linked product. +It provides data from database table _STOCK_. \ No newline at end of file diff --git a/entity/Stock_entity/entityfields/stockcount/valueProcess.js b/entity/Stock_entity/entityfields/stockcount/valueProcess.js index d5de29c8e34a1b58595736e30155324f168ff5b5..4c32e2524eda69478457c1979858e40229135cba 100644 --- a/entity/Stock_entity/entityfields/stockcount/valueProcess.js +++ b/entity/Stock_entity/entityfields/stockcount/valueProcess.js @@ -5,7 +5,7 @@ import("Product_lib"); if(vars.exists("$param.ProductId_param") && vars.get("$param.ProductId_param") != "") { - var ProductUtils = new ProductUtils(); + var pUtils = new ProductUtils(); - result.string( ProductUtils.getStockCount(vars.get("$param.ProductId_param")) ); + result.string( pUtils.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 7e71a092f62e6fdd0ee34c4c77d5e6be4ee5c6eb..79d6a489bccec838c897e390e6607cdb895c69b3 100644 --- a/others/guide/how to write JDito code.adoc +++ b/others/guide/how to write JDito code.adoc @@ -1,5 +1,5 @@ How to write JDito code -============================ +======================= :toc2: left :numbered: @@ -26,11 +26,27 @@ data = vars.get....... Example: [source,javascript] ---- -for (i = 0, i < dataLen; i++){ +for (i = 0, i < dataLen; i++) { //code here } ---- +=== loops === +nested loops should be defined with replicated indexer variables (or with a good and describing name) + +Therefore it's easy to see in which level of the counter you are. + +Example: +[source,javascript] +---- +for (i = 0, i < dataLen; i++) { + for (ii = 0, ii < dataLen[i].length; ii++) { + //code... + } +} +---- + + == defining different types of functions n libraries == === by using static methods === diff --git a/others/guide/how to write a library.adoc b/others/guide/how to write a library.adoc new file mode 100644 index 0000000000000000000000000000000000000000..a3175cc3f706693f1bcf5664c997e095dd5362f4 --- /dev/null +++ b/others/guide/how to write a library.adoc @@ -0,0 +1,110 @@ +How to write a JDito library +============================ +:toc2: left +:numbered: + + +== defining different types of functions == + +=== by using static methods === +This will be mostly utility functions and so on, where there is no need to instanciate an object. You'll need this probably the most time. + + +Definition: +[source,javascript] +---- +/** + * provides static methods for validation of communication data + * do not create an instance of this + * @static + * @class + */ +function CommValidationUtil(){<1> +} + +/** + * returns a blueprint for validation extensions; these extensions are needed for validating comm data and can be passed to other functions + * @return {object} a object with properties that have a specific default value; normally you want to overwrite that value + */ +CommValidationUtil.getExtensionsBlueprint = function(){<2> + return { + countryCode: null + }; +} +---- +<1> the function-object that keeps everything together - this function should never be actually called (no direct call, no indirect call) +<2> an actual function that can be called + +And how to use it: +[source,javascript] +---- +import("Comm_lib"); + +var additionals = CommValidationUtil.getExtensionsBlueprint(); +---- + +=== by creating an object with functions === + +You may want to hold data and create objects where methods share that data. + +Definition: +[source,javascript] +---- +/** + * object for easier handling of conditions; <1> + * With this object you do not have to check if the string is empty or not; + * you don't need to append a "1=1" condition or similar; + * this objects gains most benefit if you have a lot of conditions that are added (or not) depending on tons of JDito-conditions + * @class + * @param {String} [alias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements) <2> + * @example //TODO: add missing example <3> + */ +function SqlCondition(alias){<4> + //setting null is only needed to provide autocomplete for the ADITO-designer + this.preparedValues = null; + this._init();//the properties are initalized in an extra function because init is nearly the same as resetting (clearing) the SqlConditions + this.alias = alias; +} +/** + * append with SQL-and; no paranthesize of existing conditions is done + * @param {String} cond the condition string which shall be appended + * @return {Object} current SqlCondition-object + */ +SqlCondition.prototype.and = function(cond){<5> + if (!cond) + return this; + if (this._sqlStorage) + this._sqlStorage += " and "; + this._sqlStorage += cond; + return this; +} +---- + +<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 usefull on more complex functions +<4> constructor function; init propiertes (do not set functions ("methods") here!) +<5> add functions ("methods") to the prototype, they are available through the prototype chain + +And how to use it (normally you'd want to use preparedStatements but for the sake of an easy example it's a bit shorter here) +[source,javascript] +---- +import("system.vars"); +import("system.result"); +import("Sql_lib"); +import("Comm_lib"); + +var cond, mediumIds, idVal; + +cond = new SqlCondition(); + +mediumIds = CommExtensions.getContextualMediumIds(); +if (mediumIds.length > 0) + cond.and("COMM.MEDIUM_ID in (" + mediumIds.join(", ") + ")"); + +idVal = vars.get("$local.idvalue"); +if (uids.length > 0) + cond.and("COMM.COMMID = '" + idVal + "' "); + +result.string(cond.toString("COMM.OPTIONAL = 't'")); +---- \ No newline at end of file diff --git a/process/Date_lib/process.js b/process/Date_lib/process.js index 18a8c5d75a362aa1c4c552ee1cb394267be39062..b91e9b129d54a95f3e9c00c637daf7797da40f48 100644 --- a/process/Date_lib/process.js +++ b/process/Date_lib/process.js @@ -5,45 +5,44 @@ import("system.datetime"); * provides methods for interactions with dates */ function DateUtils(){ - - var that = this; - /** - * Validates two date inputs (beginning should always be before the end!) - * - * @param pStart {Number} - * @param pEnd {Number} - * - * @result {Boolean|null} Boolean if it was able to check smth or null if the input values were not valid - */ - this.validateBeginnBeforeEnd = function(pStart, pEnd) { - if (pStart == "" || pStart == null || pEnd == "" || pEnd == null) return null; - return pStart <= pEnd; - } - - this.getValidationFailString = function(){ - return translate.text("The expiry date must be after the start date!"); - } - - /** - * Delivers the current date at midnight in UTC - * - * @result {Number} - */ - this.getTodayUTC = function(){ - return datetime.today("UTC"); - } - - /** - * Delivers the passed date incremented by passed years - * - * @param pDate {Number} - * @param pYears {Number} - * - * @result {Number} date incremented by years - */ - this.getDateIncrementedByYears = function(pDate, pYears){ - var dateObj = new Date(pDate); - - return dateObj.setFullYear(dateObj.getFullYear() + pYears); - } +} + +/** + * Validates two date inputs (beginning should always be before the end!) + * + * @param pStart {Number} + * @param pEnd {Number} + * + * @result {Boolean|null} Boolean if it was able to check smth or null if the input values were not valid + */ +DateUtils.validateBeginnBeforeEnd = function(pStart, pEnd) { + if (pStart == "" || pStart == null || pEnd == "" || pEnd == null) return null; + return pStart <= pEnd; +} + +DateUtils.getValidationFailString = function(){ + return translate.text("The expiry date must be after the start date!"); +} + +/** + * Delivers the current date at midnight in UTC + * + * @result {Number} + */ +DateUtils.getTodayUTC = function(){ + return datetime.today("UTC"); +} + +/** + * Delivers the passed date incremented by passed years + * + * @param pDate {Number} + * @param pYears {Number} + * + * @result {Number} date incremented by years + */ +DateUtils.getDateIncrementedByYears = function(pDate, pYears){ + var dateObj = new Date(pDate); + + return dateObj.setFullYear(dateObj.getFullYear() + pYears); } \ No newline at end of file diff --git a/process/Entity_lib/Entity_lib.aod b/process/Entity_lib/Entity_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..d8eaf5d930b5b52d7ffac81d99591fbecf8dc56c --- /dev/null +++ b/process/Entity_lib/Entity_lib.aod @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.7" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.1.7"> + <name>Entity_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/Entity_lib/process.js</process> +</process> diff --git a/process/Entity_lib/process.js b/process/Entity_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..4c0e41ed076844b898fe86edc950773ceb3d1d15 --- /dev/null +++ b/process/Entity_lib/process.js @@ -0,0 +1,23 @@ +/** +* provides static methods for special handling of entities in JDito-Processes +* do not create an instance of this +* @class +* @static +*/ +function ProcessHandlingUtils(){ +} + +/** +* In onValidation-Process a local variable called "$local.value" is made available from kernel. +* It contains the entered value - the field contains the value only after successfull validation (vars.get("$field.Fieldname")). +* The onValidation-Process is running again before saving the entity, at this point there's "$local.value" varialbe no longer available, +* but the entered value now is the present one because the field has already been validated before. +* Otherwise a "variable not found" error would occur. +* +* @param {String} fieldValue value of the field onValidation-Process is executed ( e.g. vars.get("$field.Fieldname") ) +* +* @return {String} Field value for onValidation-Process +*/ +ProcessHandlingUtils.getOnValidationValue = function(fieldValue) { + return vars.exists("$local.value") ? vars.get("$local.value") : fieldValue; +} diff --git a/process/IndexSearch_lib/process.js b/process/IndexSearch_lib/process.js index 355f73d542a6f979ef23dcc47be2b0f101f271da..87a8adbe541d58ccabc57c4e81b5238569e62327 100644 --- a/process/IndexSearch_lib/process.js +++ b/process/IndexSearch_lib/process.js @@ -1,61 +1,70 @@ -function IndexsearchUtils(){ - this.getAffectedIdValues = function(fieldname, affectedInfoContainer, updateFn){ - var affectedIds; - switch (affectedInfoContainer.action){ - case "I": - affectedIds = [affectedInfoContainer.newValues[affectedInfoContainer.columns.indexOf(fieldname)]]; - break; - case "U": - affectedIds = updateFn.call(null, affectedInfoContainer.id); - break; - case "D": - affectedIds = [affectedInfoContainer.oldValues[affectedInfoContainer.columns.indexOf(fieldname)]]; - break; - } - return affectedIds || []; - } - - this.createAffectedInfoContainer = function(changedIdValue, changedTable, action, columnsFn, oldValueFn, newValueFn){ - var res, internalStorage; - internalStorage = {}; - res = { - id: changedIdValue - ,table: changedTable - ,action: action - ,columns: null //null for autocomplete in the ADITO-designer - ,oldValues: null - ,newValues: null - }; - Object.defineProperty(res, "columns", { - get: function(){ - if (internalStorage["columns"] == undefined) - internalStorage["columns"] = columnsFn.call(null); - return internalStorage["columns"]; - } - ,set: function (v){ - internalStorage["columns"] = v; - } - }); - Object.defineProperty(res, "oldValues", { - get: function(){ - if (internalStorage["oldValues"] == undefined) - internalStorage["oldValues"] = oldValueFn.call(null); - return internalStorage["oldValues"]; - } - ,set: function (v){ - internalStorage["oldValues"] = v; - } - }); - Object.defineProperty(res, "newValues", { - get: function(){ - if (internalStorage["newValues"] == undefined) - internalStorage["newValues"] = newValueFn.call(null); - return internalStorage["newValues"]; - } - ,set: function (v){ - internalStorage["newValues"] = v; - } - }); - return res; - } +/** +* provides static methods for special handling of entities in JDito-Processes +* do not create an instance of this +* @class +* @static +*/ +function IndexsearchUtils(){ +} + +//todo: comment +IndexsearchUtils.getAffectedIdValues = function(fieldname, affectedInfoContainer, updateFn) { + var affectedIds; + switch (affectedInfoContainer.action){ + case "I": + affectedIds = [affectedInfoContainer.newValues[affectedInfoContainer.columns.indexOf(fieldname)]]; + break; + case "U": + affectedIds = updateFn.call(null, affectedInfoContainer.id); + break; + case "D": + affectedIds = [affectedInfoContainer.oldValues[affectedInfoContainer.columns.indexOf(fieldname)]]; + break; + } + return affectedIds || []; +} + +//todo: comment +IndexsearchUtils.createAffectedInfoContainer = function(changedIdValue, changedTable, action, columnsFn, oldValueFn, newValueFn) { + var res, internalStorage; + internalStorage = {}; + res = { + id: changedIdValue + ,table: changedTable + ,action: action + ,columns: null //null for autocomplete in the ADITO-designer + ,oldValues: null + ,newValues: null + }; + Object.defineProperty(res, "columns", { + get: function(){ + if (internalStorage["columns"] == undefined) + internalStorage["columns"] = columnsFn.call(null); + return internalStorage["columns"]; + } + ,set: function (v){ + internalStorage["columns"] = v; + } + }); + Object.defineProperty(res, "oldValues", { + get: function(){ + if (internalStorage["oldValues"] == undefined) + internalStorage["oldValues"] = oldValueFn.call(null); + return internalStorage["oldValues"]; + } + ,set: function (v){ + internalStorage["oldValues"] = v; + } + }); + Object.defineProperty(res, "newValues", { + get: function(){ + if (internalStorage["newValues"] == undefined) + internalStorage["newValues"] = newValueFn.call(null); + return internalStorage["newValues"]; + } + ,set: function (v){ + internalStorage["newValues"] = v; + } + }); + return res; } \ No newline at end of file diff --git a/process/Neon_lib/Neon_lib.aod b/process/Neon_lib/Neon_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..7cf15ed468aacf84f69010e2b03c1ad3ccf64e56 --- /dev/null +++ b/process/Neon_lib/Neon_lib.aod @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.7" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.1.7"> + <name>Neon_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/Neon_lib/process.js</process> +</process> diff --git a/process/Neon_lib/process.js b/process/Neon_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..441ffa8b7d4332333fd4d9af6dffad713d563e17 --- /dev/null +++ b/process/Neon_lib/process.js @@ -0,0 +1,384 @@ +/** + * Class containing utility functions for copying modules + * @class + */ +function CopyModuleUtils() { +} + +/** + * opens the new created modules in neonClient + * + * @param {String} pNeonContext req Name of the neon context that should be opened + * @param {Object} pModulesMapping req ModulesMapping object created by method copyModule + * + * @example var ModulesMapping = CMUtils.copyModule(InputMapping); + * + * CMUtils.openNewModules("Offer_context", ModulesMapping); + */ +CopyModuleUtils.openNewModules = function(pNeonContext, pModulesMapping) +{ + if(pModulesMapping != undefined) + { + var rootModule = Object.keys(pModulesMapping)[0]; + if(pModulesMapping[rootModule].DataRows != undefined) + { + var newids = []; + + for(var row in pModulesMapping[rootModule].DataRows) + { + newids.push(pModulesMapping[rootModule].DataRows[row].newPrimaryKey); + } + + if(newids.length > 0) + neon.openContext(pNeonContext, newids, neon.OPERATINGSTATE_VIEW, null); + } + } +} + +/** +* Creates a copy of a specified module together with specified subordinated modules. <br> +* The structure of the input mapping object is the following: <br> +* pInputMapping { +* (only one rootModule allowed) +* $rootModule$: { +* condition: "sqlWhereCondition" +* , ValueMapping: {$colName$: "value"} +* , destinationModuleName: "destinationModuleName" +* , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } +* , SubModules: { +* $Module$:{ +* condition: "sqlWhereCondition" +* , ValueMapping: {$colName$: "value"} +* , destinationModuleName: "destinationModuleName" +* , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } +* , SubModules: {...} +* } +* } +* } +* } +* +* @param {Object} pInputMapping +* +* @example var CMUtils = new CopyModuleUtils(); +* +* var InputMapping = { +* +* "OFFER": { +* condition: "OFFERID = '" + vars.get("$field.OFFERID") + "'" +* ,SubModules:{ +* "OFFERITEM": { +* condition: "OFFER_ID = '" + vars.get("$field.OFFERID") + "' order by ITEMSORT" +* } +* } +* } +* } +* +* CMUtils.copyModule(InputMapping); +*/ +CopyModuleUtils.copyModule = function(pInputMapping) +{ + var AliasDefinitionStructure = project.getAliasDefinitionStructure("Data_alias", null); + var ModulesMapping = {}; + var statements = []; + + buildMapping( pInputMapping ); + buildStatements( ModulesMapping ); + + if(statements.length > 0) + db.inserts( statements ); + + return ModulesMapping; + + /** + * Builds a mapping Object for the copyModule Method. <br> + * The structure of the Object is the following: <br> + * ModulesMapping = { + * + * $rootModule$: { ($$ marks an object property) + * + * (ModuleMapping) + * name: "moduleName" + * , destinationModuleName: "destinationModuleName" + * , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } + * , ValueMapping: {$colName$: "value"} + * , dataRows:{ + * (ModuleRowMapping) + * $rowid$: { + * name: "moduleName" + * , oldPrimaryKey: "oldPrimaryKeyValue" + * , newPrimaryKey: "newPrimaryKeyValue" + * , ColumnMapping: { $colName$: { newValue: "newValue", oldValue: "oldValue", destinationColumn: "destinationColumn" } } + * , ModuleMapping: object reference to ModuleMapping object that contains ModuleRowMapping objects + * , ParentModuleMapping: object reference to supervised ModuleMapping object (at this point "null" because there is no supervised object) + * } + * } + * , SubModules: { + * (ModuleMapping) + * $moduleName$: { + * name: "moduleName" + * , destinationModuleName: "destinationModuleName" + * , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } + * , ValueMapping: {$colName$: "value"} + * , dataRows:{ + * (ModuleRowMapping) + * $rowid$: { + * name: "moduleName" + * , oldPrimaryKey: "oldPrimaryKeyValue" + * , newPrimaryKey: "newPrimaryKeyValue" + * , ColumnMapping: { $colName$: { newValue: "newValue", oldValue: "oldValue", destinationColumn: "destinationColumn" } } + * , ModuleMapping: + * , ParentModuleMapping: object reference to supervised ModuleMapping object (at this point "null" because there is no supervised object) + * } + * } + * , SubModules: {...} + * } + * } + * + * } + * + *} + * + * @param {Object} pInputMapping InputMapping + */ + function buildMapping(pInputMapping) + { + //root mapping + var rootModule = Object.keys(pInputMapping)[0]; + var ModuleMapping = _ModuleMapping(rootModule, pInputMapping[rootModule]); + var ModuleData = _getModuleData(rootModule, pInputMapping[rootModule].condition); + + for(var row in ModuleData) + { + var ModuleRowMapping = _ModuleRowMapping(ModuleMapping, null, ModuleData[row]); + + ModuleMapping.DataRows[ModuleRowMapping.oldPrimaryKey] = ModuleRowMapping; + } + + ModulesMapping[rootModule] = ModuleMapping; + + //recursive subordinated modules mapping + _buildSubordinatedMapping(pInputMapping, ModuleMapping); + + + //delivers stored data for module in Database with condition + function _getModuleData(pModule, pCondition) + { + if(pModule == undefined) return {}; + + var ModuleColumnsStructure = AliasDefinitionStructure.tables[pModule].columns; + var cols = Object.keys(ModuleColumnsStructure); + + var condition = "1=1"; + if(pCondition != undefined) + condition = pCondition; + + var dbData = db.table("select " + cols.join(", ") + " from " + pModule + " where " + condition); + + //map 2d-Array to Object { $rowNumber$: { $columnName$: { value: "valueInDB" } } } + var DataObj = {}; + for(var row = 0; row < dbData.length; row++) + { + DataObj[row] = {}; + for(var col = 0; col < dbData[row].length; col++) + { + DataObj[row][cols[col]] = { + value: dbData[row][col] + }; + } + } + + return DataObj; + } + + + //recursive: ModuleMapping and ModuleRowMapping for subordinated modules + function _buildSubordinatedMapping(pInputMapping, pParentModuleMapping) + { + var SubModules = pInputMapping[pParentModuleMapping.name].SubModules; + if(SubModules == undefined) + return; + + for(var subModuleName in SubModules) + { + var ModuleMapping = _ModuleMapping(subModuleName, SubModules[subModuleName]); + ModuleData = _getModuleData(subModuleName, SubModules[subModuleName].condition); + for(var row in ModuleData) + { + ModuleRowMapping = _ModuleRowMapping(ModuleMapping, pParentModuleMapping, ModuleData[row]); + ModuleMapping.DataRows[ModuleRowMapping.oldPrimaryKey] = ModuleRowMapping; + } + + ModulesMapping[pParentModuleMapping.name].SubModules[subModuleName] = ModuleMapping; + + _buildSubordinatedMapping(SubModules, ModuleMapping); + } + } + + function _ModuleMapping( pModuleName, pInputModuleMapping ) + { + return { + name: pModuleName, + destinationModuleName: pInputModuleMapping.destinationModuleName == undefined ? pModuleName : pInputModuleMapping.destinationModuleName, + DestinationColumnMapping: pInputModuleMapping.DestinationColumnMapping == undefined ? {} : pInputModuleMapping.DestinationColumnMapping, + ValueMapping: pInputModuleMapping.ValueMapping == undefined ? {} : pInputModuleMapping.ValueMapping, + DataRows:{}, + SubModules: {} + }; + } + + function _ModuleRowMapping(pModuleMapping, pParentModuleMapping, pDataRow) + { + var ModuleRowMapping = { + name: pModuleMapping.name, + oldPrimaryKey: null, + newPrimaryKey: null, + ColumnMapping: {}, + ModuleMapping: pModuleMapping, + ParentModuleMapping: pParentModuleMapping + }; + + var ModuleColumnsStructure = AliasDefinitionStructure.tables[ModuleRowMapping.name].columns; + + //build ColumnMapping + for(var col in ModuleColumnsStructure) + { + //set defined columns from InputMapping -> if not defined, use the same column + var destinationColumn = ModuleRowMapping.ModuleMapping.DestinationColumnMapping[col]; + if(destinationColumn == undefined) + destinationColumn = col; + + //set defined values from InputMapping -> if not defined, use the value from DB + var oldValue = pDataRow[col].value; + var newValue = newValue = ModuleRowMapping.ModuleMapping.ValueMapping[col]; + if(newValue == undefined) + newValue = oldValue; + + //set new primary key + if(ModuleColumnsStructure[col].primaryKey) + { + ModuleRowMapping.oldPrimaryKey = ModuleRowMapping.ColumnMapping[col] = oldValue; + newValue = util.getNewUUID(); + ModuleRowMapping.newPrimaryKey = newValue; + } + + ModuleRowMapping.ColumnMapping[col] = _columnMapping(newValue, oldValue, destinationColumn); + } + + switch(ModuleRowMapping.name) + { + case "OFFER": + { + //andere Values setzen + ModuleRowMapping.ColumnMapping["OFFERDATE"].newValue = DateUtils.getTodayUTC(); + } + break; + case "OFFERITEM": + { + //OFFER_ID mappen + if(ModuleRowMapping.ParentModuleMapping.name == "OFFER") + { + ModuleRowMapping.ColumnMapping["OFFER_ID"].newValue = ModulesMapping[ModuleRowMapping.ParentModuleMapping.name].DataRows[ModuleRowMapping.ColumnMapping["OFFER_ID"].oldValue].newPrimaryKey; + } + //ASSIGNEDTO mappen + if(ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue != "") + { + if(ModuleRowMapping.ParentModuleMapping == null) + { + ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].newValue = ModulesMapping["OFFERITEM"].DataRows[ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue].newPrimaryKey; + } + else + { + ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].newValue = ModuleRowMapping.ModuleMapping.DataRows[ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue].newPrimaryKey; + } + } + } + break; + case "OFFERLINK": + { + + } + default: + { + + } + + + } + + //Spezialbehandlung USER_NEW DATENEW.... + if(ModuleRowMapping.ColumnMapping["DATE_NEW"] != undefined) ModuleRowMapping.ColumnMapping["DATE_NEW"].newValue = datetime.date(); + if(ModuleRowMapping.ColumnMapping["USER_NEW"] != undefined) ModuleRowMapping.ColumnMapping["USER_NEW"].newValue = vars.get("$sys.user"); + if(ModuleRowMapping.ColumnMapping["DATE_EDIT"] != undefined) ModuleRowMapping.ColumnMapping["DATE_EDIT"].newValue = ""; + if(ModuleRowMapping.ColumnMapping["USER_EDIT"] != undefined) ModuleRowMapping.ColumnMapping["USER_EDIT"].newValue = ""; + + return ModuleRowMapping; + } + + + + + function _columnMapping(pNewValue, pOldValue, pDestinationColumn) + { + return { + newValue: pNewValue, + oldValue: pOldValue, + destinationColumn: pDestinationColumn + }; + } + + } + + /** + * Builds the insert statements for passed ModulesMapping + * + * @param {Object} pModulesMapping ModulesMapping from buildMapping() + */ + function buildStatements(pModulesMapping) + { + var rootModule = Object.keys(pModulesMapping)[0]; + + for(var row in pModulesMapping[rootModule].DataRows) + { + //buildInsertStatement + statements.push(_statement(pModulesMapping[rootModule].DataRows[row])); + } + + _subordinatedStatements(pModulesMapping[rootModule]); + + function _subordinatedStatements(pMapping) + { + if(pMapping.SubModules == undefined) + return; + + for(var subModule in pMapping.SubModules) + { + + for(var row in pMapping.SubModules[subModule].DataRows) + { + statements.push(_statement(pMapping.SubModules[subModule].DataRows[row])); + } + + _subordinatedStatements(pMapping.SubModules[subModule]); + } + + } + + function _statement(pRowMapping) + { + var cols = []; + var vals = []; + var destTable = pRowMapping.ModuleMapping.destinationModuleName; + var colMapping = pRowMapping.ColumnMapping; + + for(var col in colMapping) + { + cols.push(colMapping[col].destinationColumn); + vals.push(colMapping[col].newValue.toString()); + } + + var colTypes = db.getColumnTypes(destTable, cols) + + return [destTable, cols, colTypes, vals]; + } + } +} diff --git a/process/OfferOrder_lib/process.js b/process/OfferOrder_lib/process.js index 3e32be2ad1ab81fd1382f913485c231788c09444..7ace6bd3b1719873563408cbf29b80f968d9ac61 100644 --- a/process/OfferOrder_lib/process.js +++ b/process/OfferOrder_lib/process.js @@ -15,8 +15,7 @@ function OfferUtils() */ this.getNextOfferNumber = function() { - var JdUtils = new JDitoUtils(); - return JdUtils.getNextUniqueNumber("OFFERCODE", "OFFER"); + return NumberSequencingUtils.getNextUniqueNumber("OFFERCODE", "OFFER"); } /** @@ -26,8 +25,7 @@ function OfferUtils() */ this.getNextOfferVersionNumber = function(pOfferCode) { - var JdUtils = new JDitoUtils(); - return JdUtils.getNextUniqueNumber("VERSNR", "OFFER", 1, "OFFERCODE = " + pOfferCode); + return NumberSequencingUtils.getNextUniqueNumber("VERSNR", "OFFER", 1, "OFFERCODE = " + pOfferCode); } /** @@ -39,8 +37,7 @@ function OfferUtils() */ this.validateOfferNumber = function(pOfferNumber) { - var JdUtils = new JDitoUtils(); - return JdUtils.validateUniqueNumber(pOfferNumber, "OFFERCODE", "OFFER"); + return NumberSequencingUtils.validateUniqueNumber(pOfferNumber, "OFFERCODE", "OFFER"); } this.getOfferNumberValidationFailString = function() diff --git a/process/Product_lib/process.js b/process/Product_lib/process.js index 161c665d32d38340cba27e1bb437dd318b2b30b8..94451a5e126ea8427c36a2cdf0a633d5633e6bd8 100644 --- a/process/Product_lib/process.js +++ b/process/Product_lib/process.js @@ -82,7 +82,7 @@ function ProductUtils() * @param pPriceListFilter {Object} opt { currency: "currencyValue", quantity: "quantityValue", relationId: "relationIdValue (for custom price lists)" } * * @example //Product_entity, Field: PRODUCT_ID, Process: onValueChange - * var pid = ProcessHandlingUtil.getOnValidationValue(vars.get("$field.PRODUCT_ID")); + * 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(); @@ -280,7 +280,7 @@ function ProductUtils() * @example //Productprice_entity, Field: PRICELIST, Process: onValidation * var pUtils = new ProductUtils(); * var priceList = { - * priceList: ProcessHandlingUtil.getOnValidationValue(vars.get("$field.PRICELIST")) + * priceList: ProcessHandlingUtils.getOnValidationValue(vars.get("$field.PRICELIST")) * , fromQuantity: vars.get("$field.FROMQUANTITY") * , buySell: vars.get("$field.BUYSELL") * , currency: vars.get("$field.CURRENCY") diff --git a/process/Salesproject_lib/process.js b/process/Salesproject_lib/process.js index e3f10b6a49e5053b051b02a90dadc40c94dcf995..231a2538d20894fff9b6f9774568d282a3721d1c 100644 --- a/process/Salesproject_lib/process.js +++ b/process/Salesproject_lib/process.js @@ -24,8 +24,7 @@ Salesproject = { * @result {String} next valid project number */ getNextProjectNumber: function() { - var JdUtils = new JDitoUtils(); - return JdUtils.getNextUniqueNumber("PROJECTCODE", "SALESPROJECT"); + return NumberSequencingUtils().getNextUniqueNumber("PROJECTCODE", "SALESPROJECT"); }, /** @@ -36,8 +35,7 @@ Salesproject = { * @result {boolean} passed number is valid */ validateProjectNumber: function(projectNumber) { - var JdUtils = new JDitoUtils(); - return JdUtils.validateUniqueNumber(projectNumber, "PROJECTCODE", "SALESPROJECT"); + return NumberSequencingUtils.validateUniqueNumber(projectNumber, "PROJECTCODE", "SALESPROJECT"); }, /** diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js index 61a7c941e309be1fc8a8673b61865554c633b333..15ba49d93fcc1bca95135476814c1f5798f85b0e 100644 --- a/process/Sql_lib/process.js +++ b/process/Sql_lib/process.js @@ -657,6 +657,7 @@ SqlUtils.getSingleColumnType = function(fieldOrTableName, columnName, alias) { /** *Class containing utilities for SQL *@deprecated use SqlMaskingUtils + *@todo: shift to newer objects or remove *@class */ function LegacySqlUtils() @@ -1169,55 +1170,7 @@ function LegacySqlUtils() return res; } - /** - * Setzt eine Condition zusammen und liefert sie zurück - * builds a conditions and returns it - * - * @param {Object} pValue req Filtervalue - * @param {String} pCondition req variable in which the condition should be written - * @param {String} pWhere req additional condition - * @param {Integer} pSQLType opt SQLTYPES type of pValue - * @param {Array} pPreparedValues opt Value for the condition, if it's a prepared statement - * - * @return {String} - */ - this.makeCondition = function( pValue, pCondition, pWhere, pSQLType, pPreparedValues) - { - if ( pValue != "" ) - { - if ( pCondition != "" ) - pCondition += " and "; - - pCondition += pWhere; - if(pPreparedValues != undefined) - { - pPreparedValues.push([pValue, pSQLType]); - } - } - return pCondition; - } - /** - * returns a type of column in the database - * - * @param {String} pTableName req name of a table (e.g. "EVENT") OR if pColumnName is not passed table.column (e.g. "EVENT.STATUS") - * @param {String} pColumnName opt name of column (e.g. "STATUS") if in pTableName only tablename is passed - * @param {String} pAlias opt Alias to the database where the type should be loaded; default is current alias - * - * @return {String} type of column such as SQLTYPES.xyz - */ - this.getSingleColumnType = function(pTableName, pColumnName, pAlias) - { - if (pColumnName == undefined) - { - pColumnName = pTableName.substring(pTableName.indexOf(".") + 1); - pTableName = pTableName.substring(0, pTableName.indexOf(".")); - } - if (pAlias == undefined) - pAlias = db.getCurrentAlias(); - - return db.getColumnTypes(pTableName, [pColumnName], pAlias)[0]; - } /** * calls a given function for N blocks of sql-data as long as records are available or the paging-process is manually canceled * diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js index 969fe5b2f57169f259666d6e1a2e6f9059d1b40a..ace960b642dbf072b869738f86de3d5a4f9a4bc1 100644 --- a/process/Util_lib/process.js +++ b/process/Util_lib/process.js @@ -15,819 +15,407 @@ import("OfferOrder_lib"); import("Date_lib"); /** - * Class containing utility functions for images - * + * Class containing static utility functions for use with arrays * @class - * + * @static */ -function ImageUtils() -{ - var that = this; - /** - * reads a picture depending on description and icon_type from ASYS_ICONS - * @param {String} pIconType requ ICON_TYPE of ASYS_ICONS - * @param {String} pDescription requ DESCRIPTION of ASYS_ICONS - * @param {Boolean} pBase64 opt true/undefined: BINDATA (Tables, trees) or false: ID (Icons, XMPP picture) - * - * @return {String} BINDATA of ASYS_ICONS - * - * - */ - this.getIcon = function(pIconType, pDescription, pBase64) - { - var field = pBase64 || pBase64 == undefined ? "BINDATA" : "ID"; - return db.cell("select " + field + " from ASYS_ICONS where DESCRIPTION = '" + pDescription + "' and ICON_TYPE = '" + pIconType + "'", - "_____SYSTEMALIAS"); - } - - /** - * reads frequently used combination of description and bindata/id from ASYS_ICONS - * @param {String} pIconType ICON_TYPE of ASYS_ICONS - * @param {Boolean} pBase64 opt true: BINDATA or false/undefined: ID - * - * @return {String[][]} Key: Description, Value: ID - * - */ - this.getImageObject = function (pIconType, pBase64) - { - var field = pBase64 ? "BINDATA" : "ID"; - - var icons = {};//kein Array sondern Object, da es sich um ein assoziatives Array handelt - var data = db.table("select " + field + ", DESCRIPTION from ASYS_ICONS where ICON_TYPE = '" + pIconType + "'", - "_____SYSTEMALIAS"); - for (var i = 0; i < data.length; i++ ) - icons[data[i][1]] = data[i][0]; - - return icons; - } +function ArrayUtils(){ } -/** - * class containing utils for URLs - * @class - */ -function UrlUtils() -{ - var that = this; - /** - * Extracts a query param from an URL - * - * @param {String} pURL the text from which the parameter should be read - * @param {String} pParameter Name of the parameter - * (Messaging mit Default Trenner: - * - im.internal: "id=", "title_ack=", "title_dcl=", "text_ack=", "text_dcl=", "desc=", "name=", "result=", "decidable=", "icon=", "icon_ack=", "icon_dcl=" - * - im.file: "id=", "size=", "result=", "requestor=") - * @param {String} pSplit opt der String, which marks the end of the param value, standard is '&' - * - * @return {String} the extracted value or null if param not existent - * - * - */ - this.extractBlock = function(pURL, pParameter, pSplit) - { - var idLeft = pURL.indexOf(pParameter, 0); - if(idLeft <= -1) - return null; // Kein Block vorhanden - if(pSplit != undefined) var idRight = pURL.indexOf(pSplit, idLeft); - if(pSplit == undefined || idRight <= -1) idRight = pURL.indexOf("&", idLeft); - if(idRight <= -1) - idRight = pURL.length; - - return pURL.substring(idLeft + pParameter.length, idRight); - } - - /** - * opens the browser - * - * @param {String} pUrl req - * @param {String} pBrowserType opt Browser type 1 = Native (Internet Explorer Rendering Engine) - * ,2 = JavaFX (WebKit Rendering Engine), 3 = Native, if possible. - * default is 2; - * - * @return {void} - * - * - */ - this.openUrl = function(pUrl, pBrowserType) - { - if ( pUrl != "" ) - { - if (pUrl.substr(0, 4) != "http" - && pUrl.substr(0, 4) != "ftp:" - && pUrl.substr(0, 5) != "file:") - { - pUrl = "http://" + pUrl; - } - var browserType = pBrowserType || 2; - swing.openBrowser(true, false, encodeURI(pUrl), swing.WINDOW_CURRENT, [], browserType); - } - } -} /** - * Class containing String utility functions - * @class + * sorts a two dimensional array by the given index + * + * @param {Array} targetArray the Array to be sorted + * @param {String} index the index of the field to sort by + * @param {Boolean} [sortAsc=false] TRUE sorts ascending, FALSE sorts decending + * @param {Boolean} [isNumber=false] TRUE sorts numerical, FALSE or undefined sorts alphanumerical + * + * @return targetArray + * + * @throws {RangeError} if index is outside pArray.length + * * */ -function StringUtils() -{ - var that = this; - /** - * validates if a string is a UUID according to versions 1 through 5 of RFC4122 - * @param {String} pUUID String to be validated - * @param {bool} pIgnoreCase true if case sensitivty isn't needed - * - * @return {Boolean} ob es sich um eine UUID handelt - * - * - */ - this.isValidUUID = function(pUUID, pIgnoreCase) - { - if (pIgnoreCase) - { - pUUID = pUUID.toLowerCase(); +ArrayUtils.sort2d = function(targetArray, index, sortAsc, isNumber){ + if (targetArray.length == 0) + return targetArray; + if (targetArray[0].length == 0) + return targetArray; + if (index >= targetArray[0].length) + throw new RangeError("Index Out Of Bounds: " + index + " >= " + targetArray[0].length); + + var sortFn = function (x, y){ + if ( isNumber ) { + xx = Number(x[index]); + yy = Number(y[index]); } - - return new RegExp(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/).test(pUUID); - } - /** - * Returns wether a string starts with given string or not. - * @param pString String, which is being searched in - * @param pStringToSearch String, which is being searched - * - * @return {Boolean} - * - * - */ - this.stringStatrsWith = function(pString, pStringToSearch) - { - return pString.indexOf(pStringToSearch) === 0; - } - /** - * Splits street and buildingnumber - * - * @param {String} pAddress req Address - * - * @return {Array} [ Street, BuildingNr ] - * - * - */ - this.splitAddress = function(pAddress) - { - var ret = ["",""]; - if(pAddress != "") - { - var arr = pAddress.match( /^[^0-9]+|[0-9]+.*$/g); - ret[0] = arr[0].trim(); - if(arr[1]) - ret[1] = arr[1].trim(); + else { + xx = x[index]; + yy = y[index]; + + xx = xx.toLowerCase(); + xx = xx.replace(/ä/g,"ae"); + xx = xx.replace(/ö/g,"oe"); + xx = xx.replace(/ü/g,"ue"); + xx = xx.replace(/ß/g,"ss"); + + yy = yy.toLowerCase(); + yy = yy.replace(/ä/g,"ae"); + yy = yy.replace(/ö/g,"oe"); + yy = yy.replace(/ü/g,"ue"); + yy = yy.replace(/ß/g,"ss"); } - return ret; + if (xx == yy) + return 0; + if (xx < yy) + return (sortAsc ? -1 : 1); + return (sortAsc ? 1 : -1); } - /** - * Generates the string which shows, who created the dataset and who edited it - * - * @param {String} pDateNew req Date of creation - * @param {String} pUserNew req name of the user, who created the dataset - * @param {String} pDateEdit req date of the last change - * @param {String} pUserEdit req name of last editor - * - * @return {String} - * - * - */ - this.label_new_edit = function(pDateNew, pUserNew, pDateEdit, pUserEdit) - { - var retst = translate.withArguments("created on %0 by %1", [ datetime.toDate(pDateNew, translate.text("yyyy-MM-dd")), pUserNew]); - if (pDateEdit != undefined && pDateEdit != "") retst += " | " + translate.withArguments("changed on %0 by %1", [ datetime.toDate(pDateEdit, translate.text("yyyy-MM-dd")), pUserEdit]); - return(retst) - } - - /** - * uses the right translate method, depending on the parameters - * - * @param {String} pText string to be translated - * @param {String} pLocale locale for translating - * - * @return {String} - * - * - */ - this.translateStr = function( pText, pLocale ) - { - if ( pLocale == undefined ) - return translate.text(pText); - else - return translate.text(pText, pLocale) - } - - + targetArray.sort(sortFn); + return targetArray } -/** - * Class containing utility functions for use with arrays - * @class - */ -function DataUtils() -{ - var that = this; - /** - * sorts a two dim array by the given index - * - * @param {Array} pArray req the Array to be sorted - * @param {String} pIndex req the index of the field to sort by - * @param {Boolean} pUp req TRUE sorts ascending, FALSE sorts decending - * @param {Boolean} isNumber TRUE sorts numerical, FALSE or undefined sorts alphanumerical - * - * @return void - * - * @throws {Error} if index is outside pArray.length - * - * - */ - this.array_mDimSort = function(pArray, pIndex, pUp, isNumber) - { - if (pArray.length == 0) - return; - - if (pArray[0].length == 0) - return; - if (pIndex >= pArray[0].length) - throw new Error("Index Out Of Bounds: " + pIndex + " >= " + pArray[0].length); - - /* - * the sort function - * - * @param {String} x req value 1 - * @param {String} y req value 2 - * - * @return {Integer} - * - * @memberof array_mDimSort - */ - function array_mDimSorter(x, y) - { - if ( isNumber ) - { - xx = Number(x[pIndex]); - yy = Number(y[pIndex]); - } - else - { - xx = x[pIndex]; - yy = y[pIndex]; - - //Umlaute austauschen - xx = xx.toLowerCase(); - xx = xx.replace(/ä/g,"ae"); - xx = xx.replace(/ö/g,"oe"); - xx = xx.replace(/ü/g,"ue"); - xx = xx.replace(/ß/g,"ss"); - - yy = yy.toLowerCase(); - yy = yy.replace(/ä/g,"ae"); - yy = yy.replace(/ö/g,"oe"); - yy = yy.replace(/ü/g,"ue"); - yy = yy.replace(/ß/g,"ss"); - } - if (xx == yy) - return 0; - - if (xx < yy) - return (pUp ? -1 : 1); - - return (pUp ? 1 : -1); +/** +* sorts an array up to 6 columns with sortorder +* +* @param {Array} targetArray req the array with data +* @param {Integer} us req the Sortorder for Column 1 = Param u (1=asc, -1=desc) +* @param {Integer} u req the 1 Column +* @param {Integer} vs opt the Sortorder for Column 2 = Param v (1=asc, -1=desc) +* @param {Integer} v opt the 2 Column +* @param {Integer} ws opt the Sortorder for Column 3 = Param w (1=asc, -1=desc) +* @param {Integer} w opt the 3 Column +* @param {Integer} xs opt the Sortorder for Column 4 = Param x (1=asc, -1=desc) +* @param {Integer} x opt the 4 Column +* @param {Integer} ys opt the Sortorder for Column 5 = Param y (1=asc, -1=desc) +* @param {Integer} y opt the 5 Column +* @param {Integer} zs opt the Sortorder for Column 6 = Param z (1=asc, -1=desc) +* @param {Integer} z opt the 6 Column +* +* @return {void} +*/ +ArrayUtils.sortMulti = function(targetArray, us, u, vs, v, ws, w, xs, x, ys, y, zs, z) { + /* + * sort of a two dim array, up to 6 columns + * + * @param {String} a req value 1, first compared element + * @param {String} b req value 2, sencond compared element + * + * @return {Integer} -1 - set a below b, 0 - equal, 1 - set b below a + */ + var sortFn = function(a, b) { + var stringComparison = function(a, b) { + a = a.toLowerCase(); + a = a.replace(/ä/g,"ae"); + a = a.replace(/ö/g,"oe"); + a = a.replace(/ü/g,"ue"); + a = a.replace(/ß/g,"ss"); + + b = b.toLowerCase(); + b = b.replace(/ä/g,"ae"); + b = b.replace(/ö/g,"oe"); + b = b.replace(/ü/g,"ue"); + b = b.replace(/ß/g,"ss"); + + return( a == b ) ? 0 : ( a > b ) ? 1 : -1; } - pArray.sort(array_mDimSorter); - } - - /** - * sorts an array up to 6 columns with sortorder - * - * @param {Array} pArray req the array with data - * @param {Integer} us req the Sortorder for Column 1 = Param u (1=asc, -1=desc) - * @param {Integer} u req the 1 Column - * @param {Integer} vs opt the Sortorder for Column 2 = Param v (1=asc, -1=desc) - * @param {Integer} v opt the 2 Column - * @param {Integer} ws opt the Sortorder for Column 3 = Param w (1=asc, -1=desc) - * @param {Integer} w opt the 3 Column - * @param {Integer} xs opt the Sortorder for Column 4 = Param x (1=asc, -1=desc) - * @param {Integer} x opt the 4 Column - * @param {Integer} ys opt the Sortorder for Column 5 = Param y (1=asc, -1=desc) - * @param {Integer} y opt the 5 Column - * @param {Integer} zs opt the Sortorder for Column 6 = Param z (1=asc, -1=desc) - * @param {Integer} z opt the 6 Column - * - * @return {void} - * - * - */ - this.sortArray = function(pArray, us, u, vs, v, ws, w, xs, x, ys, y, zs, z) - { - pArray.sort(_sortmulti); - /* - * sort of a two dim array, up to 6 columns - * - * @param {String} a req value 1, first compared element - * @param {String} b req value 2, sencond compared element - * - * @return {Integer} -1 - set a below b, 0 - equal, 1 - set b below a - */ - function _sortmulti(a, b) - { - var stringComparison = function(a, b) - { - a = a.toLowerCase(); - a = a.replace(/ä/g,"ae"); - a = a.replace(/ö/g,"oe"); - a = a.replace(/ü/g,"ue"); - a = a.replace(/ß/g,"ss"); - - b = b.toLowerCase(); - b = b.replace(/ä/g,"ae"); - b = b.replace(/ö/g,"oe"); - b = b.replace(/ü/g,"ue"); - b = b.replace(/ß/g,"ss"); - return( a == b ) ? 0 : ( a > b ) ? 1 : -1; - } - - - var swap=0; + var swap=0; - if (isNaN(a[u] - b[u])) // if there is a string in the first compared element - if( isNaN(a[u]) && isNaN(b[u]) ) // if both are strings, - swap = stringComparison(a[u], b[u]); // then: true - false = 1; false - true = -1 - else - swap = (isNaN(a[u]) ? 1 : -1); + if (isNaN(a[u] - b[u])) // if there is a string in the first compared element + if( isNaN(a[u]) && isNaN(b[u]) ) // if both are strings, + swap = stringComparison(a[u], b[u]); // then: true - false = 1; false - true = -1 else - swap = (a[u] - b[u]); + swap = (isNaN(a[u]) ? 1 : -1); + else + swap = (a[u] - b[u]); - if ((v == undefined) || (swap != 0)) - return swap * us; - else - if (isNaN(a[v] - b[v])) - if ((isNaN(a[v])) && (isNaN(b[v]))) - swap = stringComparison(a[v], b[v]); - else - swap = (isNaN(a[v]) ? 1 : -1); + if ((v == undefined) || (swap != 0)) + return swap * us; + else + if (isNaN(a[v] - b[v])) + if ((isNaN(a[v])) && (isNaN(b[v]))) + swap = stringComparison(a[v], b[v]); else - swap = (a[v] - b[v]); + swap = (isNaN(a[v]) ? 1 : -1); + else + swap = (a[v] - b[v]); - if ((w == undefined) || (swap != 0)) - return swap * vs; - else - if (isNaN(a[w] - b[w])) - if ((isNaN(a[w])) && (isNaN(b[w]))) - swap = stringComparison(a[w], b[w]); - else - swap = (isNaN(a[w]) ? 1 : -1); + if ((w == undefined) || (swap != 0)) + return swap * vs; + else + if (isNaN(a[w] - b[w])) + if ((isNaN(a[w])) && (isNaN(b[w]))) + swap = stringComparison(a[w], b[w]); else - swap = (a[w] - b[w]); + swap = (isNaN(a[w]) ? 1 : -1); + else + swap = (a[w] - b[w]); - if ((x == undefined) || (swap != 0)) - return swap * ws; - else - if (isNaN(a[x] - b[x])) - if ((isNaN(a[x])) && (isNaN(b[x]))) - swap = stringComparison(a[x], b[x]); - else - swap = (isNaN(a[x]) ? 1 : -1); + if ((x == undefined) || (swap != 0)) + return swap * ws; + else + if (isNaN(a[x] - b[x])) + if ((isNaN(a[x])) && (isNaN(b[x]))) + swap = stringComparison(a[x], b[x]); else - swap = (a[x] - b[x]); + swap = (isNaN(a[x]) ? 1 : -1); + else + swap = (a[x] - b[x]); - if ((y == undefined) || (swap != 0)) - return swap * xs; - else - if (isNaN(a[y] - b[y])) - if ((isNaN(a[y])) && (isNaN(b[y]))) - swap = stringComparison(a[y], b[y]); - else - swap = (isNaN(a[y]) ? 1 : -1); + if ((y == undefined) || (swap != 0)) + return swap * xs; + else + if (isNaN(a[y] - b[y])) + if ((isNaN(a[y])) && (isNaN(b[y]))) + swap = stringComparison(a[y], b[y]); else - swap = (a[y] - b[y]); + swap = (isNaN(a[y]) ? 1 : -1); + else + swap = (a[y] - b[y]); - if ((z == undefined) || (swap != 0)) - return swap * ys; - else - if(isNaN(a[z] - b[z])) - if((isNaN(a[z])) && (isNaN(b[z]))) - swap = stringComparison(a[z], b[z]); - else - swap = (isNaN(a[z]) ? 1 : -1); + if ((z == undefined) || (swap != 0)) + return swap * ys; + else + if(isNaN(a[z] - b[z])) + if((isNaN(a[z])) && (isNaN(b[z]))) + swap = stringComparison(a[z], b[z]); else - swap = (a[z] - b[z]); - - return swap * zs; - } - - } - - /** - * removes an element from an array - * - * @param {Array} pArray req Array from which the element should be removed - * @param {String} pElement req index of the element which should be removed - * - * @return {Array} array without the removed element - * - * - */ - this.removeElement = function(pArray, pElement) - { - return pArray.splice(pElement, 1); - } - - /** - * returns all elemtens not in pSource - * - * @param {Array} pSource req this elements are being searched - * @param {Array} pReference req in this array the search is done - * @param {Boolean} pIgnoreCase opt - * - * @return {Array[]} resultIn - * - * - */ - this.notIn = function(pSource, pReference, pIgnoreCase) - { - var resultIn = new Array(); - for (var i = 0; i < pSource.length; i++) - { - if (!that.hasElement(pReference, pSource[i], pIgnoreCase)) - resultIn.push(pSource[i]); - } - - return resultIn; - } - - /** - * returns if an element is in an array. - * - * @param {Array} pArray req array which should be searched in - * @param {String} pElement req Element which should looked for - * @param {Boolean} pIgnoreCase opt - * - * @return {Boolean} true if it has the element - * - * - */ - this.hasElement = function(pArray, pElement, pIgnoreCase) - { - for (var i = 0; i < pArray.length; i++) - { - if ( pIgnoreCase != undefined && pIgnoreCase ) - { - if (pArray[i].toLowerCase() == pElement.toLowerCase()) - return true; - } - else - if (pArray[i] == pElement) - return true; - } + swap = (isNaN(a[z]) ? 1 : -1); + else + swap = (a[z] - b[z]); - return false; + return swap * zs; } - - /** - * concats arrays column by column - * see example for more details - * logging.show(JSON.stringify(concatArrayColumns(a, b))); - * //[["a","A"],["b","B"],["c","C"]] - * - * logging.show(JSON.stringify(a.concat(b))); - * //["a","b","c","A","B","C"] - * @param {Array} pArray1 req you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want - * @param {Array} pArrayN req you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want - * - * - * - * @return {Array} Beschreibung - * - * - */ - this.concatArrayColumns = function(pArray1, pArrayN) - { - var res = []; - for (var i = 0, l = arguments.length; i < l; i++)//this function can handle an "unlimited" amount of functionparams - { - var inpArr = arguments[i]; - for (var ii = 0, ll = inpArr.length; ii < ll; ii++) - { - if (res[ii] == undefined) - res[ii] = []; - - res[ii] = res[ii].concat(inpArr[ii]); - } - } - return res; - } - - /** - * function to determine, if an object is truly an Array - * @param {Object} pArray - * @return {Boolean} returns wether an Object is an Array (true) or not (false) - * - * - */ - this.isArray = function(pArray) - { - return (pArray && typeof pArray === "object" && typeof pArray.length === "number" && !(pArray.propertyIsEnumerable('length'))) - } + targetArray.sort(sortFn); + return targetArray; } -/** - * Class containing utility functions for table components - * @class - */ -function TableUtils() -{ - var that = this; - /** - * Moving Tablerows Sort field up or down a step - * - * @param {String} pTable req Table - * @param {String} pTableComp req name of the table comp - * @param {String} pCountField req sort field name z.B. 'SORT' - * @param {String} pDirection req 'up' oder 'down' - * @param {String} pCondition opt - * - * @return {void} - * - * - */ - this.moveRow = function(pTable, pTableComp, pCountField, pDirection, pCondition) - { - that.sortRow(pTable, pCountField, pCondition); - - if (pCondition == undefined || pCondition == "") - pCondition = ""; - else - pCondition = " and " + pCondition; - - var id = text.decodeFirst(vars.getString(pTableComp)); - var col = [pCountField]; - var nextval; - var typ = db.getColumnTypes( pTable, col); - var oldval = db.cell("select " + pCountField + " from " + pTable + " where " + pTable + "ID = '" + id + "'"); - - if (pDirection == "up") - { - nextval = db.cell("select max(" + pCountField + ") from " + pTable - + " where " + pCountField + " < " + oldval + pCondition); - } - else - { - nextval = db.cell("select min(" + pCountField + ") from " + pTable - + " where " + pCountField + " > " + oldval + pCondition); - } - if ( nextval == "" ) - nextval = 0; - var nextID = db.cell("select " + pTable + "ID from " + pTable + " where " + pCountField + " = " + nextval + pCondition); - db.updateData(pTable, col, typ, [oldval], pTable + "ID = '" + nextID + "'") - db.updateData(pTable, col, typ, [nextval], pTable + "ID = '" + id + "'") - } - /** - * activates Up/Down buttons - * - * @param {String} pTable req Table - * @param {String} pTableComp req name of the table comp - * @param {String} pSortfield req sort field name z.B. 'SORT' - * @param {String} pDirection req 'up' or 'down' - * @param {String} pCondition opt - * - * @return {Boolean} true if active - * - * - */ - this.moveActive = function(pTable, pTableComp, pSortfield, pDirection, pCondition) - { - var oldval = "min"; - var minmax = "min"; - var id = text.decodeFirst(vars.getString(pTableComp)); - - if (pCondition == undefined || pCondition == "") - pCondition = ""; - else - pCondition = " where " + pCondition; - - if (id != "" && swing.getTableDataByAttribute(pTableComp, swing.INSERTED ).length == 0 && vars.getString("$sys.workingmode") == swing.FRAMEMODE_EDIT ) - { - oldval = db.cell("select " + pSortfield + " from " + pTable + " where " + pTable + "ID = '" + id + "'"); - - if(pDirection == "down") - minmax = "max"; - - minmax = db.cell("select " + minmax + "(" + pSortfield + ") from " + pTable + pCondition); - } - return (oldval != minmax || oldval == "" || minmax == "" ) - } +/** +* removes an specific element from an array +* +* @param {Array} targetArray Array from which the element should be removed +* @param {String} elementPos index of the element which should be removed +* +* @return {Array} array containing the deleted element +*/ +ArrayUtils.removeElement = function(targetArray, elementPos) { + return targetArray.splice(elementPos, 1); } + /** - * Class containing utility functions for use with base64 and binaries - * @class + * concats arrays column by column; + * see example for more details + * + * @param {Array} array1 you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want + * @param {Array} arrayN you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want + * + * @return {Array} concatenated array + * + * @example + * var a = ["a", "b", "c"]; + * var b = ["A", "B", "C"]; + * logging.show(JSON.stringify(concatArrayColumns(a, b))); + * //[["a","A"],["b","B"],["c","C"]] + * + * logging.show(JSON.stringify(a.concat(b))); + * //["a","b","c","A","B","C"] */ -function BinaryUtils() -{ - var that = this; - /** - * decodes 64Based String - * - * @param {String} input req the string - * - * @return {String} decoded String - * - * - */ - this.decode64 = function(input) - { - var charset = new Configuration().getOption("Base64Charset"); +ArrayUtils.concatColumns = function(array1, arrayN) { + var res, i, ii, l, ll, inpArr; + res = []; - if ( charset == "" ) - charset = "ISO-8859-15"; - return util.decodeBase64String(input, charset); - } - /** - * returns the original size of a B64 coded String in bytes - * - * @param {String} pBase64str req base64 encoded string - * - * @return {Number} size of the string in bytes - * - * - */ - this.getBase64lengthBytes = function(pBase64str) - { - var res = pBase64str.length; - res = eMath.divDec(res, 4); - res = Math.ceil(res); - res = eMath.mulInt(3, res); - - return +res;//cast und return - } - - /** - * sanitizes the filelist gotten from Drag&Drop and returns it. - * - * @param {String[]} pFilelist req Die Filelist als Stringarray. - * - * - */ - this.sanitizeFilelist = function(pFilelist) - { - var sanitized = []; - - if(pFilelist != undefined && pFilelist.length > 0) - { - for(let i = 0; i < pFilelist.length; i++) - { - if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_ISDIRECTORY, [pFilelist[i]]) == "false") - sanitized.push(pFilelist[i]); - } + for (i = 0, l = arguments.length; i < l; i++) {//this function can handle an "unlimited" amount of functionparams + inpArr = arguments[i]; + for (ii = 0, ll = inpArr.length; ii < ll; ii++) { + if (res[ii] == undefined) + res[ii] = []; + + res[ii] = res[ii].concat(inpArr[ii]); } - - return sanitized; } - /** - * reads the folders in a filelist and returns their contained files - * - * @param {String[]} pFilelist req Die Filelist als Stringarray. - * - * - */ - this.resolveSubfolders = function(pFilelist) - { - //read systemconfig for D&D - var cfg = new Configuration(); - var filesize = (cfg.getOption("dnd.maxFileSize") *1000 *1000 ); - var fileamount = 30; - var files = []; - var folders = []; + return res; +} - if(pFilelist != undefined && pFilelist.length > 0) - { - //separate files and folders - for (let i = 0; i < pFilelist.length; i++) - { - if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_ISDIRECTORY, [pFilelist[i]]) == "false") - files.push(pFilelist[i]); - else - folders.push(pFilelist[i]); - } - //resovle Files in Folders - if(folders.length > 0) - { - for (let j = 0; j < folders.length; j++) - { - var resolvedFiles = swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_LISTFILES, [folders[j]]); - if(resolvedFiles.length > 0) - { - for (let k = 0; k < resolvedFiles.length; k++) resolvedFiles[k] = folders[j] + "\\" + resolvedFiles[k]; - files = files.concat(resolvedFiles); - } +/** + * returns if an element is in an array; + * this is needed because there is currently no support for Array.prototype.includes() and we cannot easily use a polyfill and extend the Array.prototype + * + * @param {Array} targetArray array where the element should be searched in + * @param {any primitive type} element the element which should be looked for + * @param {Boolean} [ignoreStringCase=false] if you've got a string array y + * + * @return {Boolean} true if it has the element + */ +ArrayUtils.hasElement = function(targetArray, element, ignoreStringCase) { + var i, l; + if (ignoreStringCase)//do only once to save ressources and not for every array element + element = element.toString().toLowerCase(); + for (i = 0, l = targetArray.length; i < l; i++) { + if (ignoreStringCase) { + if (targetArray[i].toString().toLowerCase() == element) + return true; + } + else { + if (targetArray[i] == element) + return true; + } + } + return false; +} + +/** + * Class containing utility functions for use with JSON + * @class + * @static + */ +function JSONUtils() { +} + +/** + * A custom JSON.stringify() to + * - keep the functions as string + * - stringify JavaArrays + * - stringify undefined as undefined and not as null + * + * @param {Object} obj the object to stringify + * + * @return {String} the stringified object as string representation + */ +JSONUtils.customStringify = function(obj) { + //stringify part from JSON polyfill: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON + var toString = Object.prototype.toString; + var escMap = { + '"': '\\"', + '\\': '\\\\', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t' + }; + var escFunc = function (m) { + return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); + }; + var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g; + + var stringify = function (value){ + //because: "undefined == null" is true + if (value === undefined) { + return 'undefined'; + } else if (value == null) { + return 'null'; + } else if (typeof value === 'number') { + return isFinite(value) ? value.toString() : 'null'; + } else if (typeof value === 'boolean') { + return value.toString(); + } else if (typeof value === 'object') +{ + //check with "hasOwnProperty" because in JavaArrays using value.toJSON would cause an exception + if (toString.call(value) != '[object Map]' && value.hasOwnProperty("toJSON") && typeof value.toJSON === 'function') { + return value.toString(); + } else if (toString.call(value) === '[object Array]') { + var res = '['; + for (var i = 0; i < value.length; i++) + res += (i ? ', ' : '') + stringify(value[i]); + return res + ']'; + } else if (toString.call(value) === '[object Object]' || toString.call(value) === '[object Map]') { + var tmp = []; + for (var k in value) { + if (hasOwnProperty.call(value, k)) + tmp.push(stringify(k) + ': ' + stringify(value[k])); } + return '{' + tmp.join(', ') + '}'; + //custom addition to stringify Rhino JavaArrays + } else if (toString.call(value) === '[object JavaArray]') { + return value.toSource().toString(); } - - //recursive call, cause more subfolders may be present - if( that.hasSubfolders(files) ) - files = that.resolveSubfolders(files); } - - //filter out files, that exceed the size limit - var retfiles = []; - for (let m = 0; m < files.length; m++) + //custom addition for function transform + //JSON.stringify returns null on functions (default) + //instead return source code of funciton for callback-functions + else if (typeof value === 'function') { - if(retfiles.length == fileamount) - break; - - if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_GETLENGTH, [files[m]]) <= filesize) - retfiles.push(files[m]); + return value.toString(); } - logging.log(retfiles) - return retfiles; + return '"' + value.toString().replace(escRE, escFunc) + '"'; } - /** - * Checks if subfolders are present in a filelist - * - * @param {String[]} pFilelist req the file list as string array - * - * - */ - this.hasSubfolder = function(pFilelist) - { - var ret = false; - if(pFilelist != undefined && pFilelist.length > 0) - { - for (let i = pFilelist.length; i >= 0; i--) - { - if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_ISDIRECTORY, [pFilelist[i]]) == "true") - { - ret = true; - break; - } - } - } - return ret; + return stringify(obj); +} + +/** + * Class containing functions for sequential numbers + * @class + * @static + */ +function NumberSequencingUtils(){ +} + +/** + * Delivers the next unique number + * + * @param {String} pColumn database column that contains unique numbers + * @param {String} pTable database table + * @param {Number} [pStartNumber=1000] number to start numeration + * @param {String} [pCondition=no condition] SQL Where Conditon + * + * @result {String} next valid number + */ +NumberSequencingUtils.getNextUniqueNumber = function(pColumn, pTable, pStartNumber, pCondition) { + var maxNum = NumberSequencingUtils.getMaxUniqueNumber(pColumn, pTable, pCondition); + if(maxNum == "0") { + if(pStartNumber == undefined) pStartNumber = 1000; + return pStartNumber; } + return eMath.addInt(maxNum, "1");//increment currently highest number +} +/** + * Checks if the passed number is valid (has to be unique) + * + * @param {String} pNumber number to check + * @param {String} pColumn req database column that contains unique numbers + * @param {String} pTable req database table + * @param {String} pCondition opt SQL Where Conditon + * + * @result {boolean} passed number is valid + */ +NumberSequencingUtils.validateUniqueNumber = function(pNumber, pColumn, pTable, pCondition) { + var maxNum = NumberSequencingUtils.getMaxUniqueNumber(pColumn, pTable, pCondition); + return Number(pNumber) > Number(maxNum); +} + +/** + * Delivers the hightest number currently stored in database + * + * @param {String} pColumn req database column that contains unique numbers + * @param {String} pTable req database table + * @param {String} pCondition opt SQL Where Conditon + * + * @result {String} hightest number + */ +NumberSequencingUtils.getMaxUniqueNumber = function(pColumn, pTable, pCondition) { + var condition = ""; + if(pCondition != undefined) + condition += " where " + pCondition; + var maxNum = db.cell("select max(" + pColumn + ") from " + pTable + condition); + return maxNum == "" ? "0" : maxNum; } + /** * Class containing miscellaneous utiltiy function extending JDito * @class + * @deprecated + * @todo: remove */ function JDitoUtils() { - var that = this; - /** - * A wrapper for process.executeScript - * - * @param {String} pIdentifier req The identifier for the script (for better error logging) - * @param {String} pScript req The script to execute - * @param {Object} pLocalVariables opt The local variables which can be accessed within the script - * @param {Array} pImports opt To add additional imports which will be prepended to the script - * @param {Boolean} pWrapIntoResult opt To wrap pScript into 'result.string("***")' - * @param {String} pAlias opt The alias to use within the script - * - * @return {Object} The result of the script - * - */ - this.evalScript = function(pIdentifier, pScript, pLocalVariables, pImports, pWrapIntoResult, pAlias) - { - var defaultImports = ["system.result", "system.vars"]; - var importsToPrepend = []; - - // add pImports to to defaultImports - if (pImports != undefined) defaultImports = defaultImports.concat(pImports); - - // go trough all defaultImports an check if they already exists - for (var i = 0; i < defaultImports.length; i++ ) - { - var cImport = "import(\"" + defaultImports[i] + "\");"; - - if (pScript.indexOf(cImport) == -1) - { - // If the current default import was not found within the script it will be prepended - importsToPrepend.push(cImport); - } - } - - // join the imports to prepend and place them in front of a new variable - var script2Execute = importsToPrepend.join("\r\n") + (importsToPrepend.length > 0 ? "\r\n" : ""); - - // Check if the script needs to be wrapped into result.string() (e.g. (function(){return "result";})()) - if (pWrapIntoResult) - script2Execute += "\r\nresult.string(" + pScript + ");\r\n"; // Wrap into 'result.string("***")' - else - script2Execute += pScript; // Just add the script after the prepended imports - - return process.executeScript("[evalScript]" + pIdentifier, script2Execute, (pLocalVariables != undefined && pLocalVariables != null ? pLocalVariables : {}), (pAlias != undefined && pAlias != null ? pAlias : null)); - } - /** * Returns a list of aliasNames with a given type * ignores errors when an alias is not confgiured (empty) @@ -863,617 +451,36 @@ function JDitoUtils() return dbAliases; } - - /** - * returns a sorted and translated list of countries ; - * DE, AT and CH are at the beginning of the list, if pData is undefined - * - * @param {String} pLanguage opt Sprache - * @param {String[][]} pData opt if the data should not be gathered automatically - * it can be given to this parameter. - * with this, the function can be used to translate country names - * - * @return {String[][]} consisting of ISO2 and Name - * - * - */ - this.getCountries = function(pLanguage, pData) - { - var data, temp; - if(pData == undefined) - { - data = [ ["DE", "Deutschland"], ["AT", "Österreich"], ["CH", "Schweiz"] ]; - temp = db.table("select COUNTRYINFO.ISO2, COUNTRYINFO.NAME_DE " - + "from COUNTRYINFO " - + "where COUNTRYINFO.ISO2 not in ('DE', 'AT', 'CH')"); - } - else - { - data = []; - temp = pData; - } - - for(var i = 0; i < data.length; i++) - data[i][1] = translateStr(data[i][1], pLanguage); - for(let i = 0; i < temp.length; i++) - temp[i][1] = translateStr(temp[i][1], pLanguage).trim(); - - //Sortierung nach der Übersetzung - array_mDimSort(temp, 1, true, false); - - return data.concat(temp); - } - - /** - * Delivers the next unique number - * - * @param {String} pColumn req database column that contains unique numbers - * @param {String} pTable req database table - * @param {Number} pStartNumber opt number to start numeration - * @param {String} pCondition opt SQL Where Conditon - * - * @result {String} next valid number - */ - this.getNextUniqueNumber = function(pColumn, pTable, pStartNumber, pCondition){ - var maxNum = that.getMaxUniqueNumber(pColumn, pTable, pCondition); - - if(maxNum == "0") - { - if(pStartNumber == undefined) pStartNumber = 1000; - return pStartNumber; - } - - return eMath.addInt(maxNum, "1");//increment currently highest number - } - - /** - * Checks if the passed number is valid (has to be unique) - * - * @param {String} pNumber number to check - * @param {String} pColumn req database column that contains unique numbers - * @param {String} pTable req database table - * @param {String} pCondition opt SQL Where Conditon - * - * @result {boolean} passed number is valid - */ - this.validateUniqueNumber = function(pNumber, pColumn, pTable, pCondition){ - var maxNum = that.getMaxUniqueNumber(pColumn, pTable, pCondition); - - return Number(pNumber) > Number(maxNum); - } - - /** - * Delivers the hightest number currently stored in database - * - * @param {String} pColumn req database column that contains unique numbers - * @param {String} pTable req database table - * @param {String} pCondition opt SQL Where Conditon - * - * @result {String} hightest number - */ - this.getMaxUniqueNumber = function(pColumn, pTable, pCondition){ - - var condition = ""; - if(pCondition != undefined) - condition += " where " + pCondition; - - var maxNum = db.cell("select max(" + pColumn + ") from " + pTable + condition); - - return maxNum == "" ? "0" : maxNum; - } } -/** - * Class containing utility functions for use with JSON - * @class - */ -function JSONUtils() -{ - /** - * A custom JSON.stringify() to - * - keep the functions as string - * - stringify JavaArrays - * - stringify undefined as undefined and not as null - * - * @param {Object} pObj req The object to stringify - * - * @return {String} The stringified string - * - * - */ - this.stringifyJSON = function(pObj) - { - //stringify part from JSON polyfill: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON - var toString = Object.prototype.toString; - var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'}; - var escFunc = function (m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); }; - var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g; - - var stringify = function (value){ - //because: "undefined == null" is true - if (value === undefined) { - return 'undefined'; - } else if (value == null) { - return 'null'; - } else if (typeof value === 'number') { - return isFinite(value) ? value.toString() : 'null'; - } else if (typeof value === 'boolean') { - return value.toString(); - } else if (typeof value === 'object') - { - //check with "hasOwnProperty" because in JavaArrays using value.toJSON would cause an exception - if (toString.call(value) != '[object Map]' && value.hasOwnProperty("toJSON") && typeof value.toJSON === 'function') { - return value.toString(); - } else if (toString.call(value) === '[object Array]') { - var res = '['; - for (var i = 0; i < value.length; i++) - res += (i ? ', ' : '') + stringify(value[i]); - return res + ']'; - } else if (toString.call(value) === '[object Object]' || toString.call(value) === '[object Map]') { - var tmp = []; - for (var k in value) { - if (hasOwnProperty.call(value, k)) - tmp.push(stringify(k) + ': ' + stringify(value[k])); - } - return '{' + tmp.join(', ') + '}'; - //custom addition to stringify Rhino JavaArrays - } else if (toString.call(value) === '[object JavaArray]') { - return value.toSource().toString(); - } - } - //custom addition for function transform - //JSON.stringify returns null on functions (default) - //instead return source code of funciton for callback-functions - else if (typeof value === 'function') - { - return value.toString(); - } - - return '"' + value.toString().replace(escRE, escFunc) + '"'; - } - - return stringify(pObj); - } - /** - * checks if pStr is JSON - * - * @param {pStr} - * - * @return false or parsed JSON - * - * - */ - this.isJSON = function(pStr) - { - if (typeof(pStr) != "string") - return false; - - if (pStr[0] != "{") - return false; - - if (pStr[pStr.length-1] != "}") - return false; - - try - { - var parse = JSON.parse(pStr); - return parse; - } - catch (ex) - { - return false; - } - return parse; - } -} /** - * Class containing utility functions for copying modules + * Class containing String utility functions * @class + * @deprecated + * @todo: remove this funciton */ -function CopyModuleUtils() +function StringUtils(){} { - var that = this; /** - * opens the new created modules in neonClient - * - * @param {String} pNeonContext req Name of the neon context that should be opened - * @param {Object} pModulesMapping req ModulesMapping object created by method copyModule - * - * @example var ModulesMapping = CMUtils.copyModule(InputMapping); - * - * CMUtils.openNewModules("Offer_context", ModulesMapping); - */ - this.openNewModules = function(pNeonContext, pModulesMapping) - { - if(pModulesMapping != undefined) - { - var rootModule = Object.keys(pModulesMapping)[0]; - if(pModulesMapping[rootModule].DataRows != undefined) - { - var newids = []; - - for(var row in pModulesMapping[rootModule].DataRows) - { - newids.push(pModulesMapping[rootModule].DataRows[row].newPrimaryKey); - } - - if(newids.length > 0) - neon.openContext(pNeonContext, newids, neon.OPERATINGSTATE_VIEW, null); - } - } - } - - /** - * Creates a copy of a specified module together with specified subordinated modules. <br> - * The structure of the input mapping object is the following: <br> - * pInputMapping { - * (only one rootModule allowed) - * $rootModule$: { - * condition: "sqlWhereCondition" - * , ValueMapping: {$colName$: "value"} - * , destinationModuleName: "destinationModuleName" - * , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } - * , SubModules: { - * $Module$:{ - * condition: "sqlWhereCondition" - * , ValueMapping: {$colName$: "value"} - * , destinationModuleName: "destinationModuleName" - * , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } - * , SubModules: {...} - * } - * } - * } - * } - * - * @param {Object} pInputMapping - * - * @example var CMUtils = new CopyModuleUtils(); + * uses the right translate method, depending on the parameters * - * var InputMapping = { + * @param {String} pText string to be translated + * @param {String} pLocale locale for translating * - * "OFFER": { - * condition: "OFFERID = '" + vars.get("$field.OFFERID") + "'" - * ,SubModules:{ - * "OFFERITEM": { - * condition: "OFFER_ID = '" + vars.get("$field.OFFERID") + "' order by ITEMSORT" - * } - * } - * } - * } + * @return {String} * - * CMUtils.copyModule(InputMapping); + * */ - this.copyModule = function(pInputMapping) + this.translateStr = function( pText, pLocale ) { - var AliasDefinitionStructure = project.getAliasDefinitionStructure("Data_alias", null); - var ModulesMapping = {}; - var statements = []; - - buildMapping( pInputMapping ); - buildStatements( ModulesMapping ); - - if(statements.length > 0) - db.inserts( statements ); - - return ModulesMapping; - - /** - * Builds a mapping Object for the copyModule Method. <br> - * The structure of the Object is the following: <br> - * ModulesMapping = { - * - * $rootModule$: { ($$ marks an object property) - * - * (ModuleMapping) - * name: "moduleName" - * , destinationModuleName: "destinationModuleName" - * , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } - * , ValueMapping: {$colName$: "value"} - * , dataRows:{ - * (ModuleRowMapping) - * $rowid$: { - * name: "moduleName" - * , oldPrimaryKey: "oldPrimaryKeyValue" - * , newPrimaryKey: "newPrimaryKeyValue" - * , ColumnMapping: { $colName$: { newValue: "newValue", oldValue: "oldValue", destinationColumn: "destinationColumn" } } - * , ModuleMapping: object reference to ModuleMapping object that contains ModuleRowMapping objects - * , ParentModuleMapping: object reference to supervised ModuleMapping object (at this point "null" because there is no supervised object) - * } - * } - * , SubModules: { - * (ModuleMapping) - * $moduleName$: { - * name: "moduleName" - * , destinationModuleName: "destinationModuleName" - * , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } - * , ValueMapping: {$colName$: "value"} - * , dataRows:{ - * (ModuleRowMapping) - * $rowid$: { - * name: "moduleName" - * , oldPrimaryKey: "oldPrimaryKeyValue" - * , newPrimaryKey: "newPrimaryKeyValue" - * , ColumnMapping: { $colName$: { newValue: "newValue", oldValue: "oldValue", destinationColumn: "destinationColumn" } } - * , ModuleMapping: - * , ParentModuleMapping: object reference to supervised ModuleMapping object (at this point "null" because there is no supervised object) - * } - * } - * , SubModules: {...} - * } - * } - * - * } - * - *} - * - * @param {Object} pInputMapping InputMapping - */ - function buildMapping(pInputMapping) - { - //root mapping - var rootModule = Object.keys(pInputMapping)[0]; - var ModuleMapping = _ModuleMapping(rootModule, pInputMapping[rootModule]); - var ModuleData = _getModuleData(rootModule, pInputMapping[rootModule].condition); - - for(var row in ModuleData) - { - var ModuleRowMapping = _ModuleRowMapping(ModuleMapping, null, ModuleData[row]); - - ModuleMapping.DataRows[ModuleRowMapping.oldPrimaryKey] = ModuleRowMapping; - } - - ModulesMapping[rootModule] = ModuleMapping; - - //recursive subordinated modules mapping - _buildSubordinatedMapping(pInputMapping, ModuleMapping); - - - //delivers stored data for module in Database with condition - function _getModuleData(pModule, pCondition) - { - if(pModule == undefined) return {}; - - var ModuleColumnsStructure = AliasDefinitionStructure.tables[pModule].columns; - var cols = Object.keys(ModuleColumnsStructure); - - var condition = "1=1"; - if(pCondition != undefined) - condition = pCondition; - - var dbData = db.table("select " + cols.join(", ") + " from " + pModule + " where " + condition); - - //map 2d-Array to Object { $rowNumber$: { $columnName$: { value: "valueInDB" } } } - var DataObj = {}; - for(var row = 0; row < dbData.length; row++) - { - DataObj[row] = {}; - for(var col = 0; col < dbData[row].length; col++) - { - DataObj[row][cols[col]] = { - value: dbData[row][col] - }; - } - } - - return DataObj; - } - - - //recursive: ModuleMapping and ModuleRowMapping for subordinated modules - function _buildSubordinatedMapping(pInputMapping, pParentModuleMapping) - { - var SubModules = pInputMapping[pParentModuleMapping.name].SubModules; - if(SubModules == undefined) - return; - - for(var subModuleName in SubModules) - { - var ModuleMapping = _ModuleMapping(subModuleName, SubModules[subModuleName]); - ModuleData = _getModuleData(subModuleName, SubModules[subModuleName].condition); - for(var row in ModuleData) - { - ModuleRowMapping = _ModuleRowMapping(ModuleMapping, pParentModuleMapping, ModuleData[row]); - ModuleMapping.DataRows[ModuleRowMapping.oldPrimaryKey] = ModuleRowMapping; - } - - ModulesMapping[pParentModuleMapping.name].SubModules[subModuleName] = ModuleMapping; - - _buildSubordinatedMapping(SubModules, ModuleMapping); - } - } - - function _ModuleMapping( pModuleName, pInputModuleMapping ) - { - return { - name: pModuleName, - destinationModuleName: pInputModuleMapping.destinationModuleName == undefined ? pModuleName : pInputModuleMapping.destinationModuleName, - DestinationColumnMapping: pInputModuleMapping.DestinationColumnMapping == undefined ? {} : pInputModuleMapping.DestinationColumnMapping, - ValueMapping: pInputModuleMapping.ValueMapping == undefined ? {} : pInputModuleMapping.ValueMapping, - DataRows:{}, - SubModules: {} - }; - } - - function _ModuleRowMapping(pModuleMapping, pParentModuleMapping, pDataRow) - { - var ModuleRowMapping = { - name: pModuleMapping.name, - oldPrimaryKey: null, - newPrimaryKey: null, - ColumnMapping: {}, - ModuleMapping: pModuleMapping, - ParentModuleMapping: pParentModuleMapping - }; - - var ModuleColumnsStructure = AliasDefinitionStructure.tables[ModuleRowMapping.name].columns; - - //build ColumnMapping - for(var col in ModuleColumnsStructure) - { - //set defined columns from InputMapping -> if not defined, use the same column - var destinationColumn = ModuleRowMapping.ModuleMapping.DestinationColumnMapping[col]; - if(destinationColumn == undefined) - destinationColumn = col; - - //set defined values from InputMapping -> if not defined, use the value from DB - var oldValue = pDataRow[col].value; - var newValue = newValue = ModuleRowMapping.ModuleMapping.ValueMapping[col]; - if(newValue == undefined) - newValue = oldValue; - - //set new primary key - if(ModuleColumnsStructure[col].primaryKey) - { - ModuleRowMapping.oldPrimaryKey = ModuleRowMapping.ColumnMapping[col] = oldValue; - newValue = util.getNewUUID(); - ModuleRowMapping.newPrimaryKey = newValue; - } - - ModuleRowMapping.ColumnMapping[col] = _columnMapping(newValue, oldValue, destinationColumn); - } - - switch(ModuleRowMapping.name) - { - case "OFFER": - { - //andere Values setzen - var dtUtils = new DateUtils(); - ModuleRowMapping.ColumnMapping["OFFERDATE"].newValue = dtUtils.getTodayUTC(); - } - break; - case "OFFERITEM": - { - //OFFER_ID mappen - if(ModuleRowMapping.ParentModuleMapping.name == "OFFER") - { - ModuleRowMapping.ColumnMapping["OFFER_ID"].newValue = ModulesMapping[ModuleRowMapping.ParentModuleMapping.name].DataRows[ModuleRowMapping.ColumnMapping["OFFER_ID"].oldValue].newPrimaryKey; - } - //ASSIGNEDTO mappen - if(ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue != "") - { - if(ModuleRowMapping.ParentModuleMapping == null) - { - ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].newValue = ModulesMapping["OFFERITEM"].DataRows[ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue].newPrimaryKey; - } - else - { - ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].newValue = ModuleRowMapping.ModuleMapping.DataRows[ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue].newPrimaryKey; - } - } - } - break; - case "OFFERLINK": - { - - } - default: - { - - } - - - } - - //Spezialbehandlung USER_NEW DATENEW.... - if(ModuleRowMapping.ColumnMapping["DATE_NEW"] != undefined) ModuleRowMapping.ColumnMapping["DATE_NEW"].newValue = datetime.date(); - if(ModuleRowMapping.ColumnMapping["USER_NEW"] != undefined) ModuleRowMapping.ColumnMapping["USER_NEW"].newValue = vars.get("$sys.user"); - if(ModuleRowMapping.ColumnMapping["DATE_EDIT"] != undefined) ModuleRowMapping.ColumnMapping["DATE_EDIT"].newValue = ""; - if(ModuleRowMapping.ColumnMapping["USER_EDIT"] != undefined) ModuleRowMapping.ColumnMapping["USER_EDIT"].newValue = ""; - - return ModuleRowMapping; - } - - - - - function _columnMapping(pNewValue, pOldValue, pDestinationColumn) - { - return { - newValue: pNewValue, - oldValue: pOldValue, - destinationColumn: pDestinationColumn - }; - } - - } - - /** - * Builds the insert statements for passed ModulesMapping - * - * @param {Object} pModulesMapping ModulesMapping from buildMapping() - */ - function buildStatements(pModulesMapping) - { - var rootModule = Object.keys(pModulesMapping)[0]; - - for(var row in pModulesMapping[rootModule].DataRows) - { - //buildInsertStatement - statements.push(_statement(pModulesMapping[rootModule].DataRows[row])); - } - - _subordinatedStatements(pModulesMapping[rootModule]); - - function _subordinatedStatements(pMapping) - { - if(pMapping.SubModules == undefined) - return; - - for(var subModule in pMapping.SubModules) - { - - for(var row in pMapping.SubModules[subModule].DataRows) - { - statements.push(_statement(pMapping.SubModules[subModule].DataRows[row])); - } - - _subordinatedStatements(pMapping.SubModules[subModule]); - } - - } - - function _statement(pRowMapping) - { - var cols = []; - var vals = []; - var destTable = pRowMapping.ModuleMapping.destinationModuleName; - var colMapping = pRowMapping.ColumnMapping; - - for(var col in colMapping) - { - cols.push(colMapping[col].destinationColumn); - vals.push(colMapping[col].newValue.toString()); - } - - var colTypes = db.getColumnTypes(destTable, cols) - - return [destTable, cols, colTypes, vals]; - } - } + if ( pLocale == undefined ) + return translate.text(pText); + else + return translate.text(pText, pLocale) } -} - -/** -* provides somehow static methods for special handling in JDito-Processes -* do not create an instance of this -* @class -*/ -ProcessHandlingUtil = { - /** - * In onValidation-Process a local variable called "$local.value" is made available from kernel. - * It contains the entered value - the field contains the value only after successfull validation (vars.get("$field.Fieldname")). - * The onValidation-Process is running again before saving the entity, at this point there's "$local.value" varialbe no longer available, - * but the entered value now is the present one because the field has already been validated before. - * Otherwise a "variable not found" error would occur. - * - * @param {String} pFieldValue req value of the field onValidation-Process is executed ( e.g. vars.get("$field.Fieldname") ) - * @return {String} Field value for onValidation-Process - */ - getOnValidationValue: function(pFieldValue) - { - return vars.exists("$local.value") ? vars.get("$local.value") : pFieldValue; - } + } +