From 3dcfa6506348e9612055c2f487f5a29eeab0a376 Mon Sep 17 00:00:00 2001 From: "S.Listl" <S.Listl@SLISTL.aditosoftware.local> Date: Thu, 25 Jun 2020 10:17:35 +0200 Subject: [PATCH] 1058079 Show duplicates in QuickEntry --- .../AnyContact_entity/AnyContact_entity.aod | 22 +++++ .../contactsbyids/documentation.adoc | 3 + .../recordcontainers/db/conditionProcess.js | 5 +- .../onlyshowcontactids_param/valueProcess.js | 27 +++--- .../onlyshowcontactids_param/valueProcess.js | 24 +++-- .../QuickEntry_entity/QuickEntry_entity.aod | 18 ++++ .../children/contactids_param/valueProcess.js | 87 +++++++++++++++++++ .../withprivatepersons_param/valueProcess.js | 3 + neonContext/AnyContact/AnyContact.aod | 4 + .../AnyContactDuplicates_view.aod | 45 ++++++++++ .../DuplicateScannerFilter_view.aod | 2 +- .../QuickEntryEdit_view.aod | 5 ++ process/DuplicateScanner_lib/process.js | 26 ++++++ 13 files changed, 249 insertions(+), 22 deletions(-) create mode 100644 entity/AnyContact_entity/entityfields/contactsbyids/documentation.adoc create mode 100644 entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js create mode 100644 entity/QuickEntry_entity/entityfields/organdpersduplicates/children/withprivatepersons_param/valueProcess.js create mode 100644 neonView/AnyContactDuplicates_view/AnyContactDuplicates_view.aod diff --git a/entity/AnyContact_entity/AnyContact_entity.aod b/entity/AnyContact_entity/AnyContact_entity.aod index 4b02be4aeaa..174d18bdf2e 100644 --- a/entity/AnyContact_entity/AnyContact_entity.aod +++ b/entity/AnyContact_entity/AnyContact_entity.aod @@ -180,6 +180,28 @@ See ContactUtils.getRelationTypeByPersOrg for possible values</description> <name>AvatarText_param</name> <valueProcess>%aditoprj%/entity/AnyContact_entity/entityfields/avatartext_param/valueProcess.js</valueProcess> </entityParameter> + <entityProvider> + <name>ContactsByIds</name> + <documentation>%aditoprj%/entity/AnyContact_entity/entityfields/contactsbyids/documentation.adoc</documentation> + <dependencies> + <entityDependency> + <name>0206f7a8-fd58-47e8-8b7a-5ff4531e56fb</name> + <entityName>QuickEntry_entity</entityName> + <fieldName>OrgAndPersDuplicates</fieldName> + <isConsumer v="false" /> + </entityDependency> + </dependencies> + <children> + <entityParameter> + <name>ContactId_param</name> + <expose v="false" /> + </entityParameter> + </children> + </entityProvider> + <entityParameter> + <name>ContactIds_param</name> + <expose v="true" /> + </entityParameter> </entityFields> <recordContainers> <dbRecordContainer> diff --git a/entity/AnyContact_entity/entityfields/contactsbyids/documentation.adoc b/entity/AnyContact_entity/entityfields/contactsbyids/documentation.adoc new file mode 100644 index 00000000000..bcf8477de13 --- /dev/null +++ b/entity/AnyContact_entity/entityfields/contactsbyids/documentation.adoc @@ -0,0 +1,3 @@ += AnyContact_entity - ContactsByIds + +The list of contacts can be filtered by providing the contact ids in the parameter "ContactIds_param" \ No newline at end of file diff --git a/entity/AnyContact_entity/recordcontainers/db/conditionProcess.js b/entity/AnyContact_entity/recordcontainers/db/conditionProcess.js index b6d99a470cf..a793d04a1b1 100644 --- a/entity/AnyContact_entity/recordcontainers/db/conditionProcess.js +++ b/entity/AnyContact_entity/recordcontainers/db/conditionProcess.js @@ -24,10 +24,13 @@ if (vars.getString("$param.WithPrivatePersons_param") == "true") .and("CONTACT.PERSON_ID is not null")); } - //exclude private organisation var cond = newWhereIfSet(conditionPrivateOrganisation) .andIfSet("CONTACT.ORGANISATION_ID", orgContactId); +var contactIds = vars.exists("$param.ContactIds_param") && vars.get("$param.ContactIds_param"); +if (contactIds) + cond.andIfSet("CONTACT.CONTACTID", JSON.parse(contactIds), SqlBuilder.IN()); + //TODO: use a preparedCondition (.build instead of .toString) when available #1030812 #1034026 result.string(cond.toString()); \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js b/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js index dc76aa3c55e..27d043515ab 100644 --- a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js +++ b/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js @@ -7,11 +7,11 @@ import("system.result"); var scannerName = "OrganisationDuplicates"; var targetEntity = "Organisation_entity"; var valuesToCheck = {}; -var entityFieldsToLoad = DuplicateScannerUtils.GetEntityFieldsFromConfig(scannerName, targetEntity); +var entityFieldsToLoad = DuplicateScannerUtils.getEntityFieldObjectFromConfig(scannerName, targetEntity); var idsForEmptyResult = JSON.stringify(["nodata"]); -if (entityFieldsToLoad == null || entityFieldsToLoad.length == 0) +if (entityFieldsToLoad == null) result.string(idsForEmptyResult); else { @@ -22,17 +22,22 @@ else vars.get("$field.STANDARD_CITY"); vars.get("$field.STANDARD_ZIP"); vars.get("$field.STANDARD_ADDRESS"); - - for (let fieldname in entityFieldsToLoad) - { - var field = entityFieldsToLoad[fieldname]; + + var allFieldsToLoad = entityFieldsToLoad.entityFields.concat(entityFieldsToLoad.entityIdField); + allFieldsToLoad.forEach(function (field) + { var fieldValue = vars.get("$field." + field); - if (fieldValue) - valuesToCheck[field] = fieldValue; - } - - var scanResults = DuplicateScannerUtils.ScanForDuplicates(scannerName, targetEntity, valuesToCheck, null) || []; + valuesToCheck[field] = fieldValue; + }); + + var scanResults = []; + + //don't search if only the id field has a value + var fieldsToCheck = Object.keys(valuesToCheck); + if (!(fieldsToCheck.length === 0 || (fieldsToCheck.length === 1 && entityFieldsToLoad.entityIdField in valuesToCheck))) + scanResults = DuplicateScannerUtils.ScanForDuplicates(scannerName, targetEntity, valuesToCheck, null) || []; + var duplicateIds = scanResults.map(function (scanResult) { return scanResult[indexsearch.FIELD_ID]; diff --git a/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js b/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js index 298a3115245..ce8253a306e 100644 --- a/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js +++ b/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js @@ -8,11 +8,11 @@ import("system.result"); var scannerName = "PersonDuplicates"; var targetEntity = "Person_entity"; var valuesToCheck = {}; -var entityFieldsToLoad = DuplicateScannerUtils.GetEntityFieldsFromConfig(scannerName, targetEntity); +var entityFieldsToLoad = DuplicateScannerUtils.getEntityFieldObjectFromConfig(scannerName, targetEntity); var idsForEmptyResult = JSON.stringify(["nodata"]); -if (entityFieldsToLoad == null || entityFieldsToLoad.length == 0) +if (entityFieldsToLoad == null) result.string(idsForEmptyResult); else { @@ -27,16 +27,22 @@ else vars.get("$field.PersAddresses.insertedRows") vars.get("$field.PersAddresses.changedRows") vars.get("$field.PersAddresses.deletedRows") - - for (let fieldname in entityFieldsToLoad) - { - var field = entityFieldsToLoad[fieldname]; + + var allFieldsToLoad = entityFieldsToLoad.entityFields.concat(entityFieldsToLoad.entityIdField); + allFieldsToLoad.forEach(function (field) + { var fieldValue = vars.get("$field." + field); if (fieldValue) valuesToCheck[field] = fieldValue; - } - - var scanResults = DuplicateScannerUtils.ScanForDuplicates(scannerName, targetEntity, valuesToCheck, null) || []; + }); + + var scanResults = []; + + //don't search if only the id field has a value + var fieldsToCheck = Object.keys(valuesToCheck); + if (!(fieldsToCheck.length === 0 || (fieldsToCheck.length === 1 && entityFieldsToLoad.entityIdField in valuesToCheck))) + scanResults = DuplicateScannerUtils.ScanForDuplicates(scannerName, targetEntity, valuesToCheck, null) || []; + var duplicateIds = scanResults.map(function (duplicate) { return duplicate[indexsearch.FIELD_ID]; diff --git a/entity/QuickEntry_entity/QuickEntry_entity.aod b/entity/QuickEntry_entity/QuickEntry_entity.aod index 953caf69508..ac7e1e7e1bc 100644 --- a/entity/QuickEntry_entity/QuickEntry_entity.aod +++ b/entity/QuickEntry_entity/QuickEntry_entity.aod @@ -228,6 +228,24 @@ <stateProcess>%aditoprj%/entity/QuickEntry_entity/entityfields/leadquickacquisition/stateProcess.js</stateProcess> <onValueChange>%aditoprj%/entity/QuickEntry_entity/entityfields/leadquickacquisition/onValueChange.js</onValueChange> </entityField> + <entityConsumer> + <name>OrgAndPersDuplicates</name> + <dependency> + <name>dependency</name> + <entityName>AnyContact_entity</entityName> + <fieldName>ContactsByIds</fieldName> + </dependency> + <children> + <entityParameter> + <name>ContactIds_param</name> + <valueProcess>%aditoprj%/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js</valueProcess> + </entityParameter> + <entityParameter> + <name>WithPrivatePersons_param</name> + <valueProcess>%aditoprj%/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/withprivatepersons_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> </entityFields> <recordContainers> <jDitoRecordContainer> diff --git a/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js new file mode 100644 index 00000000000..206a4b979fd --- /dev/null +++ b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js @@ -0,0 +1,87 @@ +import("system.indexsearch"); +import("system.vars"); +import("DuplicateScanner_lib"); +import("system.result"); + +//trigger refresh +vars.get("$field.FIRSTNAME"); +vars.get("$field.LASTNAME"); + +var uid = vars.get("$field.UID"); +var idsForEmptyResult = JSON.stringify(["nodata"]); +var duplicateScans = []; + +duplicateScans.push(["PersonDuplicates", "Person_entity", {"CONTACTID" : uid}]); + +vars.get("$field.Contacts.insertedRows").forEach(function (contact) +{ + duplicateScans.push(["PersonDuplicates", "Person_entity", contact]); +}); + +var organisationName = vars.get("$field.ORGANISATION_NAME"); +//although the standard address is not set at this point, it can be assumed that it will be the first one +var firstOrganisationAddress = vars.get("$field.OrgAddresses.insertedRows")[0]; +if (organisationName || firstOrganisationAddress) +{ + var city = null; + var zipCode = null; + var address = null; + + if (firstOrganisationAddress) + { + city = firstOrganisationAddress["CITY"]; + zipCode = firstOrganisationAddress["ZIP"]; + address = firstOrganisationAddress["ADDRESS"]; + } + + duplicateScans.push(["OrganisationDuplicates", "Organisation_entity", { + "CONTACTID" : uid, + "NAME" : organisationName, + "STANDARD_CITY" : city, + "STANDARD_ZIP" : zipCode, + "STANDARD_ADDRESS" : address + }]); +} + +var duplicates = duplicateScans.reduce(function (duplicateArr, [scannerName, entity, fieldValues]) +{ + return duplicateArr.concat(_getDuplicates(scannerName, entity, fieldValues)); +}, []); + +if (duplicates.length === 0) + result.string(idsForEmptyResult); +else + result.string(JSON.stringify(duplicates)); + + +function _getDuplicates (pScannerName, pEntity, pEntityFieldValues) +{ + var fieldsToLoad = DuplicateScannerUtils.getEntityFieldObjectFromConfig(pScannerName, pEntity); + if (fieldsToLoad == null) + return []; + + var valuesToCheck = {}; + + var allFieldsToLoad = fieldsToLoad.entityFields.concat(fieldsToLoad.entityIdField); + allFieldsToLoad.forEach(function (field) + { + var fieldValue = field in pEntityFieldValues + ? pEntityFieldValues[field] + : vars.get("$field." + field); + if (fieldValue) + valuesToCheck[field] = fieldValue; + }); + + //don't search if only the id field has a value + var fieldsToCheck = Object.keys(valuesToCheck); + if (fieldsToCheck.length === 0 || (fieldsToCheck.length === 1 && fieldsToLoad.entityIdField in valuesToCheck)) + return []; + + var scanResults = DuplicateScannerUtils.ScanForDuplicates(pScannerName, pEntity, valuesToCheck, null) || []; + var duplicateIds = scanResults.map(function (duplicate) + { + return duplicate[indexsearch.FIELD_ID]; + }); + + return duplicateIds; +} diff --git a/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/withprivatepersons_param/valueProcess.js b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/withprivatepersons_param/valueProcess.js new file mode 100644 index 00000000000..40effa01784 --- /dev/null +++ b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/withprivatepersons_param/valueProcess.js @@ -0,0 +1,3 @@ +import("system.result"); + +result.string(true); \ No newline at end of file diff --git a/neonContext/AnyContact/AnyContact.aod b/neonContext/AnyContact/AnyContact.aod index b3ce579fd4d..60b312b5724 100644 --- a/neonContext/AnyContact/AnyContact.aod +++ b/neonContext/AnyContact/AnyContact.aod @@ -9,5 +9,9 @@ <name>1ea0b1ed-c2b5-4b8c-b359-27ffdef6e5ea</name> <view>AnyContactLookup_view</view> </neonViewReference> + <neonViewReference> + <name>45efa66c-b525-447c-8e16-014942843299</name> + <view>AnyContactDuplicates_view</view> + </neonViewReference> </references> </neonContext> diff --git a/neonView/AnyContactDuplicates_view/AnyContactDuplicates_view.aod b/neonView/AnyContactDuplicates_view/AnyContactDuplicates_view.aod new file mode 100644 index 00000000000..ff719ed3711 --- /dev/null +++ b/neonView/AnyContactDuplicates_view/AnyContactDuplicates_view.aod @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.6" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.6"> + <name>AnyContactDuplicates_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <layout> + <noneLayout> + <name>layout</name> + </noneLayout> + </layout> + <children> + <tableViewTemplate> + <name>Table</name> + <hideActions v="true" /> + <entityField>#ENTITY</entityField> + <hideHeader v="true" /> + <title>Duplicates</title> + <columns> + <neonTableColumn> + <name>c37eef9d-3e62-4353-8499-fae685376761</name> + <entityField>#IMAGE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>39645840-a9ca-4d72-a1a1-0355810243de</name> + <entityField>ORGANISATION_NAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>556ac9f2-c0ce-4401-83d1-f17ead6e14f4</name> + <entityField>PERSON_FULL_NAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>aa4ce6ed-7f7d-477c-accd-81a2f02a72e6</name> + <entityField>STANDARD_PHONE_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>3f5d8420-f3f2-4845-b4a9-14d31905bd9f</name> + <entityField>STANDARD_EMAIL_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>c90379c3-f74b-4344-bc56-8e7863bb8f65</name> + <entityField>ADDRESS_ID</entityField> + </neonTableColumn> + </columns> + </tableViewTemplate> + </children> +</neonView> diff --git a/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod b/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod index aca74dc7364..1ffb18b8f78 100644 --- a/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod +++ b/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod @@ -13,7 +13,7 @@ <favoriteActionGroup2></favoriteActionGroup2> <favoriteActionGroup3>RunActionGroup</favoriteActionGroup3> <entityField>#ENTITY</entityField> - <isCreatable v="false" /> + <isCreatable v="true" /> <isDeletable v="false" /> <isEditable v="true" /> <columns> diff --git a/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod b/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod index 125b8e68227..9598c3420b1 100644 --- a/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod +++ b/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod @@ -11,6 +11,11 @@ </boxLayout> </layout> <children> + <neonViewReference> + <name>adf71384-2c12-401b-a3aa-89ed23e757c2</name> + <entityField>OrgAndPersDuplicates</entityField> + <view>AnyContactDuplicates_view</view> + </neonViewReference> <genericViewTemplate> <name>GeneralData</name> <editMode v="true" /> diff --git a/process/DuplicateScanner_lib/process.js b/process/DuplicateScanner_lib/process.js index 9e6e4e7557b..ed3ef2cce6b 100644 --- a/process/DuplicateScanner_lib/process.js +++ b/process/DuplicateScanner_lib/process.js @@ -801,6 +801,32 @@ DuplicateScannerUtils.GetEntityFieldsFromConfig = function(pFilterName, pTargetE return entityFields; } +/** + * Loads the configured entity fields required for the given duplicate scanner. + * + * @param {String} pFilterName the name of the scanner + * @param {String} pTargetEntity the target entity + * @return {Object} an object with two properties: + * <ul> + * <li>entityFields: array of entity fields</li> + * <li>entityIdField: the id field name as string</li> + * </ul> + */ +DuplicateScannerUtils.getEntityFieldObjectFromConfig = function (pFilterName, pTargetEntity) +{ + var indexPattern = _DuplicateScannerUtils._loadIndexPattern(pFilterName, pTargetEntity); + if (!indexPattern) + return null; + var fieldConfigs = _DuplicateScannerUtils._loadEntityFieldConfigsFromPattern(indexPattern); + if (fieldConfigs == null || fieldConfigs.length === 0) + return null; + + return { + entityFields : _DuplicateScannerUtils._loadEntityFieldsFromFieldConfigs(fieldConfigs), + entityIdField : _DuplicateScannerUtils._loadEntityIdField(pFilterName, pTargetEntity) + }; +} + DuplicateScannerUtils.GetUnrelatedRelationsForDuplicate = function(pDuplicateId) { let unrelatedIds = []; -- GitLab