diff --git a/.liquibase/Data_alias/basic/2019.1.4/changelog.xml b/.liquibase/Data_alias/basic/2019.1.4/changelog.xml index 31ef6c3a237ecb7ea047a88abb6719ec480872ca..ae70de38d45ae6ab694d33ed0d57b770c3eb0ebb 100644 --- a/.liquibase/Data_alias/basic/2019.1.4/changelog.xml +++ b/.liquibase/Data_alias/basic/2019.1.4/changelog.xml @@ -197,7 +197,7 @@ <include relativeToChangelogFile="true" file="longerProductName_Code.xml"/> - <include relativeToChangelogFile="true" file="BulkMail/create_bulkMail.xml"/> + <include relativeToChangelogFile="true" file="BulkMail/create_bulkmail.xml"/> <include relativeToChangelogFile="true" file="BulkMail/create_bulkMailRecipient.xml"/> <include relativeToChangelogFile="true" file="DSGVO/create_DSGVO.xml"/> diff --git a/.liquibase/Data_alias/basic/2019.1.4/update_Offer.xml b/.liquibase/Data_alias/basic/2019.1.4/update_Offer.xml index e4ed67cc4df81a0c7d66d903b576980e6e7f846d..63b2d99c5b00b010f89f681854de803358a46759 100644 --- a/.liquibase/Data_alias/basic/2019.1.4/update_Offer.xml +++ b/.liquibase/Data_alias/basic/2019.1.4/update_Offer.xml @@ -6,7 +6,7 @@ <column name="OBJECT_TYPE" type="NVARCHAR(63)"> </column> </addColumn> - <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="OFFER"/> + <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="OFFER" columnDataType="CHAR(36)"/> <update tableName="OFFER"> <column name="OBJECT_TYPE" value="Salesproject" /> <where>OBJECT_ROWID = '0833465c-8851-4fbb-b7e3-8c1d73c903da' </where> @@ -17,7 +17,8 @@ <addNotNullConstraint tableName="OFFER" - columnName="OBJECT_TYPE"/> + columnName="OBJECT_TYPE" + columnDataType="NVARCHAR(63)"/> </changeSet> </databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Competition.xml b/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Competition.xml index d0d00e648e99d7af6befbfae4c7705d3829e1700..274714b871b8f0dedab462dfad94e1403da65ae6 100644 --- a/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Competition.xml +++ b/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Competition.xml @@ -6,8 +6,8 @@ <column name="OBJECT_TYPE" type="NVARCHAR(63)"> </column> </addColumn> - <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="SALESPROJECT_COMPETITION"/> - <renameColumn newColumnName="COMPETITIONID" oldColumnName="SALESPROJECT_COMPETITIONID" tableName="SALESPROJECT_COMPETITION"/> + <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="SALESPROJECT_COMPETITION" columnDataType="CHAR(36)"/> + <renameColumn newColumnName="COMPETITIONID" oldColumnName="SALESPROJECT_COMPETITIONID" tableName="SALESPROJECT_COMPETITION" columnDataType="CHAR(36)"/> <update tableName="SALESPROJECT_COMPETITION"> <column name="OBJECT_TYPE" value="Salesproject" /> <where>OBJECT_ROWID = '0833465c-8851-4fbb-b7e3-8c1d73c903da' </where> @@ -15,7 +15,8 @@ <addNotNullConstraint tableName="SALESPROJECT_COMPETITION" - columnName="OBJECT_TYPE"/> + columnName="OBJECT_TYPE" + columnDataType="NVARCHAR(63)"/> <renameTable oldTableName="SALESPROJECT_COMPETITION" newTableName="COMPETITION"/> diff --git a/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Forecast.xml b/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Forecast.xml index 7e7ee03202f47ad2bbe8949b0e20eb5d1dd12702..bd97822e3351649c3631fe5d095ac608b42833f7 100644 --- a/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Forecast.xml +++ b/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Forecast.xml @@ -6,8 +6,8 @@ <column name="OBJECT_TYPE" type="NVARCHAR(63)"> </column> </addColumn> - <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="SALESPROJECT_FORECAST"/> - <renameColumn newColumnName="FORECASTID" oldColumnName="SALESPROJECT_FORECASTID" tableName="SALESPROJECT_FORECAST"/> + <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="SALESPROJECT_FORECAST" columnDataType="CHAR(36)"/> + <renameColumn newColumnName="FORECASTID" oldColumnName="SALESPROJECT_FORECASTID" tableName="SALESPROJECT_FORECAST" columnDataType="CHAR(36)"/> <update tableName="SALESPROJECT_FORECAST"> <column name="OBJECT_TYPE" value="Salesproject" /> <where>OBJECT_ROWID = '0833465c-8851-4fbb-b7e3-8c1d73c903da' </where> @@ -15,7 +15,8 @@ <addNotNullConstraint tableName="SALESPROJECT_FORECAST" - columnName="OBJECT_TYPE"/> + columnName="OBJECT_TYPE" + columnDataType="NVARCHAR(63)"/> <renameTable oldTableName="SALESPROJECT_FORECAST" newTableName="FORECAST"/> diff --git a/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Member.xml b/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Member.xml index 3e6a9d523a13fd4b1bd6c7d051d855a55afc3af2..f9ca66f2dc76e89f99f8df7faabe2508038da1d5 100644 --- a/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Member.xml +++ b/.liquibase/Data_alias/basic/2019.1.4/update_Salesproject_Member.xml @@ -6,9 +6,9 @@ <column name="OBJECT_TYPE" type="NVARCHAR(63)"/> <column name="DECIDER" type="BOOLEAN"/> </addColumn> - <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="SALESPROJECT_MEMBER"/> - <renameColumn newColumnName="MEMBERID" oldColumnName="SALESPROJECT_MEMBERID" tableName="SALESPROJECT_MEMBER"/> - <renameColumn newColumnName="ROLE" oldColumnName="SALESPROJECT_ROLE" tableName="SALESPROJECT_MEMBER"/> + <renameColumn newColumnName="OBJECT_ROWID" oldColumnName="SALESPROJECT_ID" tableName="SALESPROJECT_MEMBER" columnDataType="CHAR(36)"/> + <renameColumn newColumnName="MEMBERID" oldColumnName="SALESPROJECT_MEMBERID" tableName="SALESPROJECT_MEMBER" columnDataType="CHAR(36)"/> + <renameColumn newColumnName="ROLE" oldColumnName="SALESPROJECT_ROLE" tableName="SALESPROJECT_MEMBER" columnDataType="CHAR(36)"/> <update tableName="SALESPROJECT_MEMBER"> <column name="OBJECT_TYPE" value="Salesproject" /> <where>OBJECT_ROWID = '0833465c-8851-4fbb-b7e3-8c1d73c903da'</where> @@ -16,7 +16,8 @@ <addNotNullConstraint tableName="SALESPROJECT_MEMBER" - columnName="OBJECT_TYPE"/> + columnName="OBJECT_TYPE" + columnDataType="NVARCHAR(63)"/> <renameTable oldTableName="SALESPROJECT_MEMBER" newTableName="MEMBER"/> diff --git a/entity/360Degree_entity/360Degree_entity.aod b/entity/360Degree_entity/360Degree_entity.aod index 0978ad120ab3cf5973995bf388121c14e2c1ff0e..3d70d6ee97d9fad9d50ae254009eee019596c633 100644 --- a/entity/360Degree_entity/360Degree_entity.aod +++ b/entity/360Degree_entity/360Degree_entity.aod @@ -43,6 +43,12 @@ <entityParameter> <name>ObjectType_param</name> <valueProcess>%aditoprj%/entity/360Degree_entity/entityfields/organisationobjects/children/objecttype_param/valueProcess.js</valueProcess> + <expose v="false" /> + </entityParameter> + <entityParameter> + <name>BaseContextId_param</name> + <valueProcess>%aditoprj%/entity/360Degree_entity/entityfields/organisationobjects/children/basecontextid_param/valueProcess.js</valueProcess> + <expose v="false" /> </entityParameter> </children> </entityProvider> @@ -82,6 +88,12 @@ <entityParameter> <name>ObjectType_param</name> <valueProcess>%aditoprj%/entity/360Degree_entity/entityfields/personobjects/children/objecttype_param/valueProcess.js</valueProcess> + <expose v="false" /> + </entityParameter> + <entityParameter> + <name>BaseContextId_param</name> + <valueProcess>%aditoprj%/entity/360Degree_entity/entityfields/personobjects/children/basecontextid_param/valueProcess.js</valueProcess> + <expose v="false" /> </entityParameter> </children> </entityProvider> @@ -142,6 +154,10 @@ <dropDownProcess>%aditoprj%/entity/360Degree_entity/entityfields/active/dropDownProcess.js</dropDownProcess> <searchable v="true" /> </entityField> + <entityParameter> + <name>BaseContextId_param</name> + <expose v="true" /> + </entityParameter> </entityFields> <recordContainers> <jDitoRecordContainer> diff --git a/entity/360Degree_entity/entityfields/organisationobjects/children/basecontextid_param/valueProcess.js b/entity/360Degree_entity/entityfields/organisationobjects/children/basecontextid_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..9e359dfbd05a73d0010c8e90a0a645160e0087e4 --- /dev/null +++ b/entity/360Degree_entity/entityfields/organisationobjects/children/basecontextid_param/valueProcess.js @@ -0,0 +1,3 @@ +import("system.result"); + +result.string("Organisation"); \ No newline at end of file diff --git a/entity/360Degree_entity/entityfields/personobjects/children/basecontextid_param/valueProcess.js b/entity/360Degree_entity/entityfields/personobjects/children/basecontextid_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..8ba819b0b1d3a291ac31d270ec48c009ccf047e0 --- /dev/null +++ b/entity/360Degree_entity/entityfields/personobjects/children/basecontextid_param/valueProcess.js @@ -0,0 +1,3 @@ +import("system.result"); + +result.string("Person"); \ No newline at end of file diff --git a/entity/360Degree_entity/recordcontainers/jdito/contentProcess.js b/entity/360Degree_entity/recordcontainers/jdito/contentProcess.js index 5a8d3a99c5d3d966a76d456b7e18678da8c0ce31..953a229acb9dc610df8b3fb5cfec1bda71aa0b20 100644 --- a/entity/360Degree_entity/recordcontainers/jdito/contentProcess.js +++ b/entity/360Degree_entity/recordcontainers/jdito/contentProcess.js @@ -6,12 +6,11 @@ import("system.result"); import("Context_lib"); import("system.translate"); -var resultList = []; if (vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param") && vars.exists("$param.ObjectRowId_param") && vars.get("$param.ObjectRowId_param")) { var active; - var selectMap = ContextUtils.getSelectMap () + var selectMap = ContextUtils.getSelectMap(); if(vars.exists("$local.filter") && vars.get("$local.filter") ) { var filter = vars.get("$local.filter"); @@ -27,9 +26,37 @@ if (vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param" } var contextList = JSON.parse(vars.getString("$param.ObjectType_param")); - contextList.forEach(function (context) + var contactId = vars.get("$param.ObjectRowId_param"); + var baseContext = vars.exists("$param.BaseContextId_param") ? vars.get("$param.BaseContextId_param") : undefined; + result.object(_get360Data(selectMap, baseContext, contactId, contextList, active, undefined, baseContext != undefined)); + + +} +else +{ + result.object([]); +} + +/** + * load all data for the 360Degree tree. + * @param {Object} pSelectMap the select map (result of ContextUtils.getSelectMap()) + * @param {String} pBaseContextId the ContextId in which this 360 Degree tree is located / for which the data should be loaded + * @param {String[]} pContactId the Contactid the 360 Degree tree should be loaded for + * @param {String[]} pContextList list of contexts to load. Note that subcontexts use their own list, defined in ContextUtils + * @param {Boolean} [pActive=undefined] if not undefined: it select only for active / inactive state + * @param {String[]} [pExcludedObjectIds=undefined] if set: this contextIds are filtered out. + * @param {Boolean} [pAlsoAddFromSubcontexts=false] if true also load data from contexts which are configured as subcontexts. + * A subcontext is for example used by Organisations, which also load the data from all Persons in this organisation into the 360°-view. + * Does not include subcontexts of subcontexts + * + * @return {String[][]} the resulting data + */ +function _get360Data(pSelectMap, pBaseContextId, pContactId, pContextList, pActive, pExcludedObjectIds, pAlsoAddFromSubcontexts) +{ + var resultList = []; + pContextList.forEach(function (context) { - var data = db.table(ContextUtils.getContextDataSql(context, vars.get("$param.ObjectRowId_param"), true, active, true)); + var data = db.table(ContextUtils.getContextDataSql(context, pContactId, true, pActive, true, pExcludedObjectIds)); data.forEach(function (row) { var record = []; @@ -38,22 +65,43 @@ if (vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param" record[2] = context; // TARGET_CONTEXT record[3] = row[1]; // TITLE record[4] = row[2]; //DATE - if(active != undefined) //ACTIVE - record[5] = translate.text(active); + if(pActive != undefined) //ACTIVE + record[5] = translate.text(pActive); else { - if(selectMap[context].activeStates.indexOf(row[3]) > -1) + if(pSelectMap[context].activeStates.indexOf(row[3]) > -1) record[5] = translate.text("true"); else record[5] = translate.text("false"); } resultList.push(record); - }); - - }); - result.object(resultList) -} -else -{ - result.object(resultList); + }); + }); + + if (pAlsoAddFromSubcontexts) + { + var baseContextData = pSelectMap[pBaseContextId]; + var subContexts = baseContextData.getSubContexts(pContactId); + + if (!pExcludedObjectIds) + pExcludedObjectIds = []; + + pExcludedObjectIds.push(pBaseContextId); + + for (subContextId in subContexts) + { + var subContextSql = subContexts[subContextId][0]; + // select Ids from subcontexts + var subContextContactIds = db.array(db.COLUMN, subContextSql); + + subContextContactIds.forEach(function(pId) { + // V-- do not add subcontexts deeper than one layer + var subcontextRes = _get360Data(pSelectMap, pBaseContextId, pId, subContexts[subContextId][1], pActive, pExcludedObjectIds, false); + logging.log(subcontextRes.toSource()) + resultList = resultList.concat(subcontextRes); + }); + } + } + + return resultList; } \ No newline at end of file diff --git a/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js b/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js index 4aa2fc2aac11e228e042b757019152b6ee802d5f..b71a637ad5f3e5f63751e823d8acaa560e4a7a87 100644 --- a/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js +++ b/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js @@ -1,4 +1,5 @@ import("system.logging"); +import("system.datetime"); import("system.translate"); import("Util_lib"); import("JditoFilter_lib"); @@ -10,20 +11,24 @@ import("system.result"); import("Sql_lib"); import("Attribute_lib"); +var t = datetime.date() + var getGroups = vars.exists("$param.GetGroups_param") && vars.get("$param.GetGroups_param"); var objectType = vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param"); var parentType = vars.exists("$param.AttrParentType_param") && vars.get("$param.AttrParentType_param"); - -logging.log([getGroups, objectType, parentType]) +var fetchUsages = true; var uidTableAlias = "UIDROW"; var sqlSelect = "select UIDROW.AB_ATTRIBUTEID, UIDROW.ATTRIBUTE_PARENT_ID, UIDROW.ATTRIBUTE_ACTIVE, UIDROW.DROPDOWNDEFINITION, UIDROW.SORTING, UIDROW.ATTRIBUTE_TYPE, " + KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.attributeType(), "UIDROW.ATTRIBUTE_TYPE") //3 - + ", '', UIDROW.ATTRIBUTE_NAME, PARENT1.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_PARENT_ID " - + "from AB_ATTRIBUTE UIDROW " - + "left join AB_ATTRIBUTE PARENT1 on UIDROW.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID " //always select the names of the next 3 parents so that less queries - + "left join AB_ATTRIBUTE PARENT2 ON PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID " //are required later when buildung the full name - + "left join AB_ATTRIBUTE PARENT3 ON PARENT2.ATTRIBUTE_PARENT_ID = PARENT3.AB_ATTRIBUTEID"; + + ", '', UIDROW.ATTRIBUTE_NAME, PARENT1.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_PARENT_ID \n\ + from AB_ATTRIBUTE UIDROW \n\ + left join AB_ATTRIBUTE PARENT1 on UIDROW.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID \n\ + left join AB_ATTRIBUTE PARENT2 ON PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID \n\ + left join AB_ATTRIBUTE PARENT3 ON PARENT2.ATTRIBUTE_PARENT_ID = PARENT3.AB_ATTRIBUTEID"; + +/* always select the names of the next 3 parents so that less queries + are required later when buildung the full name */ var sqlOrder = " order by UIDROW.ATTRIBUTE_PARENT_ID asc, UIDROW.SORTING asc"; @@ -69,18 +74,7 @@ else if (objectType) //if there's an objectType, it comes from the AttributeRel else // do not return anything, if parameter is there but an empty array condition.and("1=2"); - -} -else if (parentType) //condition for all subordinate attributes of an attribute (for the tree of subordinate attributes in an attribute) -{ - if (AttributeTypeUtil.isGroupType(parentType)) - { - var parentId = vars.exists("$param.AttrParentId_param") && vars.get("$param.AttrParentId_param"); - if (parentId) - condition.and("UIDROW.AB_ATTRIBUTEID in ('" + AttributeUtil.getAllChildren(vars.getString("$param.AttrParentId_param")).join("','") + "')"); - } - else - condition.and("1=2"); + fetchUsages = false; } //when there are filters selected, add them to the conditon @@ -91,21 +85,26 @@ if (vars.exists("$local.filter") && vars.get("$local.filter")) condition.andSqlCondition(JditoFilterUtils.getSqlCondition(filter.filter, "AB_ATTRIBUTE", uidTableAlias)); } -var usagesSelect = "select AB_ATTRIBUTE_ID, OBJECT_TYPE from AB_ATTRIBUTEUSAGE \n\ - join AB_ATTRIBUTE UIDROW on AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = UIDROW.AB_ATTRIBUTEID"; -var usageTbl = db.table(condition.buildSql(usagesSelect, "1=1")); -var usages = {}; -for (let i = 0, l = usageTbl.length; i < l; i++) +var usages; +if (fetchUsages) //this query is only necessary in Attribute, not in AttributeRelation { - let attrId = usageTbl[i][0]; - if (attrId in usages) - usages[attrId].push(usageTbl[i][1]); - else - usages[attrId] = [usageTbl[i][1]]; + var usagesSelect = "select AB_ATTRIBUTE_ID, OBJECT_TYPE from AB_ATTRIBUTEUSAGE \n\ + join AB_ATTRIBUTE UIDROW on AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = UIDROW.AB_ATTRIBUTEID"; + var usageTbl = db.table(condition.buildSql(usagesSelect, "1=1")); + usages = {}; + for (let i = 0, l = usageTbl.length; i < l; i++) + { + let attrId = usageTbl[i][0]; + if (attrId in usages) + usages[attrId].push(usageTbl[i][1]); + else + usages[attrId] = [usageTbl[i][1]]; + } } var attributes = db.table(condition.buildSql(sqlSelect, "1=1", sqlOrder)); +var nameCache = {}; result.object(_buildAttributeTable(attributes, usages)); @@ -140,7 +139,7 @@ function _buildAttributeTable (pAttributes, pUsages) for (let i in rows) { let rowData = rows[i].data; - if (rowData[5].trim() != $AttributeTypes.COMBOVALUE && i in pUsages) + if (pUsages && rowData[5].trim() != $AttributeTypes.COMBOVALUE && i in pUsages) { rowData[7] = pUsages[i].map(function (usage) { @@ -163,8 +162,17 @@ function _buildAttributeTable (pAttributes, pUsages) */ function _getFullName (pAttributeName, pParent1Name, pParent2Name, pParent3Name, pParent4Id) { - var parent4FullName = pParent4Id ? AttributeUtil.getFullAttributeName(pParent4Id) : null; + var parent4FullName; + if (pParent4Id && pParent4Id in nameCache) + parent4FullName = nameCache[pParent4Id]; + else + { + parent4FullName = pParent4Id ? AttributeUtil.getFullAttributeName(pParent4Id) : null; + nameCache[pParent4Id] = parent4FullName; + } pAttributeName = ArrayUtils.joinNonEmptyFields([parent4FullName, pParent3Name, pParent2Name, pParent1Name, pAttributeName], " / "); return pAttributeName; -} \ No newline at end of file +} + +logging.log(datetime.date() - t) \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js b/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js index 4fcb6ca5a0ce8770d33d6201b8822ebca6553037..0dd257f7f36ea76b1dea44e0405d5105a408a75d 100644 --- a/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js +++ b/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js @@ -2,7 +2,7 @@ import("system.logging"); import("system.result"); import("system.neon"); import("system.vars"); -import("PostalAddress_lib"); +import("Address_lib"); if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW && !vars.get("$this.value")) { diff --git a/process/Bulkmail_lib/Bulkmail_lib.aod b/process/Bulkmail_lib/Bulkmail_lib.aod index e02358029db1060e0396ba5d77f7b402cf4d7d27..3181ac4567c83ec13a2f9438ce34c5c9957bc113 100644 --- a/process/Bulkmail_lib/Bulkmail_lib.aod +++ b/process/Bulkmail_lib/Bulkmail_lib.aod @@ -1,6 +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.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> - <name>Bulkmail_lib</name> + <name>BulkMail_lib</name> <majorModelMode>DISTRIBUTED</majorModelMode> <process>%aditoprj%/process/Bulkmail_lib/process.js</process> <variants> diff --git a/process/Bulkmail_lib/process.js b/process/Bulkmail_lib/process.js index 27a4b8362f5d4c49ca9a2094eb1fab7919eddaa8..49290ffaef9ab4092b94476882052e1aaf7b0865 100644 --- a/process/Bulkmail_lib/process.js +++ b/process/Bulkmail_lib/process.js @@ -1,6 +1,52 @@ +import("KeywordRegistry_basic"); +import("Sql_lib"); +import("system.db"); import("DocumentTemplate_lib"); +import("Email_lib"); -function Bulkmail () + +function BulkMailUtils () {} + +BulkMailUtils.sendBulkMail = function (pBulkMailId) { + var templateId = db.cell(SqlCondition.begin() + .andPrepare("BULKMAIL.BULKMAILID", pBulkMailId) + .buildSql("select DOCUMENTTEMPLATE_ID from BULKMAIL", "1=2") + ); + var template = DocumentTemplate.loadTemplate(templateId); + var emailSender; + + var recipientData = db.table(SqlCondition.begin() + .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) + //TODO: more condition + .buildSql("select BULKMAILRECIPIENTID, CONTACT_ID, '' from BULKMAILRECIPIENT", "1=2") + ); + + var successIds = []; + var failedIds = []; + var mails = template.getReplacedEmailsByContactIds(recipientData.map(function (e) {return e[0];})); + for (let i = 0, l = recipientData.length; i < l; i++) + { + let contactId = recipientData[i][1]; + let email = mails[contactId]; + email.toRecipients = [recipientData[i][1]]; + email.sender = emailSender; + + let isSuccess = email.send(); + if (isSuccess) + successIds.push(recipientData[i][0]); //set the recipient status to 'sent' + else + failedIds.push(recipientData[i][0]); //set the recipient status to 'failed' + } + db.updateData("BULKMAILRECIPIENT", ["STATUS"], null, [$KeywordRegistry.bulkMailSentStatus$sent()], + SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds) + .build("1=2") + ); + db.updateData("BULKMAILRECIPIENT", ["STATUS"], null, [$KeywordRegistry.bulkMailSentStatus$failed()], + SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds) + .build("1=2") + ); } \ No newline at end of file diff --git a/process/Context_lib/process.js b/process/Context_lib/process.js index 0acbe8b80929ad5a9198fce2f706d7d5737c7ede..f07f6a3eee6cb52af5e36d193f7bce48bc32d646 100644 --- a/process/Context_lib/process.js +++ b/process/Context_lib/process.js @@ -237,6 +237,19 @@ function ContextSelector(pTableName, pIdField, pTitleExpression) */ this.activeStates = null; ProtoPropertyUtils.makeSemiReadOnly(this, "activeStates"); this.condition = null; ProtoPropertyUtils.makeSemiReadOnly(this, "condition"); + + /** + * an object which contains the subcontexts and the prepared select to get the contactIds of them. + * V--With Tablename! + * { V-- before id-condition | add "and", if needed --V V-IdCollumn name V-- optional after-condition SQL (e.g. group by etc.) + * "Person": ["select PERSON_ID from CONTACT where PERSON_ID is not null and", "CONTACT.ORGANISATION_ID", '', ["Offer", "Contract"]], + * // the contexts to show for this subcontext + * "Offer" ... // you can add as many subcontexts as you wish + * } + * read-only property; set it with a matching setter + * @property + */ + this.subContexts = null; ProtoPropertyUtils.makeSemiReadOnly(this, "subContexts"); } /** * creates a new instance of a ContextSelector and returns it @@ -275,6 +288,28 @@ ContextSelector.prototype.getFullFromClause = function() else return this.tableName; +}; +/** + * @return {String} full from-expression with tablename and join-part + */ +ContextSelector.prototype.getSubContexts = function(pParentRowId) +{ + if (this.subContexts) + { + var sqls = {}; + + for (contextId in this.subContexts) + { + sqls[contextId] = [SqlCondition.begin() + .andPrepare(this.subContexts[contextId][1], pParentRowId) + .buildSql(this.subContexts[contextId][0], "1=2", this.subContexts[contextId][2], false), this.subContexts[contextId][3]]; + } + + return sqls; + } + else + return {}; + }; //setters which to nothing special; no need to document them ContextSelector.prototype.setTitleExpression = function(pValue) @@ -326,6 +361,11 @@ ContextSelector.prototype.setCondition = function(pSqlCondition) this._condition = pSqlCondition; return this; }; +ContextSelector.prototype.setSubContexts = function(pContexts) +{ + this._subContexts = pContexts; + return this; +}; /** * TODO: !!!temporary function until you can get fields from another Entity!!! @@ -337,6 +377,9 @@ ContextUtils.getSelectMap = function() "Organisation": ContextSelector.create("ORGANISATION", "CONTACT.CONTACTID", "ORGANISATION.NAME") .setJoinExpression("join CONTACT on ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID and CONTACT.PERSON_ID is null") .setCondition(SqlCondition.begin().and("ORGANISATION.ORGANISATIONID != '0'")) + .setSubContexts({ + "Person": ["select CONTACTID from CONTACT where PERSON_ID is not null and", "CONTACT.ORGANISATION_ID", '', ["Offer", "Contract"]] + }) ,"Person": ContextSelector.create("CONTACT", "CONTACTID") .setTitleExpression(maskingUtils.concat([ new ContactTitleRenderer(Contact.createWithColumnPreset()).asSql() diff --git a/process/DocumentTemplate_lib/process.js b/process/DocumentTemplate_lib/process.js index 2cbeffcce9962843b8873fbbab9a2db3a4efd75d..d4c01e318b614e7fedbb43822aac0bdf8c1db096 100644 --- a/process/DocumentTemplate_lib/process.js +++ b/process/DocumentTemplate_lib/process.js @@ -1,4 +1,3 @@ -import("system.logging"); import("Communication_lib"); import("system.neon"); import("Employee_lib"); @@ -147,67 +146,103 @@ DocumentTemplate.prototype.getReplacedContent = function (pReplacements) /** * replaces the placeholders with data from the contact and returns the result */ -DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId) //TODO: function required for mass replacing (for bulkmails) +DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId) { - var replacements = this._getReplacementsByContactId(pContactId); - return this.getReplacedContent(replacements); + var replacements = this._getReplacementsByContactIds([pContactId]); + return this.getReplacedContent(replacements[pContactId]); } /** - * @private + * replaces the placeholders with data from the contacts and returns the result + * + * @param {Array} pContactIds contact ids + * + * @return {Object} replaced content for every contactId */ -DocumentTemplate.prototype._getReplacementsByContactId = function (pContactId) +DocumentTemplate.prototype.getReplacedContentByContactIds = function (pContactIds) { - var config = this._getRequiredPlaceholders(); - var addressData = getAddressesData([pContactId], config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection - var replacements = {}; - for (let i = 0, l = addressData[0].length; i < l; i++) + var replacements = this._getReplacementsByContactIds(pContactIds); + var contents = {}; + for (let contactId in replacements) { - replacements[addressData[0][i]] = addressData[1][i]; + contents[contactId] = this.getReplacedContent(replacements[contactId]); } - return replacements; + return contents; } /** - * loads data into an Email object from a template + * replaces the placeholders with data from the contacts and returns the result * - * @param {Email} pEmail email object to change - * @param {String} pContactId contactId of the recipient + * @param {Array} pContactIds contact ids + * + * @return {Object} emails for every contactId */ -DocumentTemplate.prototype.setEmailTemplateByContactId = function (pEmail, pContactId) +DocumentTemplate.prototype.getReplacedEmailsByContactIds = function (pContactIds) { - if (this.type != DocumentTemplate.types.EML && this.type != DocumentTemplate.types.HTML && this.type != DocumentTemplate.types.TXT) - throw new Error("Invalid document type for an email template"); - - var replacements = DocumentTemplate._getReplacementsByContactId(pContactId); - - if (this.type == DocumentTemplate.types.EML) //special treatment because eml contains more information than just the body + var replacements = this._getReplacementsByContactIds(pContactIds); + var emailObj = {}; + for (let contactId in replacements) { - let email = mail.parseRFC(util.decodeBase64String(this.content)); - pEmail.sender = DocumentTemplate._replaceText(email[mail.MAIL_SENDER], replacements); - pEmail.subject = DocumentTemplate._replaceText(email[mail.MAIL_SUBJECT], replacements); - pEmail.body = DocumentTemplate._replaceText(email[mail.MAIL_HTMLTEXT], replacements); + if (this.type == DocumentTemplate.types.EML) + emailObj[contactId] = this._getReplacedEML(replacements[contactId], true); + else + { + let body = this.getReplacedContent(replacements[contactId]); + emailObj[contactId] = new Email(null, null, null, body); + } } - else - pEmail.body = this.getReplacedContent(replacements); - + return emailObj; } /** - * Replaces placeholders for EML. This function only works for the email body, - * so if you also need the subject, sender, etc use DocumentTemplate.prototype.setEmailTemplateByContactId. + * @private + */ +DocumentTemplate.prototype._getReplacementsByContactIds = function (pContactIds) +{ + var config = this._getRequiredPlaceholders(); + var contactIdPlaceholder = new Placeholder("contactId", Placeholder.types.SQLPART, "CONTACT.CONTACTID"); + config = [contactIdPlaceholder].concat(config); + var addressData = getAddressesData(pContactIds, config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection + var replacements = {}; + var placeholderNames = addressData[0]; + var contactIdIndex = placeholderNames.indexOf(contactIdPlaceholder.toString()); + for (let i = 1; i < addressData.length; i++) + { + let contactId = addressData[i][contactIdIndex]; + for (let ii = 0, ll = placeholderNames.length; ii < ll; ii++) + { + if (!(contactId in replacements)) + replacements[contactId] = {}; + replacements[contactId][placeholderNames[ii]] = addressData[i][ii]; + } + } + return replacements; +} + +/** + * Replaces placeholders for EML * * @param {Object} pReplacements mapping with replacements for every placeholder + * @param {boolean} [pGetEmail] if true, return Email object * - * @return {String} the replaced content + * @return {String|Email} the replaced content * * @private */ -DocumentTemplate.prototype._getReplacedEML = function (pReplacements) +DocumentTemplate.prototype._getReplacedEML = function (pReplacements, pGetEmail) { - var email = mail.parseRFC(util.decodeBase64String(this.content)); - var htmlText = email[mail.MAIL_HTMLTEXT]; - return DocumentTemplate._replaceText(htmlText, pReplacements); + var mailData = mail.parseRFC(util.decodeBase64String(this.content)); + var email; + var body = DocumentTemplate._replaceText(mailData[mail.MAIL_HTMLTEXT], pReplacements); + if (pGetEmail) + { + var sender = DocumentTemplate._replaceText(mailData[mail.MAIL_SENDER], pReplacements); + var subject = DocumentTemplate._replaceText(mailData[mail.MAIL_SUBJECT], pReplacements); + email = new Email(null, sender, subject, body); + } + else + email = body; + return email; } /* @@ -297,7 +332,7 @@ DocumentTemplate.prototype._getReplacedODT = function (pReplacements) DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements) { var replacements = {}; - for (let placeholder in pReplacements) + for (let placeholder in pReplacements) //removes the prefix and postfix, the process needs it like this replacements[placeholder.slice(3, -3)] = pReplacements[placeholder]; //this is executed as a process because of better performance diff --git a/process/Email_lib/process.js b/process/Email_lib/process.js index 5047809e526692897da79d968059aad4a0325d89..bf786a48d2025baa41d5c9ba7045d26a5cc27c6e 100644 --- a/process/Email_lib/process.js +++ b/process/Email_lib/process.js @@ -24,9 +24,9 @@ function EmailUtils () {} EmailUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId) { var email = new Email(pToRecipients); - email.setSender(pSenderContactId); if (pTemplateId) email.setTemplate(pTemplateId, pRecipientContactId); + email.setSender(pSenderContactId); email.downloadEML(); } @@ -74,13 +74,14 @@ function Email (pToRecipients, pSender, pSubject, pBody, pCcRecipients, pBccReci * * @param {String} pTemplateId the id of the template * @param {String} pContactId the id of the template - * - * @throws {Error} if the type of the template is invalid */ Email.prototype.setTemplate = function (pTemplateId, pContactId) { var template = DocumentTemplate.loadTemplate(pTemplateId); - template.setEmailTemplateByContactId(this, pContactId); + var email = template.getReplacedEmailsByContactIds([pContactId])[pContactId]; + this.sender = email.sender; + this.body = email.body; + this.subject = email.subject; } /** @@ -186,4 +187,52 @@ Email.prototype.downloadEML = function() { neon.download(util.encodeBase64String(this.getRFCmail(), null), (this.subject || translate.text("Email Template")) + ".eml"); } - \ No newline at end of file + +/** + * sends the email object + * + * @return {boolean} true, if the mail was sent sucessfully + */ +Email.prototype.send = function () +{ + var ENCODING = "UTF-8"; + var mailId; + try + { + mailId = mail.newMail(); + } + catch(ex) + { + //TODO: fix this dirty workaround [waiting for #1038963], since newMail causes an error on the first call after a user logged in + logging.log(ex); + util.sleep(1500); + mailId = mail.newMail(); + } + + if (this.toRecipients.length) + mail.addRecipients(mailId, mail.RECIPIENT_TO, this.toRecipients); + + if (this.ccRecipients.length) + mail.addRecipients(mailId, mail.RECIPIENT_CC, this.ccRecipients); + + if (this.bccRecipients.length) + mail.addRecipients(mailId, mail.RECIPIENT_BCC, this.bccRecipients); + + if (this.subject) + mail.setSubject(mailId, this.subject, ENCODING); + + if (this.body) + mail.addText(mailId, this.body, "text/html", ENCODING, null); + else + mail.addText(mailId, "", "text/html", ENCODING, null); + + try + { + mail.sendMail(mailId); + return true; + } + catch (ex) + { + return false; + } +} \ No newline at end of file diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js index 7aacc071aeede38bc1207baf2cacad71d117a405..2030205a1cc16809707bf0aa91e4dba787a42c29 100644 --- a/process/KeywordRegistry_basic/process.js +++ b/process/KeywordRegistry_basic/process.js @@ -107,6 +107,11 @@ $KeywordRegistry.permissionCondType = function(){return "PermissionCondType";}; $KeywordRegistry.permissionAccessType = function(){return "PermissionAccessType";}; $KeywordRegistry.purpose = function(){return "Purpose";}; $KeywordRegistry.statuoritysource = function(){return "Statuoritysource";}; +$KeywordRegistry.DSGVOTablename = function() {return "DSGVOTablename";}; $KeywordRegistry.textPlaceholder = function(){return "textPlaceholder";}; $KeywordRegistry.communicationMediumCampaign = function(){return "CommunicationMediumCampaign";}; -$KeywordRegistry.DSGVOTablename = function() {return "DSGVOTablename";}; + +$KeywordRegistry.bulkMailSentStatus = function(){return "BulkMailSentStatus";}; +$KeywordRegistry.bulkMailSentStatus$pending = function(){return "9a0c5608-070e-49fb-92cd-f6abece9242d";}; +$KeywordRegistry.bulkMailSentStatus$sent = function(){return "147211fb-a1cf-49c8-8e08-c3cfe0404f9b";}; +$KeywordRegistry.bulkMailSentStatus$failed = function(){return "353e27e9-7491-4bfd-b9f9-f18f2cb2a36c";}; \ No newline at end of file