Skip to content
Snippets Groups Projects
Commit 5df4ed48 authored by Sebastian Pongratz's avatar Sebastian Pongratz :ping_pong:
Browse files

Merge branch '2021.0_duplicates' into '2021.0'

2021.0 duplicates

See merge request xrm/basic!640
parents 3a1eeda6 cbe4da1f
No related branches found
No related tags found
No related merge requests found
Showing
with 36 additions and 348 deletions
import("system.logging");
import("system.vars");
import("system.neon");
let contextName = "Duplicates";
let viewName = "OrganisationClusterMain_view";
var params = {};
params["ClusterId_param"] = vars.get("$sys.selection")[0];
params["TargetEntity"] = "Organisation_entity";
neon.openContext(contextName, viewName, null, neon.OPERATINGSTATE_VIEW, params);
\ No newline at end of file
import("system.logging");
import("system.vars");
import("system.neon");
let contextName = "Duplicates";
let viewName = "PersonClusterMain_view";
var params = {};
params["ClusterId_param"] = vars.get("$sys.selection")[0];
params["TargetEntity"] = "Person_entity";
neon.openContext(contextName, viewName, null, neon.OPERATINGSTATE_VIEW, params);
\ No newline at end of file
import("system.vars");
import("system.result");
let clusterId = vars.get("$param.ClusterId_param");
result.string(clusterId);
\ No newline at end of file
import("system.result");
result.string("Organisation_entity");
\ No newline at end of file
import("system.vars");
import("system.result");
//let clusterId = vars.get("$sys.selection");
let clusterId = vars.get("$param.ClusterId_param");
result.string(clusterId);
\ No newline at end of file
import("system.result");
result.string("Person_entity");
\ No newline at end of file
import("system.result");
import("system.translate");
result.string(translate.text("Person duplicates"));
\ No newline at end of file
import("Sql_lib");
import("system.logging");
import("system.db");
import("system.vars");
import("system.result");
var INDEX_CLUSTERID = 0;
var INDEX_FIRSTNAME = 1;
var INDEX_LASTNAME = 2;
var INDEX_ORGNAME = 1;
let targetEntity = vars.get("$param.TargetEntity");
let duplicates = [];
let selectedClusterId = vars.get("$param.ClusterId_param");
let duplicateInfosQuery = new SqlBuilder();
let selectedId = vars.get("$local.idvalues");
if(selectedId)
{
/*
* Definitely a todo.
* Support for the action "Ignore whole cluster"
* If this action is used two times in a row, an error occurs on the second time.
* Although the selected record isn't present in the recordcontainer any more, the core tries to load a record with the same id.
* As a result an error gets thrown. If a dummy record gets returned here, it plays along with the current internal logic and doesn't throw an error.
* If a preview should be shown, this part of the container has to be extended.
*/
duplicates.push([selectedId, "", "", "", ""]);
result.object(duplicates);
}
else
{
if(targetEntity == "Person_entity")
{
duplicateInfosQuery.select("CLUSTERID, FIRSTNAME, LASTNAME")
.from("DUPLICATECLUSTERS")
.join("CONTACT", "CONTACT.CONTACTID = DUPLICATEID")
.join("PERSON", "PERSON.PERSONID = CONTACT.PERSON_ID")
.where("DUPLICATEID not in (select UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID from UNRELATEDDUPLICATES)")
.andIfSet("DUPLICATECLUSTERS.CLUSTERID", selectedClusterId)
.orderBy("CLUSTERID");
}
else
{
duplicateInfosQuery.select("CLUSTERID, ORGANISATION.\"NAME\"")
.from("DUPLICATECLUSTERS")
.join("CONTACT", "CONTACT.CONTACTID = DUPLICATEID")
.join("ORGANISATION", "ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID")
.where("DUPLICATEID not in (select UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID from UNRELATEDDUPLICATES)")
.andIfSet("DUPLICATECLUSTERS.CLUSTERID", vars.get("$local.idvalues"), SqlBuilder.IN())
.orderBy("CLUSTERID");
}
let duplicateInfos = duplicateInfosQuery.table();
let MAX_SHOW_CLUSTER_RECORDS = 4;
let recordClusterId = "";
let recordDescription = "";
let recordDuplicateInClusterCount = 0;
for (let i = 0; i < duplicateInfos.length; i++)
{
let currentClusterId = duplicateInfos[i][INDEX_CLUSTERID];
let currentDescription = "";
//Build the description depending on the targetEntity
if(targetEntity == "Person_entity")
currentDescription = duplicateInfos[i][INDEX_FIRSTNAME] + " " + duplicateInfos[i][INDEX_LASTNAME];
else
currentDescription = duplicateInfos[i][INDEX_ORGNAME];
if(i == 0)
{
recordClusterId = currentClusterId;
recordDescription = currentDescription;
recordDuplicateInClusterCount = 1;
continue;
}
//If the record belongs to the same Cluster as the one before, append its value and increase the counter
//otherwise write the clusters record an start a new record.
if(recordClusterId == currentClusterId)
{
if(recordDuplicateInClusterCount < MAX_SHOW_CLUSTER_RECORDS)
recordDescription += ", " + currentDescription;
if(recordDuplicateInClusterCount == MAX_SHOW_CLUSTER_RECORDS)
recordDescription += ", ..."
recordDuplicateInClusterCount++;
/*
* Finish the current record if its the last duplicate.
* It has to be checked wether or not more than one element is currently in the cluster:
* Normally, there are always at least 2 elements in a cluster. If then a duplicate relation
* is beign ignored, there's only one record left in this particluar cluster.
* As there are then no interactions possible (and a cluster of one is no cluster), this cluster musn't be shown in the list.
*/
if(i == duplicateInfos.length-1 && recordDuplicateInClusterCount > 1)
duplicates.push([recordClusterId, recordDescription, recordDuplicateInClusterCount, targetEntity, recordClusterId]);
}
else
{
/*
* Finish the current record if its the next cluster.
* It has to be checked wether or not more than one element is currently in the cluster:
* Normally, there are always at least 2 elements in a cluster. If then a duplicate relation
* is beign ignored, there's only one record left in this particluar cluster.
* As there would be no interactions possible (and a cluster of one is no cluster), this cluster musn't be shown in the list.
*/
if(recordDuplicateInClusterCount > 1)
duplicates.push([recordClusterId, recordDescription, recordDuplicateInClusterCount, targetEntity, recordClusterId]);
recordClusterId = currentClusterId
recordDescription = currentDescription
recordDuplicateInClusterCount = 1;
}
}
result.object(duplicates);
}
...@@ -1026,10 +1026,6 @@ ...@@ -1026,10 +1026,6 @@
<name>OnlyShowContactIds_param</name> <name>OnlyShowContactIds_param</name>
<valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js</valueProcess> <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js</valueProcess>
</entityParameter> </entityParameter>
<entityParameter>
<name>ExcludedContactIds_param</name>
<valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js</valueProcess>
</entityParameter>
</children> </children>
</entityConsumer> </entityConsumer>
<entityConsumer> <entityConsumer>
...@@ -1264,52 +1260,6 @@ ...@@ -1264,52 +1260,6 @@
<name>FilterPreSet_param</name> <name>FilterPreSet_param</name>
<expose v="true" /> <expose v="true" />
</entityParameter> </entityParameter>
<entityProvider>
<name>NonselfDuplicates</name>
<documentation>%aditoprj%/entity/Organisation_entity/entityfields/nonselfduplicates/documentation.adoc</documentation>
<dependencies>
<entityDependency>
<name>2e410b9e-5ebc-48ea-9562-da386202d7e8</name>
<entityName>Duplicates_entity</entityName>
<fieldName>DuplicateOrganisationsConsumer</fieldName>
<isConsumer v="false" />
</entityDependency>
</dependencies>
<children>
<entityParameter>
<name>AttributeKeyId_param</name>
<expose v="false" />
</entityParameter>
<entityParameter>
<name>DuplicateCurrentContactId_param</name>
<expose v="false" />
</entityParameter>
<entityParameter>
<name>ExcludedContactIds_param</name>
<expose v="false" />
</entityParameter>
<entityParameter>
<name>OnlyShowContactIds_param</name>
<expose v="true" />
</entityParameter>
<entityParameter>
<name>OnlyOwnSupervised_param</name>
<expose v="false" />
</entityParameter>
<entityParameter>
<name>MapViewAdditionalFeatures_param</name>
<expose v="false" />
</entityParameter>
<entityParameter>
<name>MapViewCenterLon_param</name>
<expose v="false" />
</entityParameter>
<entityParameter>
<name>MapViewCenterLat_param</name>
<expose v="false" />
</entityParameter>
</children>
</entityProvider>
<entityConsumer> <entityConsumer>
<name>AttributesFilter</name> <name>AttributesFilter</name>
<dependency> <dependency>
...@@ -1526,22 +1476,13 @@ ...@@ -1526,22 +1476,13 @@
<stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js</stateProcess> <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js</stateProcess>
</entityActionField> </entityActionField>
<entityActionField> <entityActionField>
<name>IgnoreDuplicate</name> <name>IgnoreDuplicates</name>
<title>Ignore Duplicate</title> <title>Ignore Duplicate(s)</title>
<onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js</onActionProcess> <onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicates/onActionProcess.js</onActionProcess>
<isMenuAction v="true" />
<isObjectAction v="false" /> <isObjectAction v="false" />
<isSelectionAction v="true" /> <isSelectionAction v="true" />
<iconId>VAADIN:CLOSE</iconId> <iconId>VAADIN:CLOSE</iconId>
<stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js</stateProcess> <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicates/stateProcess.js</stateProcess>
</entityActionField>
<entityActionField>
<name>IgnoreWholeCluster</name>
<title>Ignore whole Cluster</title>
<onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js</onActionProcess>
<isObjectAction v="false" />
<iconId>VAADIN:CLOSE</iconId>
<stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js</stateProcess>
</entityActionField> </entityActionField>
</children> </children>
</entityActionGroup> </entityActionGroup>
...@@ -1880,6 +1821,13 @@ ...@@ -1880,6 +1821,13 @@
<filterConditionProcess>%aditoprj%/entity/Organisation_entity/recordcontainers/db/filterextensions/responsibleassignment/filterConditionProcess.js</filterConditionProcess> <filterConditionProcess>%aditoprj%/entity/Organisation_entity/recordcontainers/db/filterextensions/responsibleassignment/filterConditionProcess.js</filterConditionProcess>
<filtertype>EXTENDED</filtertype> <filtertype>EXTENDED</filtertype>
</filterExtension> </filterExtension>
<filterExtension>
<name>Duplicates_filter</name>
<title>Duplicates</title>
<contentType>NUMBER</contentType>
<filterConditionProcess>%aditoprj%/entity/Organisation_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js</filterConditionProcess>
<filtertype>BASIC</filtertype>
</filterExtension>
<filterExtension> <filterExtension>
<name>Communication_Mail_filter</name> <name>Communication_Mail_filter</name>
<title>Communication: Mail</title> <title>Communication: Mail</title>
......
import("system.neon");
import("system.vars");
import("DuplicateScanner_lib");
let sourceContactId = vars.get("$param.DuplicateCurrentContactId_param");
let selectedContactId = vars.get("$sys.selection");
let clusterId = DuplicateScannerUtils.getClusterId(sourceContactId);
DuplicateScannerUtils.createUnrelatedDuplicateRelation(sourceContactId, selectedContactId, clusterId);
neon.refreshAll();
\ No newline at end of file
import("system.neon");
import("system.vars");
import("system.result");
import("DuplicateScanner_lib");
var selection = vars.get("$sys.selection");
var contactId = vars.get("$param.DuplicateCurrentContactId_param");
DuplicateScannerUtils.ignoreDuplicates("Organisation_entity", contactId, selection);
// Update the rows, because UNRELATEDDUPLICATES wont trigger a refresh even with write entities
neon.refreshAll();
import("system.logging");
import("system.neon");
import("system.vars");
import("DuplicateScanner_lib");
import("system.notification");
let contactId = vars.get("$field.CONTACTID");
let clusterId = DuplicateScannerUtils.getClusterId(contactId);
let duplicateContactIdsInClusterRay = DuplicateScannerUtils.getCachedDuplicatesForClusterId(clusterId)
if(duplicateContactIdsInClusterRay.length > 1)
{
let referenceDuplicateId = duplicateContactIdsInClusterRay[0];
for (let i = 1; i < duplicateContactIdsInClusterRay.length; i++)
{
DuplicateScannerUtils.createUnrelatedDuplicateRelation(referenceDuplicateId, duplicateContactIdsInClusterRay[i], clusterId);
}
}
import("system.logging");
import("system.vars");
import("system.neon");
import("system.result");
//Actions to show in the duplicates view inside the persons main view
let actionState = vars.get("$param.DuplicateActionsControl_param");
if(actionState != null && actionState != "2")//todo replace with keyword
result.string(neon.COMPONENTSTATE_INVISIBLE);
\ No newline at end of file
import("Employee_lib"); import("Employee_lib");
import("system.vars"); import("system.vars");
import("system.neon"); import("system.neon");
import("DuplicateScanner_lib"); import("DuplicateMerge_lib");
let sourceContactId = vars.get("$param.DuplicateCurrentContactId_param"); let sourceContactId = vars.get("$param.DuplicateCurrentContactId_param");
let targetContactId = vars.get("$sys.selection")[0]; let targetContactId = vars.get("$sys.selection")[0];
//todo the actual merge ought to happen in a separate view where the contact infos can be merged manually by the user. //todo the actual merge ought to happen in a separate view where the contact infos can be merged manually by the user.
let mergeSuccess = DuplicateScannerUtils.mergeOrganisation(sourceContactId, targetContactId); let mergeSuccess = DuplicateMergeUtils.mergeOrganisation(sourceContactId, targetContactId);
if(mergeSuccess) if(mergeSuccess)
{ {
let currentContactId = EmployeeUtils.getCurrentContactId(); let currentContactId = EmployeeUtils.getCurrentContactId();
if(currentContactId == null) if(currentContactId == null)
currentContactId = ""; currentContactId = "";
DuplicateScannerUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Organisation"); DuplicateMergeUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Organisation");
neon.openContext("Organisation", "OrganisationMain_view", [targetContactId], neon.OPERATINGSTATE_VIEW, null) neon.openContext("Organisation", "OrganisationMain_view", [targetContactId], neon.OPERATINGSTATE_VIEW, null)
} }
\ No newline at end of file
import("Employee_lib"); import("Employee_lib");
import("system.vars"); import("system.vars");
import("system.neon"); import("system.neon");
import("DuplicateScanner_lib"); import("DuplicateMerge_lib");
let targetContactId = vars.get("$param.DuplicateCurrentContactId_param"); let targetContactId = vars.get("$param.DuplicateCurrentContactId_param");
let sourceContactId = vars.get("$sys.selection")[0]; let sourceContactId = vars.get("$sys.selection")[0];
//todo the actual merge ought to happen in a separate view where the contact infos can be merged manually by the user. //todo the actual merge ought to happen in a separate view where the contact infos can be merged manually by the user.
let mergeSuccess = DuplicateScannerUtils.mergeOrganisation(sourceContactId, targetContactId); let mergeSuccess = DuplicateMergeUtils.mergeOrganisation(sourceContactId, targetContactId);
if(mergeSuccess) if(mergeSuccess)
{ {
let currentContactId = EmployeeUtils.getCurrentContactId(); let currentContactId = EmployeeUtils.getCurrentContactId();
if(currentContactId == null) if(currentContactId == null)
currentContactId = ""; currentContactId = "";
DuplicateScannerUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Organisation"); DuplicateMergeUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Organisation");
//neon.refresh() with no fields will refresh the current image (and all sub images) but NOT the preview. neon.refreshAll() would refresh both, //neon.refresh() with no fields will refresh the current image (and all sub images) but NOT the preview. neon.refreshAll() would refresh both,
//why it would lead to an error because it's trying to load the already opened preview of the duplicateContact which just got deleted //why it would lead to an error because it's trying to load the already opened preview of the duplicateContact which just got deleted
//and does not exist any more which results in an exception //and does not exist any more which results in an exception
......
Provides organisation duplicate-records without the `Organisation_entity` scope, for example for the `Duplicates_entity`.
The provider is named `NonselfDuplicates` to differentiate this provider and the `SelfDuplicates`-provider.
\ No newline at end of file
import("system.logging");
import("system.vars");
import("system.result");
import("DuplicateScanner_lib");
let unrelatedIds = DuplicateScannerUtils.getUnrelatedRelationsForDuplicate(vars.get("$field.CONTACTID"));
result.string(JSON.stringify(unrelatedIds));
\ No newline at end of file
import("system.project");
import("system.indexsearch");
import("system.vars");
import("DuplicateScanner_lib"); import("DuplicateScanner_lib");
import("system.result"); import("system.result");
var scannerName = "OrganisationDuplicates"; var duplicateIds = DuplicateScannerUtils.getDuplicateIdsByEntityVars("Organisation_entity");
var targetEntity = "Organisation_entity"; result.string(JSON.stringify(duplicateIds));
var valuesToCheck = {};
var entityFieldsToLoad = DuplicateScannerUtils.getEntityFieldObjectFromConfig(scannerName, targetEntity);
var idsForEmptyResult = JSON.stringify(["nodata"]);
if (entityFieldsToLoad == null)
result.string(idsForEmptyResult);
else
{
//Read the values of all available entity fields and write the fieldname7value combination
//as key/value pairs into an object. This is used to trigger the scan for duplicates
vars.get("$field.NAME")
vars.get("$field.STANDARD_CITY");
vars.get("$field.STANDARD_ZIP");
vars.get("$field.STANDARD_ADDRESS");
var allFieldsToLoad = entityFieldsToLoad.entityFields.concat(entityFieldsToLoad.entityIdField);
allFieldsToLoad.forEach(function (field)
{
var fieldValue = vars.get("$field." + field);
if (fieldValue)
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];
});
/*
* To achieve that if there are no duplicates, no contacts should be shown and therefore returned by the
* recordcontainer, an invalid id gets returned. It then is used in the conditionProcess to load the duplicates.
* Because of its invalidity, no records are shown.
*/
if (duplicateIds.length == 0)
result.string(idsForEmptyResult);
else
result.string(JSON.stringify(duplicateIds));
}
\ No newline at end of file
...@@ -5,15 +5,10 @@ import("KeywordRegistry_basic"); ...@@ -5,15 +5,10 @@ import("KeywordRegistry_basic");
import("system.result"); import("system.result");
var filter = vars.get("$param.FilterPreSet_param"); var filter = vars.get("$param.FilterPreSet_param");
if(!filter && vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER)
var res;
if (filter)
res = filter;
else if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER)
{ {
var statusInactive = $KeywordRegistry.contactStatus$inactive(); var statusInactive = $KeywordRegistry.contactStatus$inactive();
filter = JSON.stringify({
filter = {
type: "group", type: "group",
operator: "AND", operator: "AND",
childs: [{ childs: [{
...@@ -24,9 +19,9 @@ else if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FIL ...@@ -24,9 +19,9 @@ else if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FIL
key: statusInactive, key: statusInactive,
value: KeywordUtils.getViewValue($KeywordRegistry.contactStatus(), statusInactive) value: KeywordUtils.getViewValue($KeywordRegistry.contactStatus(), statusInactive)
}] }]
}; });
res = JSON.stringify(filter); }
if(filter)
{
result.string(filter);
} }
if (res)
result.string(res);
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment