diff --git a/.liquibase/Data_alias/basic/2021.0.3/Duplicate/alter_unrelatedduplicates.xml b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/alter_unrelatedduplicates.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6e59bdbd9153071bac10d9f15d87c48b7c2b300 --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/alter_unrelatedduplicates.xml @@ -0,0 +1,24 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="p.neub" id="082919b2-ebfd-4864-9839-89e593fb6f2d"> + <dropColumn tableName="UNRELATEDDUPLICATES"> + <column name="CLUSTERID"/> + </dropColumn> + <addColumn tableName="UNRELATEDDUPLICATES"> + <column name="DUPLICATETYPE" type="NVARCHAR(63)"/> + </addColumn> + <createIndex indexName="IDX_UNRELATEDDUPLICATES_DUPLICATETYPE" tableName="UNRELATEDDUPLICATES"> + <column name="DUPLICATETYPE"/> + </createIndex> + <createIndex indexName="IDX_UNRELATEDDUPLICATES_SOURCEDUPLICATEID" tableName="UNRELATEDDUPLICATES"> + <column name="SOURCEDUPLICATEID"/> + </createIndex> + <createIndex indexName="IDX_UNRELATEDDUPLICATES" tableName="UNRELATEDDUPLICATES"> + <column name="DUPLICATETYPE"/> + <column name="SOURCEDUPLICATEID"/> + <column name="UNRELATEDDUPLICATEID"/> + </createIndex> + </changeSet> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.0.3/Duplicate/changelog.xml b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/changelog.xml new file mode 100644 index 0000000000000000000000000000000000000000..10ee1b9cc2cb6cd02db3b863cb695a114c9625d1 --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/changelog.xml @@ -0,0 +1,10 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <include relativeToChangelogFile="true" file="create_hasduplicate.xml"/> + <include relativeToChangelogFile="true" file="drop_duplicateclusters.xml"/> + <include relativeToChangelogFile="true" file="drop_duplicatescannerresultfieldconfig.xml"/> + <include relativeToChangelogFile="true" file="alter_unrelatedduplicates.xml"/> + <include relativeToChangelogFile="true" file="migrate_unrelatedduplicates.xml"/> +</databaseChangeLog> diff --git a/.liquibase/Data_alias/basic/2021.0.3/Duplicate/create_hasduplicate.xml b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/create_hasduplicate.xml new file mode 100644 index 0000000000000000000000000000000000000000..f61d512d35ce13c2928fc3e6922ac8f25058542d --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/create_hasduplicate.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="p.neub" id="db282e4c-01c2-4b55-aa85-01306f4b1f7a"> + <createTable tableName="HASDUPLICATE"> + <column name="HASDUPLICATEID" type="CHAR(36)"> + <constraints primaryKey="true" primaryKeyName="PK_HASDUPLICATE_HASDUPLICATEID"></constraints> + </column> + + <column name="OBJECT_TYPE" type="NVARCHAR(63)"/> + <column name="OBJECT_ROWID" type="CHAR(36)"/> + <column name="DUPLICATECOUNT" type="INTEGER"/> + </createTable> + <createIndex indexName="IDX_HASDUPLICATE" tableName="HASDUPLICATE"> + <column name="OBJECT_TYPE"/> + <column name="OBJECT_ROWID"/> + </createIndex> + <createIndex indexName="IDX_HASDUPLICATE_OBJECT_ROWID" tableName="HASDUPLICATE"> + <column name="OBJECT_ROWID"/> + </createIndex> + </changeSet> +</databaseChangeLog> diff --git a/.liquibase/Data_alias/basic/2021.0.3/Duplicate/drop_duplicateclusters.xml b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/drop_duplicateclusters.xml new file mode 100644 index 0000000000000000000000000000000000000000..b61e7cb651dec4c502d4956f3caf44b7640bbb1c --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/drop_duplicateclusters.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="p.neub" id="45db014f-922d-4619-b69f-9bf3a36285f4"> + <dropTable tableName="DUPLICATECLUSTERS"/> + </changeSet> +</databaseChangeLog> diff --git a/.liquibase/Data_alias/basic/2021.0.3/Duplicate/drop_duplicatescannerresultfieldconfig.xml b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/drop_duplicatescannerresultfieldconfig.xml new file mode 100644 index 0000000000000000000000000000000000000000..6804c4345d4c745de74f6850985649c4329e11bb --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/drop_duplicatescannerresultfieldconfig.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="p.neub" id="fcf592c4-e07c-4919-bfba-0bf99db161ec"> + <dropTable tableName="DUPLICATESCANNERRESULTFIELDCONFIG"/> + </changeSet> +</databaseChangeLog> diff --git a/.liquibase/Data_alias/basic/2021.0.3/Duplicate/migrate_unrelatedduplicates.xml b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/migrate_unrelatedduplicates.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac72a09e145674f5a0774b07bd80a1d1b7db5d50 --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.3/Duplicate/migrate_unrelatedduplicates.xml @@ -0,0 +1,23 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="p.neub" id="587e1ed7-b7c8-46f0-9058-01521ed97fd5"> + <update tableName="UNRELATEDDUPLICATES"> + <column name="DUPLICATETYPE" value="Person_entity"/> + <where> + DUPLICATETYPE is null and + (select PERSON_ID from CONTACT where CONTACTID = SOURCEDUPLICATEID) + is not null + </where> + </update> + <update tableName="UNRELATEDDUPLICATES"> + <column name="DUPLICATETYPE" value="Organisation_entity"/> + <where> + DUPLICATETYPE is null and + (select PERSON_ID from CONTACT where CONTACTID = SOURCEDUPLICATEID) + is null + </where> + </update> + </changeSet> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.0.3/changelog.xml b/.liquibase/Data_alias/basic/2021.0.3/changelog.xml index 2c2fee9f8764ff778599efbc23b6f9aa499df6eb..887abd39115340a861cfcd8a4b9c9cdb09c74cda 100644 --- a/.liquibase/Data_alias/basic/2021.0.3/changelog.xml +++ b/.liquibase/Data_alias/basic/2021.0.3/changelog.xml @@ -9,4 +9,5 @@ <include relativeToChangelogFile="true" file="Checklists/changelog.xml"/> <include relativeToChangelogFile="true" file="Planning/changelog.xml"/> <include relativeToChangelogFile="true" file="alter_origin_attribute.xml"/> + <include relativeToChangelogFile="true" file="Duplicate/changelog.xml"/> </databaseChangeLog> \ No newline at end of file diff --git a/aliasDefinition/Data_alias/Data_alias.aod b/aliasDefinition/Data_alias/Data_alias.aod index 8b751faf53e63dcb050f7de37e2146edf319454f..5943721355fd27f1c045dd4b6278fb3daa8604cb 100644 --- a/aliasDefinition/Data_alias/Data_alias.aod +++ b/aliasDefinition/Data_alias/Data_alias.aod @@ -12176,82 +12176,6 @@ </entityFieldDb> </entityFields> </entityDb> - <entityDb> - <name>DUPLICATECLUSTERS</name> - <dbName></dbName> - <idColumn>ID</idColumn> - <idGeneratorType v="0" /> - <idGeneratorInterval v="1" /> - <documentation></documentation> - <title></title> - <description></description> - <auditSyncConfig> - <name>auditSyncConfig</name> - <auditMode v="0" /> - <syncActive v="false" /> - <syncComplete v="true" /> - <syncDirection v="1" /> - <syncIds></syncIds> - </auditSyncConfig> - <entityFields> - <entityFieldDb> - <name>DUPLICATEID</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="1" /> - <size v="36" /> - <scale v="0" /> - <notNull v="true" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>CLUSTERID</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="1" /> - <size v="36" /> - <scale v="0" /> - <notNull v="true" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>ID</name> - <dbName></dbName> - <primaryKey v="true" /> - <columnType v="1" /> - <size v="36" /> - <scale v="0" /> - <notNull v="true" /> - <isUnique v="true" /> - <index v="true" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>TARGET_ENTITY</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="12" /> - <size v="200" /> - <scale v="0" /> - <notNull v="false" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - </entityFields> - </entityDb> <entityDb> <name>UNRELATEDDUPLICATES</name> <dbName></dbName> @@ -12313,136 +12237,11 @@ <description></description> </entityFieldDb> <entityFieldDb> - <name>CLUSTERID</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="1" /> - <size v="36" /> - <scale v="0" /> - <notNull v="false" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - </entityFields> - </entityDb> - <entityDb> - <name>DUPLICATESCANNERRESULTFIELDCONFIG</name> - <dbName></dbName> - <idColumn>ID</idColumn> - <idGeneratorType v="0" /> - <idGeneratorInterval v="1" /> - <documentation></documentation> - <title></title> - <description></description> - <auditSyncConfig> - <name>auditSyncConfig</name> - <auditMode v="0" /> - <syncActive v="false" /> - <syncComplete v="true" /> - <syncDirection v="1" /> - <syncIds></syncIds> - </auditSyncConfig> - <entityFields> - <entityFieldDb> - <name>DATE_EDIT</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="93" /> - <size v="29" /> - <scale v="9" /> - <notNull v="false" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>DUPLICATESCANNER_ID</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="1" /> - <size v="36" /> - <scale v="0" /> - <notNull v="true" /> - <isUnique v="false" /> - <index v="true" /> - <documentation></documentation> - <title></title> - <description></description> - <dependencies> - <entityDependency> - <name>9b123a17-2f41-49f8-9492-f386ece46f7c</name> - <entityName>DUPLICATESCANNER</entityName> - <fieldName>ID</fieldName> - </entityDependency> - </dependencies> - </entityFieldDb> - <entityFieldDb> - <name>DATE_NEW</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="93" /> - <size v="29" /> - <scale v="9" /> - <notNull v="true" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>ID</name> - <dbName></dbName> - <primaryKey v="true" /> - <columnType v="1" /> - <size v="36" /> - <scale v="0" /> - <notNull v="true" /> - <isUnique v="true" /> - <index v="true" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>USER_NEW</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="12" /> - <size v="50" /> - <scale v="0" /> - <notNull v="true" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>ENTITY_FIELD_NAME</name> + <name>DUPLICATETYPE</name> <dbName></dbName> <primaryKey v="false" /> <columnType v="12" /> - <size v="100" /> - <scale v="0" /> - <notNull v="false" /> - <isUnique v="false" /> - <index v="false" /> - <documentation></documentation> - <title></title> - <description></description> - </entityFieldDb> - <entityFieldDb> - <name>USER_EDIT</name> - <dbName></dbName> - <primaryKey v="false" /> - <columnType v="12" /> - <size v="50" /> + <size v="63" /> <scale v="0" /> <notNull v="false" /> <isUnique v="false" /> @@ -18533,6 +18332,82 @@ </entityFieldDb> </entityFields> </entityDb> + <entityDb> + <name>HASDUPLICATE</name> + <dbName></dbName> + <idColumn>HASDUPLICATEID</idColumn> + <idGeneratorType v="0" /> + <idGeneratorInterval v="1" /> + <documentation></documentation> + <title></title> + <description></description> + <auditSyncConfig> + <name>auditSyncConfig</name> + <auditMode v="0" /> + <syncActive v="false" /> + <syncComplete v="true" /> + <syncDirection v="1" /> + <syncIds></syncIds> + </auditSyncConfig> + <entityFields> + <entityFieldDb> + <name>OBJECT_ROWID</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="1" /> + <size v="36" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>OBJECT_TYPE</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="12" /> + <size v="63" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>DUPLICATECOUNT</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="4" /> + <size v="10" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>HASDUPLICATEID</name> + <dbName></dbName> + <primaryKey v="true" /> + <columnType v="1" /> + <size v="36" /> + <scale v="0" /> + <notNull v="true" /> + <isUnique v="true" /> + <index v="true" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + </entityFields> + </entityDb> <entityDb> <name>AB_SYNCCONTACT</name> <dbName></dbName> diff --git a/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod b/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod index 1a320de850c8f403f718f35e8b8e7f72857f1f8e..70f6ccdb13ba5c645c402c818b42d9e954b3aa69 100644 --- a/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod +++ b/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod @@ -330,11 +330,6 @@ <kind v="10077" /> <title></title> </entityNode> - <entityNode> - <name>Duplicates</name> - <kind v="10077" /> - <title></title> - </entityNode> <entityNode> <name>AuditLogHistory</name> <kind v="10077" /> diff --git a/entity/Contact_entity/recordcontainers/db/onDBDelete.js b/entity/Contact_entity/recordcontainers/db/onDBDelete.js index 61afa46ba950988b60515de3cae0e732553cfe69..c7b08f7110800f03efb3ca755622080493d3fd48 100644 --- a/entity/Contact_entity/recordcontainers/db/onDBDelete.js +++ b/entity/Contact_entity/recordcontainers/db/onDBDelete.js @@ -5,7 +5,8 @@ import("system.vars"); import("DuplicateScanner_lib"); var contactId = vars.get("$field.CONTACTID"); -DuplicateScannerUtils.deleteCachedDuplicate(contactId); +DuplicateScannerUtils.deleteHasDuplicateEntries("Person_entity", [contactId]); +DuplicateScannerUtils.deleteHasDuplicateEntries("Organisation_entity", [contactId]); new AttributeRelationQuery(contactId, null, "Person") .deleteAllAttributes(); diff --git a/entity/DuplicateOrganisation_entity/DuplicateOrganisation_entity.aod b/entity/DuplicateOrganisation_entity/DuplicateOrganisation_entity.aod new file mode 100644 index 0000000000000000000000000000000000000000..eb3318d1aad761ad8f944dd5596c80d20a8999b3 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/DuplicateOrganisation_entity.aod @@ -0,0 +1,241 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.18" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.18"> + <name>DuplicateOrganisation_entity</name> + <title>Duplicate</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <grantCreate v="false" /> + <grantUpdate v="false" /> + <grantDelete v="false" /> + <initFilterProcess>%aditoprj%/entity/DuplicateOrganisation_entity/initFilterProcess.js</initFilterProcess> + <titlePlural>Duplicates</titlePlural> + <recordContainer>db</recordContainer> + <entityFields> + <entityProvider> + <name>#PROVIDER</name> + <targetContextField>targetContext</targetContextField> + <targetIdField>CONTACTID</targetIdField> + <dependencies> + <entityDependency> + <name>a666bb24-2875-481a-879e-86b8e8517c1e</name> + <entityName>Organisation_entity</entityName> + <fieldName>Duplicates</fieldName> + <isConsumer v="false" /> + </entityDependency> + </dependencies> + </entityProvider> + <entityProvider> + <name>#PROVIDER_AGGREGATES</name> + <useAggregates v="true" /> + </entityProvider> + <entityField> + <name>CONTACTID</name> + <linkedContext>Organisation</linkedContext> + </entityField> + <entityField> + <name>ORGNAME</name> + <title>Name</title> + <linkedContext>Organisation</linkedContext> + </entityField> + <entityParameter> + <name>Id_param</name> + <valueProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/id_param/valueProcess.js</valueProcess> + <mandatory v="true" /> + </entityParameter> + <entityParameter> + <name>Obj_param</name> + <expose v="true" /> + <mandatory v="true" /> + <description>Contains a name value object of all variables that can be used to scan for duplicates</description> + </entityParameter> + <entityActionGroup> + <name>filterActions</name> + <children> + <entityActionField> + <name>ignoreDuplicates</name> + <title>${IGNORE_DUPLICATE}</title> + <onActionProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js</onActionProcess> + <isObjectAction v="false" /> + <isSelectionAction v="true" /> + <iconId>VAADIN:CLOSE</iconId> + <titleProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js</titleProcess> + </entityActionField> + <entityActionField> + <name>mergeselectedintocurrent</name> + <title>Integrate selected into current contact</title> + <onActionProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js</onActionProcess> + <isSelectionAction v="true" /> + <iconId>NEON:IMPORT</iconId> + </entityActionField> + <entityActionField> + <name>mergecurrentintoselected</name> + <title>Integrate current into selected contact</title> + <onActionProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js</onActionProcess> + <isSelectionAction v="true" /> + <iconId>NEON:EXPORT</iconId> + </entityActionField> + </children> + </entityActionGroup> + <entityField> + <name>STATUS</name> + <title>Status</title> + <consumer>KeywordContactStates</consumer> + </entityField> + <entityField> + <name>CUSTOMERCODE</name> + <title>Customercode</title> + </entityField> + <entityConsumer> + <name>KeywordContactStates</name> + <dependency> + <name>dependency</name> + <entityName>KeywordEntry_entity</entityName> + <fieldName>SpecificContainerKeywords</fieldName> + </dependency> + <children> + <entityParameter> + <name>ContainerName_param</name> + <valueProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> + <entityField> + <name>TYPE</name> + <title>Type</title> + <consumer>KeywordOrganisationTypes</consumer> + </entityField> + <entityField> + <name>STANDARD_EMAIL_COMMUNICATION</name> + <title>E-Mail</title> + </entityField> + <entityField> + <name>STANDARD_PHONE_COMMUNICATION</name> + <title>Phone</title> + </entityField> + <entityField> + <name>STANDARD_ADDRESS</name> + <title>Address</title> + </entityField> + <entityConsumer> + <name>KeywordOrganisationTypes</name> + <dependency> + <name>dependency</name> + <entityName>KeywordEntry_entity</entityName> + <fieldName>SpecificContainerKeywords</fieldName> + </dependency> + <children> + <entityParameter> + <name>ContainerName_param</name> + <valueProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/keywordorganisationtypes/children/containername_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> + <entityField> + <name>targetContext</name> + <valueProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/targetcontext/valueProcess.js</valueProcess> + </entityField> + <entityField> + <name>DUPLICATE</name> + <title>Duplicate</title> + <contentType>BOOLEAN</contentType> + </entityField> + <entityField> + <name>PICTURE</name> + <title>Picture</title> + <contentType>IMAGE</contentType> + <displayValueProcess>%aditoprj%/entity/DuplicateOrganisation_entity/entityfields/picture/displayValueProcess.js</displayValueProcess> + </entityField> + </entityFields> + <recordContainers> + <dbRecordContainer> + <name>db</name> + <isReadOnly v="true" /> + <fromClauseProcess>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/fromClauseProcess.js</fromClauseProcess> + <conditionProcess>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/conditionProcess.js</conditionProcess> + <alias>Data_alias</alias> + <recordFieldMappings> + <dbRecordFieldMapping> + <name>CONTACTID.value</name> + <recordfield>CONTACT.CONTACTID</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>ORGNAME.value</name> + <recordfield>CONTACT.CONTACTID</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>ORGNAME.displayValue</name> + <recordfield>ORGANISATION.NAME</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>CUSTOMERCODE.value</name> + <recordfield>ORGANISATION.CUSTOMERCODE</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STATUS.value</name> + <recordfield>CONTACT.STATUS</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STATUS.displayValue</name> + <expression>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js</expression> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STANDARD_EMAIL_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STANDARD_PHONE_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js</expression> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STANDARD_ADDRESS.value</name> + <recordfield>ADDRESS.ADDRESS</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>TYPE.value</name> + <recordfield>ORGANISATION.KIND</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>TYPE.displayValue</name> + <expression>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/type.displayvalue/expression.js</expression> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>DUPLICATE.value</name> + <expression>%aditoprj%/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>PICTURE.value</name> + <recordfield>ORGANISATION.PICTURE</recordfield> + </dbRecordFieldMapping> + </recordFieldMappings> + <linkInformation> + <linkInformation> + <name>49ea0faa-d04e-4d9d-b489-5a7b815e9e89</name> + <tableName>ORGANISATION</tableName> + <primaryKey>ORGANISATIONID</primaryKey> + <isUIDTable v="false" /> + <readonly v="true" /> + </linkInformation> + <linkInformation> + <name>523f6891-14ee-49ba-952d-7d5981e14b0c</name> + <tableName>CONTACT</tableName> + <primaryKey>CONTACTID</primaryKey> + <isUIDTable v="true" /> + <readonly v="true" /> + </linkInformation> + <linkInformation> + <name>6b73b998-a194-4fb6-8659-42eac09e20cc</name> + <tableName>ADDRESS</tableName> + <primaryKey>ADDRESSID</primaryKey> + <isUIDTable v="false" /> + <readonly v="true" /> + </linkInformation> + </linkInformation> + </dbRecordContainer> + </recordContainers> +</entity> diff --git a/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..a070181a8e991188f1a7ff62ab87423364cdb5a6 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js @@ -0,0 +1,7 @@ +import("system.neon"); +import("system.vars"); +import("system.result"); +import("DuplicateScanner_lib"); + +DuplicateScannerUtils.updateIgnored("Organisation_entity", vars.get("$param.Id_param"), vars.get("$sys.selection"), parseInt(vars.get("$field.DUPLICATE"))); +neon.refreshAll(); // Update the rows, because UNRELATEDDUPLICATES wont trigger a refresh even with write entities diff --git a/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..4c5ccfffeb0e7b5efc940eba237fbdc99ab9b603 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js @@ -0,0 +1,9 @@ +import("system.translate"); +import("system.vars"); +import("system.result"); + +result.string( + parseInt(vars.get("$field.DUPLICATE")) ? + translate.text("${IGNORE_DUPLICATE}") : + translate.text("${UNIGNORE_DUPLICATE}") +); diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js similarity index 51% rename from entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js rename to entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js index 29dab7b72c8898044cfabd79426552733f9f7dc4..b23396c01c1214f15be0e2bae435e1e9da91c702 100644 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js +++ b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js @@ -1,20 +1,29 @@ +import("system.translate"); +import("system.question"); import("Employee_lib"); import("system.vars"); import("system.neon"); -import("DuplicateScanner_lib"); +import("DuplicateMerge_lib"); -let sourceContactId = vars.get("$param.DuplicateCurrentContactId_param"); +if(vars.get("$sys.selection").length == 1) +{ +let sourceContactId = vars.get("$param.Id_param"); 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. -let mergeSuccess = DuplicateScannerUtils.mergeOrganisation(sourceContactId, targetContactId); +let mergeSuccess = DuplicateMergeUtils.mergeOrganisation(sourceContactId, targetContactId); if(mergeSuccess) { let currentContactId = EmployeeUtils.getCurrentContactId(); if(currentContactId == null) currentContactId = ""; - DuplicateScannerUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Organisation"); + DuplicateMergeUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Organisation"); neon.openContext("Organisation", "OrganisationMain_view", [targetContactId], neon.OPERATINGSTATE_VIEW, null) -} \ No newline at end of file +} +} +else +{ + question.showMessage(translate.text("Please select only one element")); +} diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js similarity index 61% rename from entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js rename to entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js index 5ac58579f1326724bf4e6340a751ee86b12850b4..ac1a9260b226c00b472e5222a97c0c4f8286bfad 100644 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js +++ b/entity/DuplicateOrganisation_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js @@ -1,22 +1,31 @@ +import("system.translate"); +import("system.question"); import("Employee_lib"); import("system.vars"); import("system.neon"); -import("DuplicateScanner_lib"); +import("DuplicateMerge_lib"); -let targetContactId = vars.get("$param.DuplicateCurrentContactId_param"); +if(vars.get("$sys.selection").length == 1) +{ +let targetContactId = vars.get("$param.Id_param"); 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. -let mergeSuccess = DuplicateScannerUtils.mergeOrganisation(sourceContactId, targetContactId); +let mergeSuccess = DuplicateMergeUtils.mergeOrganisation(sourceContactId, targetContactId); if(mergeSuccess) { let currentContactId = EmployeeUtils.getCurrentContactId(); if(currentContactId == null) 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, //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 neon.refresh(); -} \ No newline at end of file +} +} +else +{ + question.showMessage(translate.text("Please select only one element")); +} diff --git a/entity/DuplicateOrganisation_entity/entityfields/id_param/valueProcess.js b/entity/DuplicateOrganisation_entity/entityfields/id_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..bffe18eb097c82b9fa662880ad23d5d1ee32f7ff --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/id_param/valueProcess.js @@ -0,0 +1,5 @@ +import("system.result"); +import("system.vars"); + +var jsonObj = JSON.parse(vars.get("$param.Obj_param")); +result.string(jsonObj["CONTACTID"]); diff --git a/entity/DuplicateOrganisation_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js b/entity/DuplicateOrganisation_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..43e8d27c6a6cdd11f092f8b404a97dc237577a38 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Keyword_lib"); +import("KeywordRegistry_basic"); + +result.string($KeywordRegistry.contactStatus()); diff --git a/entity/DuplicateOrganisation_entity/entityfields/keywordorganisationtypes/children/containername_param/valueProcess.js b/entity/DuplicateOrganisation_entity/entityfields/keywordorganisationtypes/children/containername_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..9f418ef99a76f73e9ce0e06b98eb299c5431d63c --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/keywordorganisationtypes/children/containername_param/valueProcess.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Keyword_lib"); +import("KeywordRegistry_basic"); + +result.string($KeywordRegistry.organisationType()); diff --git a/entity/DuplicateOrganisation_entity/entityfields/picture/displayValueProcess.js b/entity/DuplicateOrganisation_entity/entityfields/picture/displayValueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..aa69acba15ba5aa0635f9e0a4a391a04c5819c2f --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/picture/displayValueProcess.js @@ -0,0 +1,11 @@ +import("system.result"); +import("system.vars"); + +if (vars.get("$field.PICTURE")) +{ + result.string(vars.get("$field.PICTURE")); +} +else +{ + result.string("TEXT:" + vars.getString("$field.ORGNAME.displayValue").trim()); +} diff --git a/entity/DuplicateOrganisation_entity/entityfields/targetcontext/valueProcess.js b/entity/DuplicateOrganisation_entity/entityfields/targetcontext/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..84e4b61c2566393178456e74db7e36eb6abdaf80 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/entityfields/targetcontext/valueProcess.js @@ -0,0 +1,4 @@ +import("system.result"); +import("Context_lib"); + +result.string(ContextUtils.getContextName("Organisation")); diff --git a/entity/DuplicateOrganisation_entity/initFilterProcess.js b/entity/DuplicateOrganisation_entity/initFilterProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..92797e84dbb193011bf821bb4ecc828681a2d414 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/initFilterProcess.js @@ -0,0 +1,15 @@ +import("system.translate"); +import("system.result"); + +result.string(JSON.stringify({ + type: "group", + operator: "AND", + childs: [{ + type: "row", + name: "DUPLICATE", + operator: "EQUAL", + contenttype: "BOOLEAN", + key: "1", + value: translate.text("Yes") + }] +})); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/conditionProcess.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/conditionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..e53fabe5b3f46c2048bdb6500df6f37ca9300d61 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/conditionProcess.js @@ -0,0 +1,8 @@ +import("system.vars"); +import("Sql_lib"); +import("DuplicateScanner_lib"); +import("system.result"); + +var duplicateIds = DuplicateScannerUtils.getDuplicateIdsByEntityObj("Organisation_entity", JSON.parse(vars.get("$param.Obj_param"))); +duplicateIds.push("-"); // workaround empty arrays +result.string(newWhere("CONTACT.CONTACTID", duplicateIds, SqlBuilder.IN()).toString()); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/fromClauseProcess.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/fromClauseProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..5412501fe024b394c16299840fb75b9997368d1f --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/fromClauseProcess.js @@ -0,0 +1,14 @@ +import("Sql_lib"); +import("system.vars"); +import("system.result"); + +var unrelatedCondition = newWhere("UNRELATEDDUPLICATES.DUPLICATETYPE", "Organisation_entity") + .and("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", vars.get("$param.Id_param")) + .and("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID = CONTACT.CONTACTID"); + +result.string( + "ORGANISATION " + + "join CONTACT on (ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID) " + + "left join ADDRESS on (ADDRESS.ADDRESSID = CONTACT.ADDRESS_ID) " + + "left join UNRELATEDDUPLICATES on " + unrelatedCondition.toString() +); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..8f7063b2ac8e637e6618ccc1ff745653daf0b339 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js @@ -0,0 +1,7 @@ +import("system.result"); +import("Sql_lib"); + +var statement = SqlBuilder.caseStatement() + .when("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID is not null") + .thenString("0").elseString("1"); +result.string(statement); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..5827c59c416c332ba9281a60756f919fcb75c0a2 --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js @@ -0,0 +1,4 @@ +import("system.result"); +import("Communication_lib"); + +result.string(CommUtil.getStandardSubSqlMail()); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..0f438c2535b21180c5ad03637de90f577f2c736e --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js @@ -0,0 +1,4 @@ +import("system.result"); +import("Communication_lib"); + +result.string(CommUtil.getStandardSubSqlPhone()); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..b63b2d57742bc29e8f35acd8bae539ec0a560c9c --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js @@ -0,0 +1,6 @@ +import("system.result"); +import("Keyword_lib"); +import("KeywordRegistry_basic"); + +var sql = KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.contactStatus(), "CONTACT.STATUS"); +result.string(sql); diff --git a/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/type.displayvalue/expression.js b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/type.displayvalue/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..7ab8eb06526a4f6be6502246408fe7891c4dc46d --- /dev/null +++ b/entity/DuplicateOrganisation_entity/recordcontainers/db/recordfieldmappings/type.displayvalue/expression.js @@ -0,0 +1,6 @@ +import("system.result"); +import("Keyword_lib"); +import("KeywordRegistry_basic"); + +var sql = KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.organisationType(), "ORGANISATION.KIND"); +result.string(sql); diff --git a/entity/DuplicatePerson_entity/DuplicatePerson_entity.aod b/entity/DuplicatePerson_entity/DuplicatePerson_entity.aod new file mode 100644 index 0000000000000000000000000000000000000000..2d5dd73d048c99ae42a4b422275198df818f39d1 --- /dev/null +++ b/entity/DuplicatePerson_entity/DuplicatePerson_entity.aod @@ -0,0 +1,270 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.18" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.18"> + <name>DuplicatePerson_entity</name> + <title>Duplicate</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <grantCreate v="false" /> + <grantUpdate v="false" /> + <grantDelete v="false" /> + <initFilterProcess>%aditoprj%/entity/DuplicatePerson_entity/initFilterProcess.js</initFilterProcess> + <titlePlural>Duplicates</titlePlural> + <recordContainer>db</recordContainer> + <entityFields> + <entityProvider> + <name>#PROVIDER</name> + <targetContextField>parentContext</targetContextField> + <targetIdField>CONTACTID</targetIdField> + <dependencies> + <entityDependency> + <name>7f8210c4-644f-4c45-a571-e232582f8d34</name> + <entityName>Person_entity</entityName> + <fieldName>Duplicates</fieldName> + <isConsumer v="false" /> + </entityDependency> + <entityDependency> + <name>fd3253c7-f3f8-483f-8f49-6192f4e75979</name> + <entityName>Person_entity</entityName> + <fieldName>Duplicates</fieldName> + <isConsumer v="false" /> + </entityDependency> + </dependencies> + </entityProvider> + <entityProvider> + <name>#PROVIDER_AGGREGATES</name> + <useAggregates v="true" /> + </entityProvider> + <entityParameter> + <name>Obj_param</name> + <expose v="true" /> + <mandatory v="true" /> + <description>Contains a name value object of all variables that can be used to scan for duplicates</description> + </entityParameter> + <entityActionGroup> + <name>filterActions</name> + <children> + <entityActionField> + <name>ignoreDuplicates</name> + <title>${IGNORE_DUPLICATE}</title> + <onActionProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js</onActionProcess> + <isObjectAction v="false" /> + <isSelectionAction v="true" /> + <iconId>VAADIN:CLOSE</iconId> + <titleProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js</titleProcess> + </entityActionField> + <entityActionField> + <name>mergeselectedintocurrent</name> + <title>Integrate selected into current contact</title> + <onActionProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js</onActionProcess> + <isSelectionAction v="true" /> + <iconId>NEON:IMPORT</iconId> + </entityActionField> + <entityActionField> + <name>mergecurrentintoselected</name> + <title>Integrate current into selected contact</title> + <onActionProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js</onActionProcess> + <isSelectionAction v="true" /> + <iconId>NEON:EXPORT</iconId> + </entityActionField> + </children> + </entityActionGroup> + <entityField> + <name>CONTACTID</name> + <linkedContext>Person</linkedContext> + </entityField> + <entityParameter> + <name>Id_param</name> + <valueProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/id_param/valueProcess.js</valueProcess> + <mandatory v="true" /> + </entityParameter> + <entityField> + <name>FIRSTNAME</name> + <title>Firstname</title> + </entityField> + <entityField> + <name>LASTNAME</name> + <title>Lastname</title> + <linkedContext>Person</linkedContext> + </entityField> + <entityField> + <name>MIDDLENAME</name> + <title>Middlename</title> + </entityField> + <entityField> + <name>LETTERSALUTATION</name> + <title>Salutation</title> + </entityField> + <entityField> + <name>TITLE</name> + <title>Title</title> + </entityField> + <entityField> + <name>PICTURE</name> + <title>Picture</title> + <contentType>IMAGE</contentType> + <displayValueProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/picture/displayValueProcess.js</displayValueProcess> + </entityField> + <entityField> + <name>STANDARD_EMAIL_COMMUNICATION</name> + <title>E-Mail</title> + </entityField> + <entityField> + <name>STANDARD_PHONE_COMMUNICATION</name> + <title>Phone</title> + </entityField> + <entityField> + <name>ORGANISATION_ID</name> + <title>Company</title> + <consumer>Organisations</consumer> + <linkedContext>Organisation</linkedContext> + </entityField> + <entityConsumer> + <name>Organisations</name> + <dependency> + <name>dependency</name> + <entityName>Organisation_entity</entityName> + <fieldName>Organisations</fieldName> + </dependency> + </entityConsumer> + <entityField> + <name>STATUS</name> + <title>Status</title> + <consumer>KeywordContactStates</consumer> + </entityField> + <entityConsumer> + <name>KeywordContactStates</name> + <dependency> + <name>dependency</name> + <entityName>KeywordEntry_entity</entityName> + <fieldName>SpecificContainerKeywords</fieldName> + </dependency> + <children> + <entityParameter> + <name>ContainerName_param</name> + <valueProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> + <entityField> + <name>STANDARD_ADDRESS</name> + <title>Address</title> + </entityField> + <entityField> + <name>parentContext</name> + <valueProcess>%aditoprj%/entity/DuplicatePerson_entity/entityfields/parentcontext/valueProcess.js</valueProcess> + </entityField> + <entityField> + <name>DUPLICATE</name> + <title>Duplicate</title> + <contentType>BOOLEAN</contentType> + </entityField> + </entityFields> + <recordContainers> + <dbRecordContainer> + <name>db</name> + <isReadOnly v="true" /> + <fromClauseProcess>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/fromClauseProcess.js</fromClauseProcess> + <conditionProcess>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/conditionProcess.js</conditionProcess> + <alias>Data_alias</alias> + <recordFieldMappings> + <dbRecordFieldMapping> + <name>CONTACTID.value</name> + <recordfield>CONTACT.CONTACTID</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>MIDDLENAME.value</name> + <recordfield>PERSON.MIDDLENAME</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>LASTNAME.value</name> + <recordfield>CONTACT.CONTACTID</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>FIRSTNAME.value</name> + <recordfield>PERSON.FIRSTNAME</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>LETTERSALUTATION.value</name> + <recordfield>PERSON.SALUTATION</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>LASTNAME.displayValue</name> + <recordfield>PERSON.LASTNAME</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>TITLE.value</name> + <recordfield>PERSON.TITLE</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>PICTURE.value</name> + <recordfield>PERSON.PICTURE</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STANDARD_PHONE_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STANDARD_EMAIL_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>ORGANISATION_ID.value</name> + <expression>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STATUS.value</name> + <recordfield>CONTACT.STATUS</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STATUS.displayValue</name> + <expression>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js</expression> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>STANDARD_ADDRESS.value</name> + <recordfield>ADDRESS.ADDRESS</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>ORGANISATION_ID.displayValue</name> + <expression>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.displayvalue/expression.js</expression> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>DUPLICATE.value</name> + <expression>%aditoprj%/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> + </recordFieldMappings> + <linkInformation> + <linkInformation> + <name>93f86a28-8c69-491e-8f62-79f5840e23a5</name> + <tableName>PERSON</tableName> + <primaryKey>PERSONID</primaryKey> + <isUIDTable v="false" /> + <readonly v="true" /> + </linkInformation> + <linkInformation> + <name>c989f19d-d211-4f54-9ea5-ddf16e5a13a0</name> + <tableName>CONTACT</tableName> + <primaryKey>CONTACTID</primaryKey> + <isUIDTable v="true" /> + <readonly v="true" /> + </linkInformation> + <linkInformation> + <name>7ff69154-f1ff-4e33-8cd4-5a88e3b18269</name> + <tableName>ADDRESS</tableName> + <primaryKey>ADDRESSID</primaryKey> + <isUIDTable v="false" /> + <readonly v="true" /> + </linkInformation> + </linkInformation> + </dbRecordContainer> + </recordContainers> +</entity> diff --git a/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js b/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..69c03d7bc979275e8276e8bc9700787e0e513f13 --- /dev/null +++ b/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/onActionProcess.js @@ -0,0 +1,7 @@ +import("system.neon"); +import("system.vars"); +import("system.result"); +import("DuplicateScanner_lib"); + +DuplicateScannerUtils.updateIgnored("Person_entity", vars.get("$param.Id_param"), vars.get("$sys.selection"), parseInt(vars.get("$field.DUPLICATE"))); +neon.refreshAll(); // Update the rows, because UNRELATEDDUPLICATES wont trigger a refresh even with write entities diff --git a/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js b/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..4c5ccfffeb0e7b5efc940eba237fbdc99ab9b603 --- /dev/null +++ b/entity/DuplicatePerson_entity/entityfields/filteractions/children/ignoreduplicates/titleProcess.js @@ -0,0 +1,9 @@ +import("system.translate"); +import("system.vars"); +import("system.result"); + +result.string( + parseInt(vars.get("$field.DUPLICATE")) ? + translate.text("${IGNORE_DUPLICATE}") : + translate.text("${UNIGNORE_DUPLICATE}") +); diff --git a/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js b/entity/DuplicatePerson_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js similarity index 60% rename from entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js rename to entity/DuplicatePerson_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js index d5f864db525910dd0635ee2ab84279e3346599b6..8e96e7fcea38ebc093da5fceaaa9ad34a9a5c7a9 100644 --- a/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js +++ b/entity/DuplicatePerson_entity/entityfields/filteractions/children/mergecurrentintoselected/onActionProcess.js @@ -1,14 +1,18 @@ +import("system.translate"); +import("system.question"); import("system.tools"); import("Employee_lib"); import("system.vars"); import("system.neon"); -import("DuplicateScanner_lib"); +import("DuplicateMerge_lib"); -var sourceContactId = vars.get("$param.DuplicateCurrentContactId_param"); +if(vars.get("$sys.selection").length == 1) +{ +var sourceContactId = vars.get("$param.Id_param"); var 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. -var mergeSuccess = DuplicateScannerUtils.mergePerson(sourceContactId, targetContactId); +var mergeSuccess = DuplicateMergeUtils.mergePerson(sourceContactId, targetContactId); if(mergeSuccess) { @@ -21,7 +25,12 @@ if(mergeSuccess) var currentContactId = EmployeeUtils.getCurrentContactId(); if(currentContactId == null) currentContactId = ""; - DuplicateScannerUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Person"); + DuplicateMergeUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Person"); neon.openContext("Person", "PersonMain_view", [targetContactId], neon.OPERATINGSTATE_VIEW, null) -} \ No newline at end of file +} +} +else +{ + question.showMessage(translate.text("Please select only one element")); +} diff --git a/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js b/entity/DuplicatePerson_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js similarity index 70% rename from entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js rename to entity/DuplicatePerson_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js index 6a9f2ac39a7238d7dd5fbaec53e41c57eabec241..30d97b1c8023db93074e3cee7b3f30487e920aad 100644 --- a/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js +++ b/entity/DuplicatePerson_entity/entityfields/filteractions/children/mergeselectedintocurrent/onActionProcess.js @@ -1,3 +1,5 @@ +import("system.translate"); +import("system.question"); import("system.tools"); import("system.db"); import("Employee_lib"); @@ -5,14 +7,16 @@ import("KeywordRegistry_basic"); import("ActivityTask_lib"); import("system.vars"); import("system.neon"); -import("DuplicateScanner_lib"); +import("DuplicateMerge_lib"); -var targetContactId = vars.get("$param.DuplicateCurrentContactId_param"); +if(vars.get("$sys.selection").length == 1) +{ +var targetContactId = vars.get("$param.Id_param"); var sourceContactIdArray = vars.get("$sys.selection"); var sourceContactId = sourceContactIdArray[0]; //todo the actual merge ought to happen in a separate view where the contact infos can be merged manually by the user. -var mergeSuccess = DuplicateScannerUtils.mergePerson(sourceContactId, targetContactId); +var mergeSuccess = DuplicateMergeUtils.mergePerson(sourceContactId, targetContactId); if(mergeSuccess) { @@ -25,9 +29,14 @@ if(mergeSuccess) var currentContactId = EmployeeUtils.getCurrentContactId(); if(currentContactId == null) currentContactId = ""; - DuplicateScannerUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Person"); + DuplicateMergeUtils.createMergeSuccessActivity(sourceContactId, targetContactId, currentContactId, "Person"); //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 //and does not exist any more which results in an exception neon.refresh(); -} \ No newline at end of file +} +} +else +{ + question.showMessage(translate.text("Please select only one element")); +} diff --git a/entity/DuplicatePerson_entity/entityfields/id_param/valueProcess.js b/entity/DuplicatePerson_entity/entityfields/id_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..bffe18eb097c82b9fa662880ad23d5d1ee32f7ff --- /dev/null +++ b/entity/DuplicatePerson_entity/entityfields/id_param/valueProcess.js @@ -0,0 +1,5 @@ +import("system.result"); +import("system.vars"); + +var jsonObj = JSON.parse(vars.get("$param.Obj_param")); +result.string(jsonObj["CONTACTID"]); diff --git a/entity/DuplicatePerson_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js b/entity/DuplicatePerson_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..43e8d27c6a6cdd11f092f8b404a97dc237577a38 --- /dev/null +++ b/entity/DuplicatePerson_entity/entityfields/keywordcontactstates/children/containername_param/valueProcess.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Keyword_lib"); +import("KeywordRegistry_basic"); + +result.string($KeywordRegistry.contactStatus()); diff --git a/entity/DuplicatePerson_entity/entityfields/parentcontext/valueProcess.js b/entity/DuplicatePerson_entity/entityfields/parentcontext/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..f7baa76a6be6f585b2829869961b69b1ca4a5e15 --- /dev/null +++ b/entity/DuplicatePerson_entity/entityfields/parentcontext/valueProcess.js @@ -0,0 +1,4 @@ +import("system.result"); +import("Context_lib"); + +result.string(ContextUtils.getContextName("Person")); diff --git a/entity/DuplicatePerson_entity/entityfields/picture/displayValueProcess.js b/entity/DuplicatePerson_entity/entityfields/picture/displayValueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..e09fa2ae490ce0756966fd490af0150cc05e726f --- /dev/null +++ b/entity/DuplicatePerson_entity/entityfields/picture/displayValueProcess.js @@ -0,0 +1,11 @@ +import("system.result"); +import("system.vars"); + +if (vars.get("$field.PICTURE")) +{ + result.string(vars.get("$field.PICTURE")); +} +else +{ + result.string("TEXT:" + (vars.getString("$field.FIRSTNAME") + " " + vars.getString("$field.LASTNAME")).trim()); +} diff --git a/entity/DuplicatePerson_entity/initFilterProcess.js b/entity/DuplicatePerson_entity/initFilterProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..92797e84dbb193011bf821bb4ecc828681a2d414 --- /dev/null +++ b/entity/DuplicatePerson_entity/initFilterProcess.js @@ -0,0 +1,15 @@ +import("system.translate"); +import("system.result"); + +result.string(JSON.stringify({ + type: "group", + operator: "AND", + childs: [{ + type: "row", + name: "DUPLICATE", + operator: "EQUAL", + contenttype: "BOOLEAN", + key: "1", + value: translate.text("Yes") + }] +})); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/conditionProcess.js b/entity/DuplicatePerson_entity/recordcontainers/db/conditionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..658b1af578f9b7fb2d68728d73cef7393c3a7740 --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/conditionProcess.js @@ -0,0 +1,8 @@ +import("system.vars"); +import("Sql_lib"); +import("DuplicateScanner_lib"); +import("system.result"); + +var duplicateIds = DuplicateScannerUtils.getDuplicateIdsByEntityObj("Person_entity", JSON.parse(vars.get("$param.Obj_param"))); +duplicateIds.push("-"); // workaround empty arrays +result.string(newWhere("CONTACT.CONTACTID", duplicateIds, SqlBuilder.IN()).toString()); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/fromClauseProcess.js b/entity/DuplicatePerson_entity/recordcontainers/db/fromClauseProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..2205f0c5af7cbe0439abdf3017bae42b6e9be2cd --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/fromClauseProcess.js @@ -0,0 +1,14 @@ +import("Sql_lib"); +import("system.vars"); +import("system.result"); + +var unrelatedCondition = newWhere("UNRELATEDDUPLICATES.DUPLICATETYPE", "Person_entity") + .and("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", vars.get("$param.Id_param")) + .and("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID = CONTACT.CONTACTID"); + +result.string( + "PERSON " + + "join CONTACT on (PERSON.PERSONID = CONTACT.PERSON_ID) " + + "left join ADDRESS on (ADDRESS.ADDRESSID = CONTACT.ADDRESS_ID) " + + "left join UNRELATEDDUPLICATES on " + unrelatedCondition.toString() +); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..8f7063b2ac8e637e6618ccc1ff745653daf0b339 --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/duplicate.value/expression.js @@ -0,0 +1,7 @@ +import("system.result"); +import("Sql_lib"); + +var statement = SqlBuilder.caseStatement() + .when("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID is not null") + .thenString("0").elseString("1"); +result.string(statement); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.displayvalue/expression.js b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.displayvalue/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..64f71be666b2bbb3421d182e2fe8b25d989a3316 --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.displayvalue/expression.js @@ -0,0 +1,5 @@ +import("Sql_lib"); +import("system.result"); + +result.string(newSelect("ORGANISATION.NAME").from("ORGANISATION") + .where("ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID").toString()); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.value/expression.js b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..e37273248e57bcb47a83af87693aa9cca2ed5636 --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/organisation_id.value/expression.js @@ -0,0 +1,6 @@ +import("system.result"); +import("Sql_lib"); + +result.string(newSelect("c.CONTACTID").from("CONTACT as c") + .where("c.ORGANISATION_ID = CONTACT.ORGANISATION_ID") + .and("c.PERSON_ID is null").toString()); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..5827c59c416c332ba9281a60756f919fcb75c0a2 --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js @@ -0,0 +1,4 @@ +import("system.result"); +import("Communication_lib"); + +result.string(CommUtil.getStandardSubSqlMail()); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..0f438c2535b21180c5ad03637de90f577f2c736e --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js @@ -0,0 +1,4 @@ +import("system.result"); +import("Communication_lib"); + +result.string(CommUtil.getStandardSubSqlPhone()); diff --git a/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..50433a35e5f298cf550485a4d9ea33ac405a6ebd --- /dev/null +++ b/entity/DuplicatePerson_entity/recordcontainers/db/recordfieldmappings/status.displayvalue/expression.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Keyword_lib"); +import("KeywordRegistry_basic"); + +result.string(KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.contactStatus(), "CONTACT.STATUS")); diff --git a/entity/DuplicateScannerPrefilterConfig_entity/recordcontainers/dbrecordcontainer/conditionProcess.js b/entity/DuplicateScannerPrefilterConfig_entity/recordcontainers/dbrecordcontainer/conditionProcess.js deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/entity/DuplicateScannerResultFieldConfig_entity/DuplicateScannerResultFieldConfig_entity.aod b/entity/DuplicateScannerResultFieldConfig_entity/DuplicateScannerResultFieldConfig_entity.aod deleted file mode 100644 index b336a59f809d6d5834741fc16f8a6034815597b2..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/DuplicateScannerResultFieldConfig_entity.aod +++ /dev/null @@ -1,104 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.18" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.18"> - <name>DuplicateScannerResultFieldConfig_entity</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <recordContainer>recordContainer</recordContainer> - <entityFields> - <entityProvider> - <name>#PROVIDER</name> - </entityProvider> - <entityField> - <name>UID</name> - </entityField> - <entityField> - <name>ENTITY_FIELD_NAME</name> - <title>Entity field name</title> - </entityField> - <entityField> - <name>DATE_EDIT</name> - <valueProcess>%aditoprj%/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_edit/valueProcess.js</valueProcess> - </entityField> - <entityField> - <name>DATE_NEW</name> - <valueProcess>%aditoprj%/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_new/valueProcess.js</valueProcess> - </entityField> - <entityField> - <name>USER_NEW</name> - <valueProcess>%aditoprj%/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_new/valueProcess.js</valueProcess> - </entityField> - <entityField> - <name>USER_EDIT</name> - <valueProcess>%aditoprj%/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_edit/valueProcess.js</valueProcess> - </entityField> - <entityParameter> - <name>DuplicateScannerId_param</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - <entityProvider> - <name>ScannerResultFieldConfigProvider</name> - <dependencies> - <entityDependency> - <name>119b48a5-ce78-4169-bd31-76e524cece99</name> - <entityName>DuplicateScanner_entity</entityName> - <fieldName>ScannerResultFieldsConfig_Consumer</fieldName> - <isConsumer v="false" /> - </entityDependency> - </dependencies> - </entityProvider> - <entityField> - <name>DUPLICATESCANNER_ID</name> - <valueProcess>%aditoprj%/entity/DuplicateScannerResultFieldConfig_entity/entityfields/duplicatescanner_id/valueProcess.js</valueProcess> - </entityField> - <entityProvider> - <name>#PROVIDER_AGGREGATES</name> - <useAggregates v="true" /> - </entityProvider> - </entityFields> - <recordContainers> - <dbRecordContainer> - <name>recordContainer</name> - <conditionProcess>%aditoprj%/entity/DuplicateScannerResultFieldConfig_entity/recordcontainers/recordcontainer/conditionProcess.js</conditionProcess> - <alias>Data_alias</alias> - <recordFieldMappings> - <dbRecordFieldMapping> - <name>DATE_EDIT.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.DATE_EDIT</recordfield> - </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>DATE_NEW.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.DATE_NEW</recordfield> - </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>ENTITY_FIELD_NAME.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.ENTITY_FIELD_NAME</recordfield> - </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>UID.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.ID</recordfield> - </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>USER_EDIT.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.USER_EDIT</recordfield> - </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>USER_NEW.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.USER_NEW</recordfield> - </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>DUPLICATESCANNER_ID.value</name> - <recordfield>DUPLICATESCANNERRESULTFIELDCONFIG.DUPLICATESCANNER_ID</recordfield> - </dbRecordFieldMapping> - </recordFieldMappings> - <linkInformation> - <linkInformation> - <name>f7126f94-9e4c-46a0-8dc9-9e52fda1c7c0</name> - <tableName>DUPLICATESCANNERRESULTFIELDCONFIG</tableName> - <primaryKey>ID</primaryKey> - <isUIDTable v="false" /> - <readonly v="false" /> - </linkInformation> - </linkInformation> - </dbRecordContainer> - </recordContainers> -</entity> diff --git a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_edit/valueProcess.js b/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_edit/valueProcess.js deleted file mode 100644 index 5e6ef059738e0c724a468685333a5e257ac228ce..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_edit/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -import("system.util"); -import("system.result"); -import("system.neon"); -import("system.vars"); - -if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT) - result.string(vars.get("$sys.date")); \ No newline at end of file diff --git a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_new/valueProcess.js b/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_new/valueProcess.js deleted file mode 100644 index a72892783bf2bd04fe353c47f1be0cb570bbb323..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/date_new/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -import("system.util"); -import("system.result"); -import("system.neon"); -import("system.vars"); - -if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW) - result.string(vars.get("$sys.date")); \ No newline at end of file diff --git a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/duplicatescanner_id/valueProcess.js b/entity/DuplicateScannerResultFieldConfig_entity/entityfields/duplicatescanner_id/valueProcess.js deleted file mode 100644 index b8c682ad670116ff553d3774001f92eeec9478ce..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/duplicatescanner_id/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -import("system.result"); -import("system.vars"); - -if(vars.get("$this.value") == null || vars.get("$this.value") == "") -{ - result.string(vars.get("$param.DuplicateScannerId_param")); -} \ No newline at end of file diff --git a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_edit/valueProcess.js b/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_edit/valueProcess.js deleted file mode 100644 index 6af880ae3e0e2b89b4eee8327ed49f1eefe458af..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_edit/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -import("system.util"); -import("system.result"); -import("system.neon"); -import("system.vars"); - -if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT) - result.string(vars.get("$sys.user")); \ No newline at end of file diff --git a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_new/valueProcess.js b/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_new/valueProcess.js deleted file mode 100644 index e518bc75a9494e53a83613dedd943106e74fc00a..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/entityfields/user_new/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -import("system.util"); -import("system.result"); -import("system.neon"); -import("system.vars"); - -if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW) - result.string(vars.get("$sys.user")); \ No newline at end of file diff --git a/entity/DuplicateScannerResultFieldConfig_entity/recordcontainers/recordcontainer/conditionProcess.js b/entity/DuplicateScannerResultFieldConfig_entity/recordcontainers/recordcontainer/conditionProcess.js deleted file mode 100644 index b9bff609c91222abb6c3b0292492600907585487..0000000000000000000000000000000000000000 --- a/entity/DuplicateScannerResultFieldConfig_entity/recordcontainers/recordcontainer/conditionProcess.js +++ /dev/null @@ -1,5 +0,0 @@ -import("system.vars"); -import("system.result"); - -if (vars.get("$param.DuplicateScannerId_param")) - result.string(newWhere("DUPLICATESCANNERRESULTFIELDCONFIG.DUPLICATESCANNER_ID", "$param.DuplicateScannerId_param").toString()); \ No newline at end of file diff --git a/entity/DuplicateScanner_entity/DuplicateScanner_entity.aod b/entity/DuplicateScanner_entity/DuplicateScanner_entity.aod index 34fb0577dfe0c9ee42cc2f58458267359bd8f094..220ca8da86b7ff352ba94fa259602a9f004e9585 100644 --- a/entity/DuplicateScanner_entity/DuplicateScanner_entity.aod +++ b/entity/DuplicateScanner_entity/DuplicateScanner_entity.aod @@ -33,16 +33,6 @@ <contentType>BOOLEAN</contentType> <valueProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/external_service_usage_allowed/valueProcess.js</valueProcess> </entityField> - <entityActionGroup> - <name>TestActionGroup</name> - <children> - <entityActionField> - <name>TestDuplicateScanner</name> - <title>Test DuplicateSearch</title> - <onActionProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/testactiongroup/children/testduplicatescanner/onActionProcess.js</onActionProcess> - </entityActionField> - </children> - </entityActionGroup> <entityField> <name>USER_NEW</name> <valueProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/user_new/valueProcess.js</valueProcess> @@ -71,40 +61,36 @@ <state>AUTO</state> <stateProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/scan_pattern/stateProcess.js</stateProcess> </entityField> - <entityConsumer> - <name>ScannerResultFieldsConfig_Consumer</name> - <dependency> - <name>dependency</name> - <entityName>DuplicateScannerResultFieldConfig_entity</entityName> - <fieldName>ScannerResultFieldConfigProvider</fieldName> - </dependency> - <children> - <entityParameter> - <name>DuplicateScannerId_param</name> - <valueProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/scannerresultfieldsconfig_consumer/children/duplicatescannerid_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> + <entityProvider> + <name>#PROVIDER_AGGREGATES</name> + <useAggregates v="true" /> + </entityProvider> <entityActionGroup> - <name>RunActionGroup</name> - <documentation>%aditoprj%/entity/DuplicateScanner_entity/entityfields/runactiongroup/documentation.adoc</documentation> + <name>FilterActions</name> <children> <entityActionField> - <name>RebuildPersonDuplicatesCache</name> - <title>Rebuild Person duplicates cache</title> - <onActionProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildpersonduplicatescache/onActionProcess.js</onActionProcess> + <name>rebuild</name> + <title>Rebuild selected entries</title> + <onActionProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/filteractions/children/rebuild/onActionProcess.js</onActionProcess> + <isObjectAction v="false" /> + <isSelectionAction v="true" /> + <iconId>VAADIN:CALC</iconId> + <tooltip>Rebuild all duplicates from the selected scanners</tooltip> </entityActionField> <entityActionField> - <name>RebuildOrganisationDuplicatesCache</name> - <title>Rebuild Organisation duplicates cache</title> - <onActionProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildorganisationduplicatescache/onActionProcess.js</onActionProcess> + <name>viewDuplicates</name> + <title>View duplicates</title> + <onActionProcess>%aditoprj%/entity/DuplicateScanner_entity/entityfields/filteractions/children/viewduplicates/onActionProcess.js</onActionProcess> + <isSelectionAction v="true" /> + <iconId>VAADIN:TABLE</iconId> + <tooltip>View all duplicates from this scanner</tooltip> </entityActionField> </children> </entityActionGroup> - <entityProvider> - <name>#PROVIDER_AGGREGATES</name> - <useAggregates v="true" /> - </entityProvider> + <entityField> + <name>DUPLICATECOUNT</name> + <title>Count</title> + </entityField> </entityFields> <recordContainers> <dbRecordContainer> @@ -154,6 +140,11 @@ <name>SCAN_PATTERN.value</name> <recordfield>DUPLICATESCANNER.SCAN_PATTERN</recordfield> </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>DUPLICATECOUNT.value</name> + <expression>%aditoprj%/entity/DuplicateScanner_entity/recordcontainers/dbrecordcontainer/recordfieldmappings/duplicatecount.value/expression.js</expression> + <isFilterable v="true" /> + </dbRecordFieldMapping> </recordFieldMappings> <linkInformation> <linkInformation> diff --git a/entity/DuplicateScanner_entity/entityfields/filteractions/children/rebuild/onActionProcess.js b/entity/DuplicateScanner_entity/entityfields/filteractions/children/rebuild/onActionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..fc6ae65d2d318b7ba951ae069442ed54db022857 --- /dev/null +++ b/entity/DuplicateScanner_entity/entityfields/filteractions/children/rebuild/onActionProcess.js @@ -0,0 +1,34 @@ +import("system.process"); +import("system.logging"); +import("system.entities"); +import("system.vars"); +import("DuplicateScanner_lib"); + +var selectedIds = vars.get("$sys.selection"); +var loadConfig = entities.createConfigForLoadingRows() + .entity("DuplicateScanner_entity") + .uids(selectedIds) + .fields(["FILTER_NAME", "ENTITY_TO_SCAN_NAME", "ID_FIELD_NAME", "SCAN_PATTERN"]); +var selectedEntries = entities.getRows(loadConfig); +logging.log("Rebuilding " + selectedEntries.length + " entries"); + +selectedEntries.forEach(function(currEntry) +{ + var filterName = currEntry["FILTER_NAME"]; + var targetEntity = currEntry["ENTITY_TO_SCAN_NAME"]; + var targetIdField = currEntry["ID_FIELD_NAME"]; + var filter = JSON.parse(currEntry["SCAN_PATTERN"]); + + var startConfig = process.createStartAsyncConfig() + .setName("rebuildDuplicates_serverProcess") + .setShowErrorDialog(true) + .setLocalVariables({ + filterName: filterName, + targetEntity: targetEntity, + targetIdField: targetIdField, + filter: JSON.stringify(filter.filter) + }) + .setUser(vars.get("$sys.user")) + .setTimerType(process.TIMERTYPE_SERVER); + process.startAsync(startConfig); +}); diff --git a/entity/DuplicateScanner_entity/entityfields/filteractions/children/viewduplicates/onActionProcess.js b/entity/DuplicateScanner_entity/entityfields/filteractions/children/viewduplicates/onActionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..064e1f6c17fd9f84dc46723d329baf5a181ddc6d --- /dev/null +++ b/entity/DuplicateScanner_entity/entityfields/filteractions/children/viewduplicates/onActionProcess.js @@ -0,0 +1,35 @@ +import("system.translate"); +import("system.question"); +import("system.vars"); +import("system.neon"); +import("system.entities"); +import("Context_lib"); + +if(vars.get("$sys.selection").length == 1) +{ + var config = entities.createConfigForLoadingRows() + .entity("DuplicateScanner_entity") + .uid(vars.get("$sys.selection")[0]) + .fields(["ENTITY_TO_SCAN_NAME"]); + var scanner = entities.getRow(config); + var contextId = ContextUtils.getContextName(ContextUtils.getContextId(scanner["ENTITY_TO_SCAN_NAME"])); + + neon.openContext(contextId, null, null, neon.OPERATINGSTATE_SEARCH, { + FilterPreSet_param: JSON.stringify({ + type: "group", + operator: "AND", + childs: [{ + type: "row", + name: "#EXTENSION.Duplicates_filter.Duplicates_filter#NUMBER", + operator: "GREATER", + value: "All Duplicates", + key: "0", + contenttype: "NUMBER" + }] + }) + }); +} +else +{ + question.showMessage(translate.text("Please select only one element")); +} diff --git a/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildorganisationduplicatescache/onActionProcess.js b/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildorganisationduplicatescache/onActionProcess.js deleted file mode 100644 index e391b4c1938da96eddca596f8fb2211dd3d681ab..0000000000000000000000000000000000000000 --- a/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildorganisationduplicatescache/onActionProcess.js +++ /dev/null @@ -1,18 +0,0 @@ -import("system.logging"); -import("system.logging"); -import("DuplicateScanner_lib"); - -var filterName = "OrganisationDuplicates"; -var targetEntity = "Organisation_entity"; -var recordBlockSize = DuplicateScannerUtils.getBlockSize(); - -logging.log(filterName + ": Delete duplicates -> "); -DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity); - -logging.log(filterName + ": Recalculate duplicates -> "); -DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, recordBlockSize, null); - -logging.log(filterName + ": Refresh unrelated duplicates -> "); -DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - -logging.log(filterName + ": Done rebuilding "); \ No newline at end of file diff --git a/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildpersonduplicatescache/onActionProcess.js b/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildpersonduplicatescache/onActionProcess.js deleted file mode 100644 index cad8736382338846edff77ef164f85488513731d..0000000000000000000000000000000000000000 --- a/entity/DuplicateScanner_entity/entityfields/runactiongroup/children/rebuildpersonduplicatescache/onActionProcess.js +++ /dev/null @@ -1,18 +0,0 @@ -import("system.project"); -import("system.logging"); -import("DuplicateScanner_lib"); - -var filterName = "PersonDuplicates"; -var targetEntity = "Person_entity"; -var recordBlockSize = DuplicateScannerUtils.getBlockSize(); - -logging.log(filterName + ": Delete duplicates -> "); -DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity); - -logging.log(filterName + ": Recalculate duplicates -> "); -DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, recordBlockSize, null); - -logging.log(filterName + ": Refresh unrelated duplicates -> "); -DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - -logging.log(filterName + ": Done rebuilding "); \ No newline at end of file diff --git a/entity/DuplicateScanner_entity/entityfields/runactiongroup/documentation.adoc b/entity/DuplicateScanner_entity/entityfields/runactiongroup/documentation.adoc deleted file mode 100644 index f998dfcc78c6497f121cd16b294c8d01e6e1c1c7..0000000000000000000000000000000000000000 --- a/entity/DuplicateScanner_entity/entityfields/runactiongroup/documentation.adoc +++ /dev/null @@ -1 +0,0 @@ -Since duplicate caching is disabled there is no need to show this action group within views at the moment. \ No newline at end of file diff --git a/entity/DuplicateScanner_entity/entityfields/scannerresultfieldsconfig_consumer/children/duplicatescannerid_param/valueProcess.js b/entity/DuplicateScanner_entity/entityfields/scannerresultfieldsconfig_consumer/children/duplicatescannerid_param/valueProcess.js deleted file mode 100644 index 3c559d42002c59103607e03f79eb792d99d74d31..0000000000000000000000000000000000000000 --- a/entity/DuplicateScanner_entity/entityfields/scannerresultfieldsconfig_consumer/children/duplicatescannerid_param/valueProcess.js +++ /dev/null @@ -1,3 +0,0 @@ -import("system.vars"); -import("system.result"); -result.string(vars.get("$field.UID")); \ No newline at end of file diff --git a/entity/DuplicateScanner_entity/entityfields/testactiongroup/children/testduplicatescanner/onActionProcess.js b/entity/DuplicateScanner_entity/entityfields/testactiongroup/children/testduplicatescanner/onActionProcess.js deleted file mode 100644 index af7cd56a2472c2305bf914b22fc59c89a7ba03a8..0000000000000000000000000000000000000000 --- a/entity/DuplicateScanner_entity/entityfields/testactiongroup/children/testduplicatescanner/onActionProcess.js +++ /dev/null @@ -1,432 +0,0 @@ -import("system.process"); -import("KeywordRegistry_basic"); -import("system.db"); -import("ActivityTask_lib"); -import("Employee_lib"); -import("system.util"); -import("system.notification"); -import("system.notificationtypes"); -import("system.entities"); -import("system.project"); -import("system.indexsearch"); -import("system.question"); -import("system.logging"); -import("DuplicateScanner_lib"); -import("JditoFilter_lib"); - -//let testFields = []; -//let filters = DuplicateScannerUtils.loadFilters("PersonDuplicates", "Person_entity") -//logging.log("filters -> " + filters); -// -//for (let i = 0; i < filters.length; i++) -//{ -// logging.log("filters[i] -> " + filters[i]); -// let filter = JSON.parse(filters[i][0]).filter; -// let fields = JditoFilterUtils.getFilterFields(filter.childs); -// testFields = testFields.concat(fields); -//} -//logging.log("testFields -> " + testFields); - - -//##############################Test Duplicate Scan###################################################### - -//var filterName = "PersonDuplicates"; -//var targetEntity = "Person_entity"; -//var resultFieldsIdFieldName = "CONTACTID"; -// -//var tmpFieldsInFilterRay = ["CONTACTID", "FIRSTNAME", "LASTNAME", "GENDER"]; -//var queryPersonContactIds = "select CONTACTID, FIRSTNAME, LASTNAME, GENDER from CONTACT" -// + " join PERSON on PERSONID = PERSON_ID"; -// -// -//var filterFieldValueRays = [["CONTACTID", "29271db0-4253-46c9-b7c2-5e25376b9d19"], ["FIRSTNAME", "Narkus"], ["LASTNAME", "Bltinger"], ["GENDER", "m"]]; -// -////DuplicateScannerUtils.ScanForDuplicatesIndex = function(pFilterName, pTargetEntity, pFilterFieldValueRays, -////pTargetEntityResultFields, pRecordIdFieldToIgnore, pRecordIdValueToIgnore) -// -///* -// * -// */ -// -// -// -//let duplicateFieldsConfig = DuplicateScannerUtils.LoadDuplicateIndexFieldsConfiguration(filterName, targetEntity); -// -//let querySelectFields = ""; -//for (let i = 0; i < duplicateFieldsConfig.length; i++) -//{ -// querySelectFields += duplicateFieldsConfig[i][0]; -// -// if(i < duplicateFieldsConfig.length) -// querySelectFields += ", "; -//} -// -//let queryPersonFieldData = "select " + querySelectFields + " from CONTACT" -// + " join PERSON on PERSONID = PERSON_ID"; -// -//DuplicateScannerUtils.GetEntityFieldNameValueMap(duplicateFieldsConfig); -// -//DuplicateScannerUtils.ScanForDuplicatesIndex(filterName, targetEntity, -//filterFieldValueRays, [], resultFieldsIdFieldName, "29271db0-4253-46c9-b7c2-5e25376b9d19"); - - -//##############################ANs Beispiel###################################################### - - -//logging.log("TEST INDEX API with Entities"); -//logging.log(indexsearch.lookupIndexField("Person_entity", "FIRSTNAME")); -//logging.log(indexsearch.lookupIndexField("Person_entity", "FIRSTNAME.value")); -//logging.log(indexsearch.lookupIndexField("Person_entity", "PersAddresses.CITY")); -//logging.log(indexsearch.lookupIndexField("Person_entity", "PersAddresses.CITY.value")); -//logging.log(indexsearch.lookupIndexField(null, "Person_entity.FIRSTNAME.value")); -//logging.log(indexsearch.lookupIndexField(null, "Person_entity.PersAddresses.CITY.value")); -//var json = '{"entity":"Person_entity","filter":{"type":"group","operator":"AND","childs":[{"type":"row","name":"FIRSTNAME","operator":"STARTSWITH","value":"asd","key":"","contenttype":"TEXT"},{"type":"group","operator":"OR","childs":[{"type":"row","name":"LASTNAME","operator":"STARTSWITH","value":"L","key":"","contenttype":"TEXT"}]}]}}'; -//logging.log(indexsearch.buildQueryFromSearchCondition(json)); -// -//var t1 = indexsearch.createTerm("lisa").setEntityField("Person_entity.FIRSTNAME"); -//var t2 = indexsearch.createTerm("sommer").setEntityField("Person_entity.LASTNAME"); -//var t3 = indexsearch.createWildcardTerm("L").setEntityField("Person_entity.PersAddresses.CITY"); -// -//var patternConf = indexsearch.createPatternConfig().or(t1).or(t2).or(t3); -//var pattern = indexsearch.buildPatternString(patternConf); -//logging.log(pattern); -// -//var query = indexsearch.createIndexQuery() -//.setPattern(pattern) -//.setEntities("Person_entity") -////.addResultIndexFields(indexsearch.FIELD_ID) -//.addResultFields("Person_entity.FIRSTNAME") -//.addSearchFields("Person_entity.FIRSTNAME", "Person_entity.LASTNAME"); -// -//var res = indexsearch.searchIndex(query); -//logging.log("" + res); - - - -//######################################Demosuche nach Datensatz############################################## - - - - -// -//let indexQuery = indexsearch.createIndexQuery() -// .setPattern("(+(-contactid_value:(29271db0-4253-46c9-b7c2-5e25376b9d19)) +gender_value:m*)") -// .setEntities(["Person_entity"]) -// .addResultFields("Person_entity.FIRSTNAME") -// .setRows(50); -// -// -//let filterTerm1 = indexsearch.createTerm("Barkus") -// .setIndexField("firstname_value") -// .setFuzzySearchFactor(0); -////let filterTerm2 = indexsearch.createTerm("Altinger") -//// .setIndexField("lastname_value") -//// .setFuzzySearchFactor(0); -// -//let filterPatternConfig = indexsearch.createPatternConfig().and(filterTerm1); -// -//let filterPatternString = indexsearch.buildPatternString(filterPatternConfig); -//logging.log("Hauptsuche filterPatternString -> " + filterPatternString); -//indexQuery = indexQuery.addFilter(filterPatternString); -// -//let searchResult = indexsearch.searchIndex(indexQuery); -//logging.log("searchResult -> " + searchResult); -// -//logging.log("searchResults hits length -> " + searchResult[indexsearch.HITS].length); -// -//for (let i = 0; i < searchResult[indexsearch.HITS].length; i++) -//{ -// logging.log("Treffer Nr -> " + i); -// //searchResults hits 0 -> {#ADITO_SEARCH_ID=1868bd3a-05af-4b7f-a633-e3aec50ac45c, _index_group_=Person, #ADITO_SEARCH_TYPE=Person, firstname_value=Peter, _local_id_=1868bd3a-05af-4b7f-a633-e3aec50ac45c} -// let localId = searchResult[indexsearch.HITS][i]["_local_id_"]; -// let firstname = searchResult[indexsearch.HITS][i]["firstname_value"]; -// let indexGroup = searchResult[indexsearch.HITS][i]["_index_group_"]; -// logging.log("localId -> " + localId); -// logging.log("firstname -> " + firstname); -// logging.log("indexGroup -> " + indexGroup); -//} - - - - - -//#################################################################################### - - -// -//var filterName = "PersonDuplicates"; -//var targetEntity = "Person_entity"; -//var resultFieldsIdFieldName = "CONTACTID"; -//var queryPersonContactIds = "select CONTACTID, FIRSTNAME, LASTNAME, GENDER from CONTACT" -// + " join PERSON on PERSONID = PERSON_ID"; -//var tmpFieldsInFilterRay = ["CONTACTID", "FIRSTNAME", "LASTNAME", "GENDER"]; -// -//logging.log("Löschen von PERSON Dubletten -> "); -//DuplicateScannerUtils.deleteClustersByTargetEntity("Person_entity"); -// -//logging.log("Neu berechnen von PERSON Dubletten -> "); -//DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, queryPersonContactIds, -//tmpFieldsInFilterRay, resultFieldsIdFieldName); -// -//DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - -//############################################################################## - -//filterName = "OrganisationDuplicates"; -//targetEntity = "Organisation_entity"; -//resultFieldsIdFieldName = "CONTACTID"; -//queryPersonContactIds = "select CONTACTID, ORGANISATION.NAME from ORGANISATION" -// + " join CONTACT on CONTACT.CONTACTID = ORGANISATION.ORGANISATIONID" -// + " where CONTACTID != '0'"; -//tmpFieldsInFilterRay = ["CONTACTID", "NAME"]; -// -// -//logging.log("Löschen von ORGANISATION Dubletten -> "); -//DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity) -// -//logging.log("Neu berechnen von ORGANISATION Dubletten -> "); -//DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, queryPersonContactIds, -//tmpFieldsInFilterRay, resultFieldsIdFieldName); -// -//DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - - -//####################################Rebuild person duplicates########################################## - -//var filterName = "PersonDuplicates"; -//var targetEntity = "Person_entity"; -// -//let duplicateFieldsConfig = DuplicateScannerUtils.LoadIndexFieldsConfiguration(filterName, targetEntity); -//let resultFields = DuplicateScannerUtils.getResultFields(filterName, targetEntity); -// -//logging.log("duplicateFieldsConfig -> " + duplicateFieldsConfig); -//logging.log("resultFields -> " + resultFields); -// -//let querySelectFields = DuplicateScannerUtils.BuildSqlSelectFieldsFromFieldConfig(duplicateFieldsConfig); -//logging.log("querySelectFields -> " + querySelectFields); -// -//let queryPersonFieldData = "select " + querySelectFields + " from CONTACT" -// + " join PERSON on PERSONID = PERSON_ID" -// + " left join ADDRESS on ADDRESS.CONTACT_ID = CONTACT.CONTACTID"; -// -//logging.log("Löschen von PERSON Dubletten -> "); -//DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity); -// -//let formatToJsonAndCallWsCallback = function(pPossibleDuplicatesRay) -//{ -// let indexResultFields = DuplicateScannerUtils.translateEntityToIndexFields(targetEntity, resultFields) -// -// //Run thru every duplicate result an read out the resultfields -// for (let i = 0; i < pPossibleDuplicatesRay.length; i++) -// { -// for (let b = 0; b < resultFields.length; b++) -// { -// let entityFieldName = resultFields[b]; -// let indexFieldName = indexResultFields[entityFieldName]; -// //logging.log("Entity Field -> "+ pPossibleDuplicatesRay[i][indexFieldName]); -// //format values -// } -// } -// //call webservice -// //reformat results to same structure as before -// return pPossibleDuplicatesRay; -//}; -// -//logging.log("Neu berechnen von PERSON Dubletten -> "); -//DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, queryPersonFieldData, -//duplicateFieldsConfig, resultFields, formatToJsonAndCallWsCallback); -// -//DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - - - - - -//##################################single scanForDuplicates####################################################################### - - -//var filterName = "PersonDuplicates"; -//var targetEntity = "Person_entity"; -// -////Values to check, the same fields as configured -//let valuesToCheck = {}; -//valuesToCheck["CONTACTID"] = "c7ddf982-0e58-4152-b82b-8f5673b0b729"; -//valuesToCheck["FIRSTNAME"] = "Tim"; -//valuesToCheck["GENDER"] = "m "; -// -////The result values can be accessed as seen above in "formatToJsonAndCallWsCallback" -//let pPossibleDuplicatesRay = DuplicateScannerUtils.scanForDuplicates(filterName, targetEntity, valuesToCheck, null); -// -//logging.log(" pPossibleDuplicatesRay-> " + pPossibleDuplicatesRay); -// -//for (let i = 0; i < pPossibleDuplicatesRay.length; i++) -//{ -// logging.log("pPossibleDuplicatesRay[i] -> " + pPossibleDuplicatesRay[i]); -//} -// - - -//################################ entity structure auslesen ############################################## - - -//var model = project.getEntityStructure("Person_entity"); -//logging.log("Name: " + model.name); -//logging.log("Title: " + model.title); -//logging.log("Description: " + model.description); -//logging.log("UsePermissions: " + model.usePermissions); -// -//for (fieldname in model.fields) -//{ -// field = model.fields[fieldname]; -// if(field.fieldType == project.ENTITYFIELDTYPE_FIELD) -// { -// logging.log(" Name: " + field.name); -// logging.log(" Type: " + field.fieldType); -// logging.log(" Title: " + field.title); -// logging.log(" Description: " + field.description); -// logging.log(" UsePermissions: " + field.usePermissions); -// logging.log("###################### -> "); -// } -// if(field.fieldType == project.ENTITYFIELDTYPE_CONSUMER) -// { -// let consumerEntity = field.entityName; -// -// if(consumerEntity == null || consumerEntity == "") -// continue; -// -// let consumerEntityFields = project.getEntityStructure(consumerEntity); -// for (consumerEntityFieldname in consumerEntityFields.fields) -// { -// consumerField = consumerEntityFields.fields[consumerEntityFieldname]; -// if(consumerField.fieldType == project.ENTITYFIELDTYPE_FIELD) -// { -// logging.log(" Name: " + consumerField.name); -// logging.log(" Type: " + consumerField.fieldType); -// logging.log(" Title: " + consumerField.title); -// logging.log(" Description: " + consumerField.description); -// logging.log(" UsePermissions: " + consumerField.usePermissions); -// logging.log("###################### -> "); -// } -// } -// } -//} - -//############################################################################## - -//var model = project.getEntityStructure("Person_entity"); -//let duplicateFieldsConfig = DuplicateScannerUtils.LoadIndexFieldsConfiguration(filterName, targetEntity); -// -//let combinedData = [] -//let entityFieldsToLoad = []; -//for (field in duplicateFieldsConfig) -//{ -// let entityFieldName = duplicateFieldsConfig[field][0]; -// let isIdField = duplicateFieldsConfig[field][1]; -// let useForIndexSearch = duplicateFieldsConfig[field][2]; -// let entityFieldData = model[entiyFieldName]; -// combinedData.push(entityFieldName, isIdField, useForIndexSearch, entityFieldData); -//} -// -//var filterName = "PersonDuplicates"; -//var targetEntity = "Person_entity"; -//DuplicateScannerUtils.getEntityRecords(targetEntity, entityFieldsToLoad, 0, 50); - -//Beispiel 1: -//Feld mit verknüpftem Consumer - -//[entity, feldname, consumerName, ProviderName] -//let test = ["Communication_entity", "STANDARD_EMAIL_COMMUNICATION", "EmailCommunications", "EmailCommunications"]; -// -//let testrow = entities.createConfigForLoadingRows() -// .fields([test[1]]) -// .entity(test[0]) -// .provider(test[3]) -// .addParameter("ContactId_param", "d4c1bec3-656f-45ec-ae03-1c4d729d99fe") -// //.uid() -//let resultTest = entities.getRows(testrow); -//logging.log("resultTest -> " + JSON.stringify(resultTest)); - - - -//Beispiel 2: -//Feld direkt von anderem Entity -//let testrow = entities.createConfigForLoadingRows() -// .fields(["ZIP"]) -// .entity("Address_entity") -// .uid("1a67eaa7-21da-4a18-97ab-755ac5cb74f7") -// -//let resultTest = entities.getRows(testrow); -//logging.log("resultTest Beispiel 2 -> " + JSON.stringify(resultTest)); - - -//indexsearch.runIndexer(["Person"]); - - - - -//let resultClusterId = DuplicateScannerUtils.GetClusterWithDuplicates(["7a34d9d0-04c7-478c-a8e2-f584fe625c45", "c7ddf982-0e58-4152-b82b-8f5673b0b729"]); -//logging.log("resultClusterId -> " + resultClusterId); - - -// -//var filterName = "PersonDuplicates"; -//var targetEntity = "Person_entity"; -//var recordBlockSize = DuplicateScannerUtils.GetBlockSizeForScanner(filterName, targetEntity); -// -//logging.log("recordBlockSize -> " + recordBlockSize); - -//try -//{ -// let sourceContactId = "sourceContactId"; -// let targetContactId = "targetContactId"; -// -// var activityDataForInsert = { -// subject: "Es wurde ein Personendatensatz in diesen integriert", -// content: "Person mit ID " + sourceContactId + " wurde in Person mit ID " + targetContactId + " integriert", -// //categoryKeywordId: $KeywordRegistry.ac -// directionKeywordId: "x", -// responsibleContactId: EmployeeUtils.getCurrentContactId() -// }; -// -// var activityLinks = [["Person", "6e667085-bb97-4039-8dfe-2230002985e0"]] -// -// //activityLinks = ArrayUtils.distinct2d(activityLinks);//TODO: better check before adding the elements into the array if it already exists there -// -// var activityRes = ActivityUtils.insertNewActivity(activityDataForInsert, activityLinks, null, db.getCurrentAlias()); -//} -//catch (exception) -//{ -// logging.log("exception -> " + exception); -//} - -////notification.addNotification(util.getNewUUID(), null, null, null, notification., notification.PRIO_NORMAL, 2, notification.STATE_UNSEEN, [user], message, description); - - -//let currentContactId = EmployeeUtils.getCurrentContactId(); -//DuplicateScannerUtils.CreateMergeSuccessActivity("a2e084e2-d68a-4f1e-a1bb-f8d46ad6293d", "6e667085-bb97-4039-8dfe-2230002985e0", currentContactId, "Person"); - - -//logging.log("$KeywordRegistry.activityDirection$internal() -> " + $KeywordRegistry.activityDirection$internal()); - - - -//let processParameters = { -// filterName: "PersonDuplicates", -// targetEntity: "Person_entity" //process.execute is only able to handle strings -//} -//let userId = EmployeeUtils.getCurrentUserId(); -//if(userId == null) -// userId == ""; -//try -//{ -// let processId = "manualrun_rebuild_duplicatecache_" + util.getNewUUID(); -// process.executeTimer(processId, "RebuildAllDuplicatesCache_serverProcess", 0, true, false, process.TIMERTYPE_SERVER_RUN, userId, false, process.THREADPRIORITY_LOW) -// process.stopTimer(processId); -// logging.log("test -> "); -// process.executeAsync("RebuildAllDuplicateCaches_serverProcess", processParameters, false, userId, process.THREADPRIORITY_LOW) -// logging.log("test2 -> "); -//} -//catch (exception) -//{ -// logging.log(" exception-> " + exception); -//} diff --git a/entity/DuplicateScanner_entity/recordcontainers/dbrecordcontainer/recordfieldmappings/duplicatecount.value/expression.js b/entity/DuplicateScanner_entity/recordcontainers/dbrecordcontainer/recordfieldmappings/duplicatecount.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..a49f1a0db948473f671384d79a28219c3a23cd17 --- /dev/null +++ b/entity/DuplicateScanner_entity/recordcontainers/dbrecordcontainer/recordfieldmappings/duplicatecount.value/expression.js @@ -0,0 +1,13 @@ +import("system.result"); +import("Sql_lib"); + +var subselect = newSelect("count(*)").from("UNRELATEDDUPLICATES") +.where("UNRELATEDDUPLICATES.DUPLICATETYPE = HASDUPLICATE.OBJECT_TYPE") +.and("UNRELATEDDUPLICATES.SOURCEDUPLICATEID = HASDUPLICATE.OBJECT_ROWID"); +result.string( + newSelect("count(*)") + .from("HASDUPLICATE") + .where("HASDUPLICATE.OBJECT_TYPE = DUPLICATESCANNER.ENTITY_TO_SCAN_NAME") + .and("(HASDUPLICATE.DUPLICATECOUNT - (" + subselect.toString() + ")) > 0") + .toString() +); diff --git a/entity/DuplicatesUnrelated_entity/DuplicatesUnrelated_entity.aod b/entity/DuplicatesUnrelated_entity/DuplicatesUnrelated_entity.aod deleted file mode 100644 index 7d2241b72c2014a236b28f81f3f8a49060a1f768..0000000000000000000000000000000000000000 --- a/entity/DuplicatesUnrelated_entity/DuplicatesUnrelated_entity.aod +++ /dev/null @@ -1,91 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.18" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.18"> - <name>DuplicatesUnrelated_entity</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <documentation>%aditoprj%/entity/DuplicatesUnrelated_entity/documentation.adoc</documentation> - <siblings> - <element>Duplicates_entity</element> - </siblings> - <recordContainer>jditoRecordContainer</recordContainer> - <entityFields> - <entityProvider> - <name>#PROVIDER</name> - </entityProvider> - <entityProvider> - <name>UnrelatedPersonsProvider</name> - <titlePlural>Unrelated person duplicates</titlePlural> - <children> - <entityParameter> - <name>TargetEntity</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - <entityParameter> - <name>ClusterId_param</name> - <expose v="true" /> - <mandatory v="false" /> - </entityParameter> - </children> - </entityProvider> - <entityParameter> - <name>TargetEntity</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - <entityProvider> - <name>UnrelatedOrganisationsProvider</name> - <titlePlural>Unrelated organisations duplicates</titlePlural> - <children> - <entityParameter> - <name>TargetEntity</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - <entityParameter> - <name>ClusterId_param</name> - <expose v="true" /> - <mandatory v="false" /> - </entityParameter> - </children> - </entityProvider> - <entityField> - <name>SourceDuplicateDescription</name> - <title>Source duplicate</title> - </entityField> - <entityField> - <name>UnrelatedDuplicateDescription</name> - <title>Unrelated duplicate</title> - </entityField> - <entityField> - <name>UID</name> - </entityField> - <entityParameter> - <name>ClusterId_param</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - <entityProvider> - <name>#PROVIDER_AGGREGATES</name> - <useAggregates v="true" /> - </entityProvider> - </entityFields> - <recordContainers> - <jDitoRecordContainer> - <name>jditoRecordContainer</name> - <jDitoRecordAlias>Data_alias</jDitoRecordAlias> - <contentProcess>%aditoprj%/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/contentProcess.js</contentProcess> - <onDelete>%aditoprj%/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/onDelete.js</onDelete> - <recordFieldMappings> - <jDitoRecordFieldMapping> - <name>UID.value</name> - </jDitoRecordFieldMapping> - <jDitoRecordFieldMapping> - <name>SourceDuplicateDescription.value</name> - </jDitoRecordFieldMapping> - <jDitoRecordFieldMapping> - <name>UnrelatedDuplicateDescription.value</name> - </jDitoRecordFieldMapping> - </recordFieldMappings> - </jDitoRecordContainer> - </recordContainers> -</entity> diff --git a/entity/DuplicatesUnrelated_entity/documentation.adoc b/entity/DuplicatesUnrelated_entity/documentation.adoc deleted file mode 100644 index 06d0fa27494b89bf6d02bef96f9872cb1409c379..0000000000000000000000000000000000000000 --- a/entity/DuplicatesUnrelated_entity/documentation.adoc +++ /dev/null @@ -1,3 +0,0 @@ -=DuplicateUnrelated_entity - -These Duplicates not related to another Entity. \ No newline at end of file diff --git a/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/contentProcess.js b/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/contentProcess.js deleted file mode 100644 index 7f6beb6cd3e8fff07df2f33aa29835976eedc12d..0000000000000000000000000000000000000000 --- a/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/contentProcess.js +++ /dev/null @@ -1,73 +0,0 @@ -import("system.result"); -import("system.vars"); -import("system.db"); -import("Sql_lib"); - -var INDEX_ID = 0; -var INDEX_SOURCE_INFO1 = 1; -var INDEX_SOURCE_INFO2 = 3; -var INDEX_UNRELATED_INFO1 = 2; -var INDEX_UNRELATED_INFO2 = 4; -var INDEX_SOURCE_INFO = 1; -var INDEX_UNRELATED_INFO = 2; - - -let unrelatedDuplicates = []; -let resultUnrelatedDuplicates = []; -let targetEntity = vars.get("$param.TargetEntity"); -let clusterId = vars.get("$param.ClusterId_param"); -let querySelect = new SqlBuilder(); - -if(targetEntity == 'Person_entity') -{ - querySelect.select("ud.ID," - + " pSource.FIRSTNAME, pUnrelated.FIRSTNAME," - + " pSource.LASTNAME, pUnrelated.LASTNAME") - .from("UNRELATEDDUPLICATES", "ud") - .join("CONTACT", "cUnrelated.CONTACTID = ud.UNRELATEDDUPLICATEID", "cUnrelated") - .join("PERSON", "pUnrelated.PERSONID = cUnrelated.PERSON_ID", "pUnrelated") - .join("CONTACT", "cSource.CONTACTID = ud.SOURCEDUPLICATEID", "cSource") - .join("PERSON", "pSource.PERSONID = cSource.PERSON_ID", "pSource") - //If the clusterid parameter is present, only load the duplicates for this particular cluster - .whereIfSet(["UNRELATEDDUPLICATES", "CLUSTERID", "ud"], clusterId); - -} -else -{ - querySelect.select("ud.ID," - + " oSource.\"NAME\"," - + " oUnrelated.\"NAME\"") - .from("UNRELATEDDUPLICATES", "ud") - .join("CONTACT", "cUnrelated.CONTACTID = ud.UNRELATEDDUPLICATEID", "cUnrelated") - .join("ORGANISATION", "oUnrelated.ORGANISATIONID = cUnrelated.CONTACTID", "oUnrelated") - .join("CONTACT", "cSource.CONTACTID = ud.SOURCEDUPLICATEID", "cSource") - .join("ORGANISATION", "oSource.ORGANISATIONID = cSource.CONTACTID", "oSource") - //If the clusterid parameter is present, only load the duplicates for this particular cluster - .whereIfSet(["UNRELATEDDUPLICATES", "CLUSTERID", "ud"], clusterId); -} - -unrelatedDuplicates = querySelect.table(); - -for (let i = 0; i < unrelatedDuplicates.length; i++) -{ - let id = unrelatedDuplicates[i][INDEX_ID]; - let sourceInfo = ""; - let unrelatedInfo = ""; - - let sourceInfo1 = unrelatedDuplicates[i][INDEX_SOURCE_INFO1]; - let sourceInfo2 = unrelatedDuplicates[i][INDEX_SOURCE_INFO2]; - let unrelatedInfo1 = unrelatedDuplicates[i][INDEX_UNRELATED_INFO1]; - let unrelatedInfo2 = unrelatedDuplicates[i][INDEX_UNRELATED_INFO2]; - - sourceInfo = sourceInfo1; - if(sourceInfo2 != undefined) - sourceInfo += " " + sourceInfo2; - - unrelatedInfo = unrelatedInfo1; - if(unrelatedInfo2 != undefined) - unrelatedInfo += " " + unrelatedInfo2; - - resultUnrelatedDuplicates.push([id, sourceInfo, unrelatedInfo]); -} - -result.object(resultUnrelatedDuplicates); \ No newline at end of file diff --git a/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/onDelete.js b/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/onDelete.js deleted file mode 100644 index 4cb5ca4f2a88d7144b24d27ecf99028f547ca144..0000000000000000000000000000000000000000 --- a/entity/DuplicatesUnrelated_entity/recordcontainers/jditorecordcontainer/onDelete.js +++ /dev/null @@ -1,3 +0,0 @@ -import("Sql_lib"); - -newWhere("UNRELATEDDUPLICATES.ID", "$local.uid").deleteData(); \ No newline at end of file diff --git a/entity/Duplicates_entity/Duplicates_entity.aod b/entity/Duplicates_entity/Duplicates_entity.aod deleted file mode 100644 index fe9a19485083c678b01823430ae02631da122683..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/Duplicates_entity.aod +++ /dev/null @@ -1,234 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.18" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.18"> - <name>Duplicates_entity</name> - <title>Duplicates</title> - <majorModelMode>DISTRIBUTED</majorModelMode> - <documentation>%aditoprj%/entity/Duplicates_entity/documentation.adoc</documentation> - <siblings> - <element>DuplicatesUnrelated_entity</element> - </siblings> - <iconId>VAADIN:DATABASE</iconId> - <recordContainer>recordContainer</recordContainer> - <entityFields> - <entityProvider> - <name>#PROVIDER</name> - </entityProvider> - <entityField> - <name>CLUSTER_DESCRIPTION</name> - <title>Cluster description</title> - </entityField> - <entityField> - <name>COUNT_DUPLICATES_IN_CLUSTER</name> - <title>Count duplicates in cluster</title> - <contentType>NUMBER</contentType> - </entityField> - <entityField> - <name>TARGET_ENTITY</name> - </entityField> - <entityField> - <name>UID</name> - </entityField> - <entityProvider> - <name>SelfPersonDuplicatesProvider</name> - <titlePluralProcess>%aditoprj%/entity/Duplicates_entity/entityfields/selfpersonduplicatesprovider/titlePluralProcess.js</titlePluralProcess> - <titlePlural>Person duplicates</titlePlural> - <children> - <entityParameter> - <name>TargetEntity</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - </children> - </entityProvider> - <entityConsumer> - <name>SelfPersonDuplicatesConsumer</name> - <dependency> - <name>dependency</name> - <entityName>Duplicates_entity</entityName> - <fieldName>SelfPersonDuplicatesProvider</fieldName> - </dependency> - <children> - <entityParameter> - <name>TargetEntity</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/targetentity/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>ClusterId_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/clusterid_param/valueProcess.js</valueProcess> - <title></title> - </entityParameter> - </children> - </entityConsumer> - <entityParameter> - <name>TargetEntity</name> - <expose v="true" /> - <mandatory v="true" /> - </entityParameter> - <entityConsumer> - <name>SelfOrganisationDuplicatesConsumer</name> - <dependency> - <name>dependency</name> - <entityName>Duplicates_entity</entityName> - <fieldName>SelfOrganisationDuplicatesProvider</fieldName> - </dependency> - <children> - <entityParameter> - <name>TargetEntity</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/targetentity/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>ClusterId_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/clusterid_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> - <entityProvider> - <name>SelfOrganisationDuplicatesProvider</name> - <titlePlural>Organisation duplicates</titlePlural> - </entityProvider> - <entityConsumer> - <name>DuplicatesUnrelatedPersonConsumer</name> - <dependency> - <name>dependency</name> - <entityName>DuplicatesUnrelated_entity</entityName> - <fieldName>UnrelatedPersonsProvider</fieldName> - </dependency> - <children> - <entityParameter> - <name>TargetEntity</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/targetentity/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>ClusterId_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/clusterid_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> - <entityField> - <name>CLUSTER_ID</name> - </entityField> - <entityConsumer> - <name>DuplicatePersonsConsumer</name> - <dependency> - <name>dependency</name> - <entityName>Person_entity</entityName> - <fieldName>NonselfDuplicates</fieldName> - </dependency> - <children> - <entityParameter> - <name>OnlyShowContactIds_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/onlyshowcontactids_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/duplicateactionscontrol_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> - <entityActionGroup> - <name>DuplicateClusterActionGroup</name> - <title>Duplicate actions</title> - <children> - <entityActionField> - <name>IgnoreWholeCluster</name> - <title>Ignore whole cluster</title> - <onActionProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicateclusteractiongroup/children/ignorewholecluster/onActionProcess.js</onActionProcess> - <isSelectionAction v="true" /> - <iconId>VAADIN:CLOSE</iconId> - </entityActionField> - </children> - </entityActionGroup> - <entityConsumer> - <name>DuplicatesUnrelatedOrganisationConsumer</name> - <dependency> - <name>dependency</name> - <entityName>DuplicatesUnrelated_entity</entityName> - <fieldName>UnrelatedOrganisationsProvider</fieldName> - </dependency> - <children> - <entityParameter> - <name>TargetEntity</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/targetentity/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>ClusterId_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/clusterid_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> - <entityActionGroup> - <name>PersonOpenClusterDetailActionGroup</name> - <children> - <entityActionField> - <name>PersonOpenClusterDetail</name> - <title></title> - <onActionProcess>%aditoprj%/entity/Duplicates_entity/entityfields/personopenclusterdetailactiongroup/children/personopenclusterdetail/onActionProcess.js</onActionProcess> - <isSelectionAction v="true" /> - <iconId>VAADIN:FOLDER_OPEN</iconId> - </entityActionField> - </children> - </entityActionGroup> - <entityParameter> - <name>ClusterId_param</name> - <expose v="true" /> - </entityParameter> - <entityActionGroup> - <name>OrganisationOpenClusterDetailActionGroup</name> - <children> - <entityActionField> - <name>OrganisationOpenClusterDetail</name> - <title>Open cluster detail</title> - <onActionProcess>%aditoprj%/entity/Duplicates_entity/entityfields/organisationopenclusterdetailactiongroup/children/organisationopenclusterdetail/onActionProcess.js</onActionProcess> - <isSelectionAction v="true" /> - <iconId>VAADIN:FOLDER_OPEN</iconId> - </entityActionField> - </children> - </entityActionGroup> - <entityConsumer> - <name>DuplicateOrganisationsConsumer</name> - <dependency> - <name>dependency</name> - <entityName>Organisation_entity</entityName> - <fieldName>NonselfDuplicates</fieldName> - </dependency> - <children> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/duplicateactionscontrol_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>OnlyShowContactIds_param</name> - <valueProcess>%aditoprj%/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/onlyshowcontactids_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> - <entityProvider> - <name>#PROVIDER_AGGREGATES</name> - <useAggregates v="true" /> - </entityProvider> - </entityFields> - <recordContainers> - <jDitoRecordContainer> - <name>recordContainer</name> - <jDitoRecordAlias>Data_alias</jDitoRecordAlias> - <contentProcess>%aditoprj%/entity/Duplicates_entity/recordcontainers/recordcontainer/contentProcess.js</contentProcess> - <recordFieldMappings> - <jDitoRecordFieldMapping> - <name>UID.value</name> - </jDitoRecordFieldMapping> - <jDitoRecordFieldMapping> - <name>CLUSTER_DESCRIPTION.value</name> - </jDitoRecordFieldMapping> - <jDitoRecordFieldMapping> - <name>COUNT_DUPLICATES_IN_CLUSTER.value</name> - </jDitoRecordFieldMapping> - <jDitoRecordFieldMapping> - <name>TARGET_ENTITY.value</name> - </jDitoRecordFieldMapping> - <jDitoRecordFieldMapping> - <name>CLUSTER_ID.value</name> - </jDitoRecordFieldMapping> - </recordFieldMappings> - </jDitoRecordContainer> - </recordContainers> -</entity> diff --git a/entity/Duplicates_entity/documentation.adoc b/entity/Duplicates_entity/documentation.adoc deleted file mode 100644 index aa8d46c4a31d622c6ed142e3e92422218a302ae9..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/documentation.adoc +++ /dev/null @@ -1,3 +0,0 @@ -=Duplicates_entity - -This Entity shows the Duplicates of \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/duplicateclusteractiongroup/children/ignorewholecluster/onActionProcess.js b/entity/Duplicates_entity/entityfields/duplicateclusteractiongroup/children/ignorewholecluster/onActionProcess.js deleted file mode 100644 index 77822dbca0461d5ffccc31af058b9a5750002a4b..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicateclusteractiongroup/children/ignorewholecluster/onActionProcess.js +++ /dev/null @@ -1,27 +0,0 @@ -import("system.logging"); -import("system.neon"); -import("system.vars"); -import("DuplicateScanner_lib"); -import("system.notification"); - -let clusterId = vars.get("$sys.selection"); - -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); - } - //notification.createConfig().notificationType(notification.t) - //neon.refresh(["$field.SelfPersonDuplicatesConsumer"]) - - //todo Temporary!!! In the first refresh is the record via idValue selected and gets refreshed but stays visible in the record container - //todo Temporary!!! on the second refresh, no selecten remains and the container loads the remaining records as expected - neon.refreshAll(); - neon.refreshAll(); -} - - diff --git a/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/duplicateactionscontrol_param/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/duplicateactionscontrol_param/valueProcess.js deleted file mode 100644 index 5267adbe23e51fbb6b2c1c2aa44c947c3c3e7c34..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/duplicateactionscontrol_param/valueProcess.js +++ /dev/null @@ -1,2 +0,0 @@ -import("system.result"); -result.string("2");//todo exchange with keyword diff --git a/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/onlyshowcontactids_param/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/onlyshowcontactids_param/valueProcess.js deleted file mode 100644 index ecb8c518cd40068c5e36606b64c83765a69962f6..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicateorganisationsconsumer/children/onlyshowcontactids_param/valueProcess.js +++ /dev/null @@ -1,18 +0,0 @@ -import("system.logging"); -import("system.result"); -import("system.vars"); -import("DuplicateScanner_lib"); - -let clusterRecordId = vars.get("$field.UID"); - -let contactIdsInCluster = DuplicateScannerUtils.getCachedDuplicatesForClusterId(clusterRecordId); - -/* - * 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(contactIdsInCluster.length == 0) - result.string(JSON.stringify(["nodata"])); -else - result.string(JSON.stringify(contactIdsInCluster)); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/duplicateactionscontrol_param/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/duplicateactionscontrol_param/valueProcess.js deleted file mode 100644 index 5267adbe23e51fbb6b2c1c2aa44c947c3c3e7c34..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/duplicateactionscontrol_param/valueProcess.js +++ /dev/null @@ -1,2 +0,0 @@ -import("system.result"); -result.string("2");//todo exchange with keyword diff --git a/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/onlyshowcontactids_param/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/onlyshowcontactids_param/valueProcess.js deleted file mode 100644 index ecb8c518cd40068c5e36606b64c83765a69962f6..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicatepersonsconsumer/children/onlyshowcontactids_param/valueProcess.js +++ /dev/null @@ -1,18 +0,0 @@ -import("system.logging"); -import("system.result"); -import("system.vars"); -import("DuplicateScanner_lib"); - -let clusterRecordId = vars.get("$field.UID"); - -let contactIdsInCluster = DuplicateScannerUtils.getCachedDuplicatesForClusterId(clusterRecordId); - -/* - * 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(contactIdsInCluster.length == 0) - result.string(JSON.stringify(["nodata"])); -else - result.string(JSON.stringify(contactIdsInCluster)); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/clusterid_param/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/clusterid_param/valueProcess.js deleted file mode 100644 index 152dfe0324a75ddba53552148d86b4af97acc6dd..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/clusterid_param/valueProcess.js +++ /dev/null @@ -1,5 +0,0 @@ -import("system.vars"); -import("system.result"); - -let clusterId = vars.get("$field.UID"); -result.string(clusterId); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/targetentity/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/targetentity/valueProcess.js deleted file mode 100644 index ea14ae6b612db05a0e3a34e900ca16547f9ed208..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicatesunrelatedorganisationconsumer/children/targetentity/valueProcess.js +++ /dev/null @@ -1,4 +0,0 @@ -import("system.vars"); -import("system.result"); - -result.string("Organisation_entity"); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/clusterid_param/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/clusterid_param/valueProcess.js deleted file mode 100644 index 03d5df5be8044683b94f306002bcbeada25b8737..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/clusterid_param/valueProcess.js +++ /dev/null @@ -1,6 +0,0 @@ -import("system.vars"); -import("system.result"); - -//let clusterId = vars.get("$field.UID"); -let clusterId = vars.get("$param.ClusterId_param"); -result.string(clusterId); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/targetentity/valueProcess.js b/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/targetentity/valueProcess.js deleted file mode 100644 index f8b07f56abc4e6b2df8800916a77fa58b50e99bf..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/duplicatesunrelatedpersonconsumer/children/targetentity/valueProcess.js +++ /dev/null @@ -1,4 +0,0 @@ -import("system.vars"); -import("system.result"); - -result.string("Person_entity"); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/organisationopenclusterdetailactiongroup/children/organisationopenclusterdetail/onActionProcess.js b/entity/Duplicates_entity/entityfields/organisationopenclusterdetailactiongroup/children/organisationopenclusterdetail/onActionProcess.js deleted file mode 100644 index f599915bdc2dbbb45a9b52bb92e7de622c74f618..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/organisationopenclusterdetailactiongroup/children/organisationopenclusterdetail/onActionProcess.js +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/entity/Duplicates_entity/entityfields/personopenclusterdetailactiongroup/children/personopenclusterdetail/onActionProcess.js b/entity/Duplicates_entity/entityfields/personopenclusterdetailactiongroup/children/personopenclusterdetail/onActionProcess.js deleted file mode 100644 index 9d5cf8d6da175cc02200a6874c07f460589f6745..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/personopenclusterdetailactiongroup/children/personopenclusterdetail/onActionProcess.js +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/clusterid_param/valueProcess.js b/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/clusterid_param/valueProcess.js deleted file mode 100644 index 8ef7b768764a4f534b886492c99ec2e9408c00e5..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/clusterid_param/valueProcess.js +++ /dev/null @@ -1,5 +0,0 @@ -import("system.vars"); -import("system.result"); - -let clusterId = vars.get("$param.ClusterId_param"); -result.string(clusterId); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/targetentity/valueProcess.js b/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/targetentity/valueProcess.js deleted file mode 100644 index e781fb72fd248164b8b63a98008094744aee7460..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/selforganisationduplicatesconsumer/children/targetentity/valueProcess.js +++ /dev/null @@ -1,2 +0,0 @@ -import("system.result"); -result.string("Organisation_entity"); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/clusterid_param/valueProcess.js b/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/clusterid_param/valueProcess.js deleted file mode 100644 index 258d52e236265f259b354a56fd04b1a3d2fcb566..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/clusterid_param/valueProcess.js +++ /dev/null @@ -1,6 +0,0 @@ -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 diff --git a/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/targetentity/valueProcess.js b/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/targetentity/valueProcess.js deleted file mode 100644 index 0f7bee25ea3bd34aeeceff7f47f7f9118e69b7ba..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/selfpersonduplicatesconsumer/children/targetentity/valueProcess.js +++ /dev/null @@ -1,2 +0,0 @@ -import("system.result"); -result.string("Person_entity"); \ No newline at end of file diff --git a/entity/Duplicates_entity/entityfields/selfpersonduplicatesprovider/titlePluralProcess.js b/entity/Duplicates_entity/entityfields/selfpersonduplicatesprovider/titlePluralProcess.js deleted file mode 100644 index b213a20410d97225b6a299a51857dcf0057ba50c..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/entityfields/selfpersonduplicatesprovider/titlePluralProcess.js +++ /dev/null @@ -1,3 +0,0 @@ -import("system.result"); -import("system.translate"); -result.string(translate.text("Person duplicates")); \ No newline at end of file diff --git a/entity/Duplicates_entity/recordcontainers/recordcontainer/contentProcess.js b/entity/Duplicates_entity/recordcontainers/recordcontainer/contentProcess.js deleted file mode 100644 index fc29941db97b09cd069a88b166304eb2be952b1e..0000000000000000000000000000000000000000 --- a/entity/Duplicates_entity/recordcontainers/recordcontainer/contentProcess.js +++ /dev/null @@ -1,121 +0,0 @@ -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); -} diff --git a/entity/KeywordEntry_entity/KeywordEntry_entity.aod b/entity/KeywordEntry_entity/KeywordEntry_entity.aod index 39bcb0a4581d7af2ee3b4a472d718ba09173abe5..125241142e38eac39484efca2261f07e5538ce81 100644 --- a/entity/KeywordEntry_entity/KeywordEntry_entity.aod +++ b/entity/KeywordEntry_entity/KeywordEntry_entity.aod @@ -637,6 +637,24 @@ <fieldName>KeywordPhases</fieldName> <isConsumer v="false" /> </entityDependency> + <entityDependency> + <name>414ad662-727b-4e9f-9e86-0659d92ec60d</name> + <entityName>DuplicatePerson_entity</entityName> + <fieldName>KeywordContactStates</fieldName> + <isConsumer v="false" /> + </entityDependency> + <entityDependency> + <name>0f8f9bfc-379c-4f05-8364-8270e718fe9a</name> + <entityName>DuplicateOrganisation_entity</entityName> + <fieldName>KeywordContactStates</fieldName> + <isConsumer v="false" /> + </entityDependency> + <entityDependency> + <name>b78c0dd7-a35c-4d9b-9a23-1a20e7e41464</name> + <entityName>DuplicateOrganisation_entity</entityName> + <fieldName>KeywordOrganisationTypes</fieldName> + <isConsumer v="false" /> + </entityDependency> <entityDependency> <name>98608b12-9927-4197-adfe-1398a388bcb0</name> <entityName>WebtrackingTag_entity</entityName> diff --git a/entity/Organisation_entity/Organisation_entity.aod b/entity/Organisation_entity/Organisation_entity.aod index be40c333adc19fbc28ccd8715d0596b2f151a47f..b0b919e690ff43f4ff5aafc1723cb375a3b0a56f 100644 --- a/entity/Organisation_entity/Organisation_entity.aod +++ b/entity/Organisation_entity/Organisation_entity.aod @@ -186,6 +186,12 @@ <fieldName>OrganisationConsumer</fieldName> <isConsumer v="false" /> </entityDependency> + <entityDependency> + <name>fa1b6124-1e00-4fa0-8c04-debc19f58d95</name> + <entityName>DuplicatePerson_entity</entityName> + <fieldName>Organisations</fieldName> + <isConsumer v="false" /> + </entityDependency> <entityDependency> <name>640a8509-1972-4dc5-980e-68832f1c03c5</name> <entityName>Planning_entity</entityName> @@ -226,14 +232,6 @@ <name>AttributeId_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> <entityParameter> <name>MapViewAdditionalFeatures_param</name> <expose v="false" /> @@ -699,14 +697,6 @@ <name>ExcludedContactIds_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> <entityParameter> <name>MapViewAdditionalFeatures_param</name> <expose v="false" /> @@ -770,14 +760,6 @@ <name>ExcludedContactIds_param</name> <expose v="true" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> <entityParameter> <name>MapViewAdditionalFeatures_param</name> <expose v="false" /> @@ -955,65 +937,10 @@ <colorProcess>%aditoprj%/entity/Organisation_entity/entityfields/lastactivity/colorProcess.js</colorProcess> <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/lastactivity/valueProcess.js</valueProcess> </entityField> - <entityProvider> - <name>SelfDuplicates</name> - <documentation>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicates/documentation.adoc</documentation> - <titlePlural>Duplicates</titlePlural> - <children> - <entityParameter> - <name>AttributeId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>AttributeKeyId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>FilterPreSet_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>OrganisationType_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>WithPrivate_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>OnlyOwnSupervised_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>ExcludeOrganisationsByPersonId</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>MapViewAdditionalFeatures_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>MapViewCenterLat_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>MapViewCenterLon_param</name> - <expose v="false" /> - </entityParameter> - </children> - </entityProvider> <entityParameter> <name>OnlyShowContactIds_param</name> <expose v="true" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="true" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="true" /> - </entityParameter> <entityField> <name>STANDARD_COUNTRY</name> <title>Standard Country</title> @@ -1023,33 +950,6 @@ <name>OnlyOwnSupervised_param</name> <expose v="true" /> </entityParameter> - <entityConsumer> - <name>SelfDuplicatesUncached</name> - <dependency> - <name>dependency</name> - <entityName>Organisation_entity</entityName> - <fieldName>SelfDuplicates</fieldName> - </dependency> - <children> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js</valueProcess> - <title></title> - </entityParameter> - <entityParameter> - <name>OnlyShowContactIds_param</name> - <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>ExcludedContactIds_param</name> - <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> <entityConsumer> <name>CommRestrictions</name> <dependency> @@ -1153,14 +1053,6 @@ <name>AttributeKeyId_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> <entityParameter> <name>ExcludedContactIds_param</name> <expose v="false" /> @@ -1226,10 +1118,6 @@ <name>OnlyShowContactIds_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> <entityParameter> <name>ExcludeOrganisationsByPersonId</name> <expose v="false" /> @@ -1282,52 +1170,6 @@ <name>FilterPreSet_param</name> <expose v="true" /> </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> <name>AttributesFilter</name> <dependency> @@ -1526,52 +1368,6 @@ <iconId>VAADIN:ENVELOPE</iconId> <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/newletter/stateProcess.js</stateProcess> </entityActionField> - <entityActionGroup> - <name>DuplicateActions</name> - <title>Duplicate actions</title> - <state>AUTO</state> - <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/stateProcess.js</stateProcess> - <children> - <entityActionField> - <name>IntegrateSelectedIntoCurrentAction</name> - <title>Integrate selected into current contact</title> - <onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js</onActionProcess> - <isMenuAction v="true" /> - <isObjectAction v="false" /> - <isSelectionAction v="true" /> - <iconId>NEON:IMPORT</iconId> - <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js</stateProcess> - </entityActionField> - <entityActionField> - <name>IntegrateCurrentIntoSelectedAction</name> - <title>Integrate current into selected contact</title> - <onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js</onActionProcess> - <isMenuAction v="true" /> - <isObjectAction v="false" /> - <isSelectionAction v="true" /> - <iconId>NEON:EXPORT</iconId> - <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js</stateProcess> - </entityActionField> - <entityActionField> - <name>IgnoreDuplicate</name> - <title>Ignore Duplicate</title> - <onActionProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js</onActionProcess> - <isMenuAction v="true" /> - <isObjectAction v="false" /> - <isSelectionAction v="true" /> - <iconId>VAADIN:CLOSE</iconId> - <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/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> - </children> - </entityActionGroup> <entityActionField> <name>startWorkflow</name> <title>Start workflow</title> @@ -1629,6 +1425,22 @@ <iconId>VAADIN:CURLY_BRACKETS</iconId> <stateProcess>%aditoprj%/entity/Organisation_entity/entityfields/openadminview/stateProcess.js</stateProcess> </entityActionField> + <entityConsumer> + <name>Duplicates</name> + <selectionMode>MULTI</selectionMode> + <selectionModeProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicates/selectionModeProcess.js</selectionModeProcess> + <dependency> + <name>dependency</name> + <entityName>DuplicateOrganisation_entity</entityName> + <fieldName>#PROVIDER</fieldName> + </dependency> + <children> + <entityParameter> + <name>Obj_param</name> + <valueProcess>%aditoprj%/entity/Organisation_entity/entityfields/duplicates/children/obj_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> <entityConsumer> <name>Plannings</name> <dependency> @@ -1787,11 +1599,13 @@ </dbRecordFieldMapping> <dbRecordFieldMapping> <name>STANDARD_EMAIL_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js</expression> <isFilterable v="false" /> <isLookupFilter v="false" /> </dbRecordFieldMapping> <dbRecordFieldMapping> <name>STANDARD_PHONE_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js</expression> <isFilterable v="false" /> <isLookupFilter v="false" /> </dbRecordFieldMapping> @@ -1943,6 +1757,13 @@ <filterConditionProcess>%aditoprj%/entity/Organisation_entity/recordcontainers/db/filterextensions/responsibleassignment/filterConditionProcess.js</filterConditionProcess> <filtertype>EXTENDED</filtertype> </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> <name>Communication_Mail_filter</name> <title>Communication: Mail</title> diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js deleted file mode 100644 index c9289cbd0ef896d7dbcf5eae668bac8a87e861da..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js +++ /dev/null @@ -1,10 +0,0 @@ -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 diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js deleted file mode 100644 index b736eb15f5b18e779e37281fe216c9297ca86191..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 != "1")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js deleted file mode 100644 index 99d7f55925b3fed54dc2d5e20604793b6e9fd862..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js +++ /dev/null @@ -1,21 +0,0 @@ -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); - } -} - - diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js deleted file mode 100644 index fdd913ad06bf2815da3127d405d21b258c4ad795..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js deleted file mode 100644 index b736eb15f5b18e779e37281fe216c9297ca86191..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 != "1")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js deleted file mode 100644 index b736eb15f5b18e779e37281fe216c9297ca86191..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 != "1")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/duplicateactions/stateProcess.js b/entity/Organisation_entity/entityfields/duplicateactions/stateProcess.js deleted file mode 100644 index 5b8b24021ddfcba34a258bf7af7d4ec5ec65852a..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/duplicateactions/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 == "0")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/duplicates/children/obj_param/valueProcess.js b/entity/Organisation_entity/entityfields/duplicates/children/obj_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..39bdfae5361be160d838627fc4e4de2d45eb9b82 --- /dev/null +++ b/entity/Organisation_entity/entityfields/duplicates/children/obj_param/valueProcess.js @@ -0,0 +1,20 @@ +import("system.vars"); +import("system.result"); + +result.string(JSON.stringify({ + CONTACTID: vars.get("$field.CONTACTID"), + NAME: vars.get("$field.NAME"), + ADDRESS_ID: vars.get("$field.ADDRESS_ID"), + CUSTOMERCODE: vars.get("$field.CUSTOMERCODE"), + LANGUAGE: vars.get("$field.LANGUAGE"), + TYPE: vars.get("$field.TYPE"), + STATUS: vars.get("$field.STATUS"), + STANDARD_ADDRESS: vars.get("$field.STANDARD_ADDRESS"), + STANDARD_CITY: vars.get("$field.STANDARD_CITY"), + STANDARD_COUNTRY: vars.get("$field.STANDARD_COUNTRY"), + STANDARD_EMAIL_COMMUNICATION: vars.get("$field.STANDARD_EMAIL_COMMUNICATION"), + STANDARD_LAT: vars.get("$field.STANDARD_LAT"), + STANDARD_LON: vars.get("$field.STANDARD_LON"), + STANDARD_PHONE_COMMUNICATION: vars.get("$field.STANDARD_PHONE_COMMUNICATION"), + STANDARD_ZIP: vars.get("$field.STANDARD_ZIP") +})); diff --git a/entity/Organisation_entity/entityfields/duplicates/selectionModeProcess.js b/entity/Organisation_entity/entityfields/duplicates/selectionModeProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..c92317456ba1a7476401171e875526c8eff6fa76 --- /dev/null +++ b/entity/Organisation_entity/entityfields/duplicates/selectionModeProcess.js @@ -0,0 +1,9 @@ +import("system.result"); +import("system.vars"); +import("system.neon"); + +result.string( + vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW || + vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_EDIT ? + "NONE" : "MULTI" +); diff --git a/entity/Organisation_entity/entityfields/nonselfduplicates/documentation.adoc b/entity/Organisation_entity/entityfields/nonselfduplicates/documentation.adoc deleted file mode 100644 index dc35980c12405163da2417e0472197dd9e3c7285..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/nonselfduplicates/documentation.adoc +++ /dev/null @@ -1,3 +0,0 @@ -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 diff --git a/entity/Organisation_entity/entityfields/selfduplicates/documentation.adoc b/entity/Organisation_entity/entityfields/selfduplicates/documentation.adoc deleted file mode 100644 index 396162acb9e2db72c8c1090f4fdda9eb84a95738..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/selfduplicates/documentation.adoc +++ /dev/null @@ -1 +0,0 @@ -Provides organisation duplicate-records within the `Organisation_entity` scope itself. \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js b/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js deleted file mode 100644 index 81570217c3ecd3cf76deb5e14d7ac0a6121a59bb..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js +++ /dev/null @@ -1,2 +0,0 @@ -import("system.result"); -result.string("1");//todo use keyword \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js b/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js deleted file mode 100644 index 821415ae694dd9c3d98a7703f4c59b81a37f524b..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js +++ /dev/null @@ -1,3 +0,0 @@ -import("system.vars"); -import("system.result"); -result.string(vars.get("$field.CONTACTID")); \ No newline at end of file diff --git a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js b/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js deleted file mode 100644 index 849632882bbabd4e65dfb25c7dc984366bde5116..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -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 diff --git a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js b/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js deleted file mode 100644 index 77f449d52a0995e5bb7e8ba94746a1659bf36001..0000000000000000000000000000000000000000 --- a/entity/Organisation_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js +++ /dev/null @@ -1,55 +0,0 @@ -import("system.project"); -import("system.indexsearch"); -import("system.vars"); -import("DuplicateScanner_lib"); -import("system.result"); - -var scannerName = "OrganisationDuplicates"; -var targetEntity = "Organisation_entity"; -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 diff --git a/entity/Organisation_entity/initFilterProcess.js b/entity/Organisation_entity/initFilterProcess.js index 936a623f599055fc4fb5fa81c850fc7d323bff22..afecc1ede02d6f412681545610696b1efa17dc90 100644 --- a/entity/Organisation_entity/initFilterProcess.js +++ b/entity/Organisation_entity/initFilterProcess.js @@ -5,15 +5,10 @@ import("KeywordRegistry_basic"); import("system.result"); var filter = vars.get("$param.FilterPreSet_param"); - -var res; -if (filter) - res = filter; -else if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER) +if(!filter && vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER) { var statusInactive = $KeywordRegistry.contactStatus$inactive(); - - filter = { + filter = JSON.stringify({ type: "group", operator: "AND", childs: [{ @@ -24,9 +19,9 @@ else if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FIL key: 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 diff --git a/entity/Organisation_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js b/entity/Organisation_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..96f185ae76701ab39777fe91db3d18baf328b614 --- /dev/null +++ b/entity/Organisation_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js @@ -0,0 +1,16 @@ +import("system.result"); +import("Sql_lib"); +import("system.vars"); +import("DuplicateScanner_lib"); + +var sqlOperator = SqlUtils.getSqlConditionalOperator(vars.get("$local.operator")); + +result.string(newWhere( + "CONTACT.CONTACTID", + DuplicateScannerUtils.getDuplicateConditionalListSql( + ["Organisation_entity"], + vars.get("$local.rawvalue"), + sqlOperator, true + ), + SqlBuilder.IN() +)); diff --git a/entity/Organisation_entity/recordcontainers/db/onDBDelete.js b/entity/Organisation_entity/recordcontainers/db/onDBDelete.js index 00e50690d485216c4b4956f416b688ab433e68a8..320f4e3329fa4fd7195da03495b3cd7e5812a9d6 100644 --- a/entity/Organisation_entity/recordcontainers/db/onDBDelete.js +++ b/entity/Organisation_entity/recordcontainers/db/onDBDelete.js @@ -7,7 +7,7 @@ import("Attribute_lib"); // TODO: enable when duplicate-module is finalized let contactId = vars.get("$field.CONTACTID"); -DuplicateScannerUtils.deleteCachedDuplicate(contactId); +DuplicateScannerUtils.deleteHasDuplicateEntries("Organisation_entity", [contactId]); new AttributeRelationQuery(contactId, null, ContextUtils.getCurrentContextId()) .deleteAllAttributes(); diff --git a/entity/Organisation_entity/recordcontainers/db/onDBInsert.js b/entity/Organisation_entity/recordcontainers/db/onDBInsert.js index 60299cd7d4eda2ceed743571cec9cbda912ad318..c8db8fdecb72ba18f14ae3fd3a5bd05223ac2396 100644 --- a/entity/Organisation_entity/recordcontainers/db/onDBInsert.js +++ b/entity/Organisation_entity/recordcontainers/db/onDBInsert.js @@ -1,5 +1,8 @@ import("system.vars"); import("Workflow_lib"); +import("DuplicateScanner_lib"); + +DuplicateScannerUtils.updateHasDuplicateEntry("Organisation_entity"); //start the execution in afterOperatingState, because here the dataset is not yet inserted vars.set("$context.workflowQueue", {}); diff --git a/entity/Organisation_entity/recordcontainers/db/onDBUpdate.js b/entity/Organisation_entity/recordcontainers/db/onDBUpdate.js index 9f0337793c84207f61013809345f829d1f874671..737768cc8678c59374d99e790f4956812e3661db 100644 --- a/entity/Organisation_entity/recordcontainers/db/onDBUpdate.js +++ b/entity/Organisation_entity/recordcontainers/db/onDBUpdate.js @@ -6,6 +6,9 @@ import("Communication_lib"); import("Entity_lib"); import("Workflow_lib"); import("KeywordRegistry_basic"); +import("DuplicateScanner_lib"); + +DuplicateScannerUtils.updateHasDuplicateEntry("Organisation_entity"); // TODO: this is a workaround for missing possibility to react on changes of fields not connected to record Contqainer #1030023 var rowdata = vars.get("$local.rowdata"); diff --git a/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js b/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..41ef07ff58ec51eda018bed12cb5b0b2b657c348 --- /dev/null +++ b/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Communication_lib"); + +var res = CommUtil.getStandardSubSqlMail(); +result.string(res); \ No newline at end of file diff --git a/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js b/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..320ae40ad1ba794b4e759037fa0f382b9af67696 --- /dev/null +++ b/entity/Organisation_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Communication_lib"); + +var res = CommUtil.getStandardSubSqlPhone(); +result.string(res); \ No newline at end of file diff --git a/entity/Person_entity/Person_entity.aod b/entity/Person_entity/Person_entity.aod index 87f279d514b74c7e9a02d6386c8416589874f65d..acb1ac9f267e189656e69ac475c18f3059e76dfd 100644 --- a/entity/Person_entity/Person_entity.aod +++ b/entity/Person_entity/Person_entity.aod @@ -162,10 +162,6 @@ <name>OnlyActive_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> </children> </entityProvider> <entityField> @@ -411,14 +407,6 @@ <name>OnlyActive_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> </children> </entityProvider> <entityConsumer> @@ -776,10 +764,6 @@ <name>OnlyActive_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> </children> </entityProvider> <entityConsumer> @@ -945,46 +929,11 @@ <displayValueProcess>%aditoprj%/entity/Person_entity/entityfields/organisation_contactid/displayValueProcess.js</displayValueProcess> <onValidation>%aditoprj%/entity/Person_entity/entityfields/organisation_contactid/onValidation.js</onValidation> </entityField> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="true" /> - </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="true" /> - <mandatory v="false" /> - </entityParameter> <entityField> <name>STANDARD_COUNTRY</name> <title>Country</title> <groupable v="true" /> </entityField> - <entityConsumer> - <name>SelfDuplicatesUncached</name> - <dependency> - <name>dependency</name> - <entityName>Person_entity</entityName> - <fieldName>SelfDuplicates</fieldName> - </dependency> - <children> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <valueProcess>%aditoprj%/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <valueProcess>%aditoprj%/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>OnlyShowContactIds_param</name> - <valueProcess>%aditoprj%/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js</valueProcess> - </entityParameter> - <entityParameter> - <name>ExcludedContactIds_param</name> - <valueProcess>%aditoprj%/entity/Person_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js</valueProcess> - </entityParameter> - </children> - </entityConsumer> <entityField> <name>LEAD_LEADID</name> </entityField> @@ -1035,47 +984,12 @@ <name>OnlyActive_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> </children> </entityProvider> <entityParameter> <name>OnlyShowContactIds_param</name> <expose v="true" /> </entityParameter> - <entityProvider> - <name>SelfDuplicates</name> - <documentation>%aditoprj%/entity/Person_entity/entityfields/selfduplicates/documentation.adoc</documentation> - <titlePlural>Duplicates</titlePlural> - <children> - <entityParameter> - <name>OnlyShowContactIds_param</name> - <mandatory v="true" /> - </entityParameter> - <entityParameter> - <name>OnlyActive_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>OrgId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>OnlyOwnSupervised_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>ContactId_param</name> - <expose v="false" /> - </entityParameter> - </children> - </entityProvider> <entityField> <name>IndexCommunication</name> </entityField> @@ -1124,10 +1038,6 @@ <name>ContactId_param</name> <expose v="false" /> </entityParameter> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> <entityParameter> <name>OnlyOwnSupervised_param</name> <expose v="false" /> @@ -1144,10 +1054,6 @@ <name>OnlyActive_param</name> <expose v="true" /> </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> </children> </entityProvider> <entityField> @@ -1164,12 +1070,6 @@ <name>indexP</name> <documentation>%aditoprj%/entity/Person_entity/entityfields/indexp/documentation.adoc</documentation> <recordContainer>index</recordContainer> - <children> - <entityParameter> - <name>DuplicateActionsControl_param</name> - <expose v="false" /> - </entityParameter> - </children> </entityProvider> <entityField> <name>PERSON_OBJECTTYPE</name> @@ -1195,40 +1095,6 @@ </entityParameter> </children> </entityConsumer> - <entityProvider> - <name>NonselfDuplicates</name> - <documentation>%aditoprj%/entity/Person_entity/entityfields/nonselfduplicates/documentation.adoc</documentation> - <dependencies> - <entityDependency> - <name>3a4352e2-9686-4c52-9d01-dbfad8c68ea7</name> - <entityName>Duplicates_entity</entityName> - <fieldName>DuplicatePersonsConsumer</fieldName> - <isConsumer v="false" /> - </entityDependency> - </dependencies> - <children> - <entityParameter> - <name>ContactId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>DuplicateCurrentContactId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>OnlyShowContactIds_param</name> - <expose v="true" /> - </entityParameter> - <entityParameter> - <name>OrgId_param</name> - <expose v="false" /> - </entityParameter> - <entityParameter> - <name>OnlyOwnSupervised_param</name> - <expose v="false" /> - </entityParameter> - </children> - </entityProvider> <entityConsumer> <name>DistrictResponsibles</name> <state>INVISIBLE</state> @@ -1395,52 +1261,6 @@ </entityActionField> </children> </entityActionGroup> - <entityActionGroup> - <name>DuplicateActions</name> - <title>Duplicate actions</title> - <state>AUTO</state> - <stateProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/stateProcess.js</stateProcess> - <children> - <entityActionField> - <name>IntegrateSelectedIntoCurrentAction</name> - <title>Integrate selected into current contact</title> - <onActionProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/onActionProcess.js</onActionProcess> - <isMenuAction v="true" /> - <isObjectAction v="false" /> - <isSelectionAction v="true" /> - <iconId>NEON:IMPORT</iconId> - <stateProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js</stateProcess> - </entityActionField> - <entityActionField> - <name>IntegrateCurrentIntoSelectedAction</name> - <title>Integrate current into selected contact</title> - <onActionProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/onActionProcess.js</onActionProcess> - <isMenuAction v="true" /> - <isObjectAction v="false" /> - <isSelectionAction v="true" /> - <iconId>NEON:EXPORT</iconId> - <stateProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js</stateProcess> - </entityActionField> - <entityActionField> - <name>IgnoreDuplicate</name> - <title>Ignore Duplicate</title> - <onActionProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js</onActionProcess> - <isMenuAction v="true" /> - <isObjectAction v="false" /> - <isSelectionAction v="true" /> - <iconId>VAADIN:CLOSE</iconId> - <stateProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js</stateProcess> - </entityActionField> - <entityActionField> - <name>IgnoreWholeCluster</name> - <title>Ignore whole cluster</title> - <onActionProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js</onActionProcess> - <isObjectAction v="false" /> - <iconId>VAADIN:CLOSE</iconId> - <stateProcess>%aditoprj%/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js</stateProcess> - </entityActionField> - </children> - </entityActionGroup> <entityActionField> <name>newEmail</name> <title>Write email</title> @@ -1499,6 +1319,26 @@ <iconId>VAADIN:CURLY_BRACKETS</iconId> <stateProcess>%aditoprj%/entity/Person_entity/entityfields/openadminview/stateProcess.js</stateProcess> </entityActionField> + <entityParameter> + <name>FilterPreSet_param</name> + <expose v="true" /> + </entityParameter> + <entityConsumer> + <name>Duplicates</name> + <selectionMode>MULTI</selectionMode> + <selectionModeProcess>%aditoprj%/entity/Person_entity/entityfields/duplicates/selectionModeProcess.js</selectionModeProcess> + <dependency> + <name>dependency</name> + <entityName>DuplicatePerson_entity</entityName> + <fieldName>#PROVIDER</fieldName> + </dependency> + <children> + <entityParameter> + <name>Obj_param</name> + <valueProcess>%aditoprj%/entity/Person_entity/entityfields/duplicates/children/obj_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> <entityConsumer> <name>ContactCommunicationSettings</name> <refreshParent v="true" /> @@ -1703,11 +1543,13 @@ </dbRecordFieldMapping> <dbRecordFieldMapping> <name>STANDARD_EMAIL_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js</expression> <isFilterable v="false" /> <isLookupFilter v="false" /> </dbRecordFieldMapping> <dbRecordFieldMapping> <name>STANDARD_PHONE_COMMUNICATION.value</name> + <expression>%aditoprj%/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js</expression> <isFilterable v="false" /> <isLookupFilter v="false" /> </dbRecordFieldMapping> @@ -1857,6 +1699,13 @@ <filterConditionProcess>%aditoprj%/entity/Person_entity/recordcontainers/db/filterextensions/supervisorassignment/filterConditionProcess.js</filterConditionProcess> <filtertype>BASIC</filtertype> </filterExtension> + <filterExtension> + <name>Duplicates_filter</name> + <title>Duplicates</title> + <contentType>NUMBER</contentType> + <filterConditionProcess>%aditoprj%/entity/Person_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js</filterConditionProcess> + <filtertype>BASIC</filtertype> + </filterExtension> <filterExtension> <name>Communication_Mail_filter</name> <title>Communication: Mail</title> diff --git a/entity/Person_entity/entityfields/deletefunction/onActionProcess.js b/entity/Person_entity/entityfields/deletefunction/onActionProcess.js index 083e2d8b0d3d0649e0984e06b3947a6ae709fd3e..324c438c9d7953e5177748efa8679fe55ab0e8f6 100644 --- a/entity/Person_entity/entityfields/deletefunction/onActionProcess.js +++ b/entity/Person_entity/entityfields/deletefunction/onActionProcess.js @@ -21,7 +21,7 @@ if(contactIds > 1) entities.deleteRow(config); - DuplicateScannerUtils.deleteCachedDuplicate(contactId); + DuplicateScannerUtils.deleteHasDuplicateEntries("Person_entity", [contactId]); new AttributeRelationQuery(contactId, null, ContextUtils.getCurrentContextId()) .deleteAllAttributes(); diff --git a/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js b/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js deleted file mode 100644 index c9289cbd0ef896d7dbcf5eae668bac8a87e861da..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/onActionProcess.js +++ /dev/null @@ -1,10 +0,0 @@ -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 diff --git a/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js b/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js deleted file mode 100644 index b736eb15f5b18e779e37281fe216c9297ca86191..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/children/ignoreduplicate/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 != "1")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js b/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js deleted file mode 100644 index f74997cb1d72c3baa56b1dd12a42ba77eb782769..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/onActionProcess.js +++ /dev/null @@ -1,28 +0,0 @@ -import("system.logging"); -import("system.neon"); -import("system.vars"); -import("DuplicateScanner_lib"); -import("system.notification"); - -let contactId = vars.get("$field.CONTACTID"); -logging.log("contactId -> " + contactId); -let clusterId = DuplicateScannerUtils.getClusterId(contactId); -logging.log("clusterId -> " + clusterId); -let duplicateContactIdsInClusterRay = DuplicateScannerUtils.getCachedDuplicatesForClusterId(clusterId) -logging.log("duplicateContactIdsInClusterRay -> " + duplicateContactIdsInClusterRay); -if(duplicateContactIdsInClusterRay.length > 1) -{ - let referenceDuplicateId = duplicateContactIdsInClusterRay[0]; - for (let i = 1; i < duplicateContactIdsInClusterRay.length; i++) - { - DuplicateScannerUtils.createUnrelatedDuplicateRelation(referenceDuplicateId, duplicateContactIdsInClusterRay[i], clusterId); - } - neon.refreshAll(); - -// var params = {}; -// params["TargetEntity"] = "Person_entity"; -// -// neon.openContext("Duplicates", "DuplicatesOverview_view", null, neon.OPERATINGSTATE_VIEW, params); -} - - diff --git a/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js b/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js deleted file mode 100644 index fdd913ad06bf2815da3127d405d21b258c4ad795..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/children/ignorewholecluster/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js b/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js deleted file mode 100644 index b736eb15f5b18e779e37281fe216c9297ca86191..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/children/integratecurrentintoselectedaction/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 != "1")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js b/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js deleted file mode 100644 index b736eb15f5b18e779e37281fe216c9297ca86191..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/children/integrateselectedintocurrentaction/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 != "1")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Person_entity/entityfields/duplicateactions/stateProcess.js b/entity/Person_entity/entityfields/duplicateactions/stateProcess.js deleted file mode 100644 index 5b8b24021ddfcba34a258bf7af7d4ec5ec65852a..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/duplicateactions/stateProcess.js +++ /dev/null @@ -1,11 +0,0 @@ -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 == "0")//todo replace with keyword - result.string(neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/Person_entity/entityfields/duplicates/children/obj_param/valueProcess.js b/entity/Person_entity/entityfields/duplicates/children/obj_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..59dc755e2ab02e6af201c71f835676e5172180bc --- /dev/null +++ b/entity/Person_entity/entityfields/duplicates/children/obj_param/valueProcess.js @@ -0,0 +1,23 @@ +import("system.vars"); +import("system.result"); + +result.string(JSON.stringify({ + CONTACTID: vars.get("$field.CONTACTID"), + FIRSTNAME: vars.get("$field.FIRSTNAME"), + MIDDLENAME: vars.get("$field.MIDDLENAME"), + LASTNAME: vars.get("$field.LASTNAME"), + DATEOFBIRTH: vars.get("$field.DATEOFBIRTH"), + GENDER: vars.get("$field.GENDER"), + SALUTATION: vars.get("$field.SALUTATION"), + TITLE: vars.get("$field.TITLE"), + TITLESUFFIX: vars.get("$field.TITLESUFFIX"), + POSITION: vars.get("$field.POSITION"), + STATUS: vars.get("$field.STATUS"), + DEPARTMENT: vars.get("$field.DEPARTMENT"), + STANDARD_ADDRESS: vars.get("$field.STANDARD_ADDRESS"), + STANDARD_CITY: vars.get("$field.STANDARD_CITY"), + STANDARD_COUNTRY: vars.get("$field.STANDARD_COUNTRY"), + STANDARD_EMAIL_COMMUNICATION: vars.get("$field.STANDARD_EMAIL_COMMUNICATION"), + STANDARD_PHONE_COMMUNICATION: vars.get("$field.STANDARD_PHONE_COMMUNICATION"), + STANDARD_ZIP: vars.get("$field.STANDARD_ZIP") +})); diff --git a/entity/Person_entity/entityfields/duplicates/selectionModeProcess.js b/entity/Person_entity/entityfields/duplicates/selectionModeProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..c92317456ba1a7476401171e875526c8eff6fa76 --- /dev/null +++ b/entity/Person_entity/entityfields/duplicates/selectionModeProcess.js @@ -0,0 +1,9 @@ +import("system.result"); +import("system.vars"); +import("system.neon"); + +result.string( + vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_NEW || + vars.get("$sys.operatingstate") == neon.OPERATINGSTATE_EDIT ? + "NONE" : "MULTI" +); diff --git a/entity/Person_entity/entityfields/nonselfduplicates/documentation.adoc b/entity/Person_entity/entityfields/nonselfduplicates/documentation.adoc deleted file mode 100644 index d7964dadc0c8a8c7d5ea6856c3b82d07ccbcc361..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/nonselfduplicates/documentation.adoc +++ /dev/null @@ -1,3 +0,0 @@ -Provides contact duplicate-records without the `Person_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 diff --git a/entity/Person_entity/entityfields/selfduplicates/documentation.adoc b/entity/Person_entity/entityfields/selfduplicates/documentation.adoc deleted file mode 100644 index 4f02d8b1a256bb4d0a791bab46cf0cda23790c84..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/selfduplicates/documentation.adoc +++ /dev/null @@ -1 +0,0 @@ -Provides contact duplicate-records within the `Person_entity` scope itself. \ No newline at end of file diff --git a/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js b/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js deleted file mode 100644 index 81570217c3ecd3cf76deb5e14d7ac0a6121a59bb..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicateactionscontrol_param/valueProcess.js +++ /dev/null @@ -1,2 +0,0 @@ -import("system.result"); -result.string("1");//todo use keyword \ No newline at end of file diff --git a/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js b/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js deleted file mode 100644 index 821415ae694dd9c3d98a7703f4c59b81a37f524b..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/selfduplicatesuncached/children/duplicatecurrentcontactid_param/valueProcess.js +++ /dev/null @@ -1,3 +0,0 @@ -import("system.vars"); -import("system.result"); -result.string(vars.get("$field.CONTACTID")); \ No newline at end of file diff --git a/entity/Person_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js b/entity/Person_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js deleted file mode 100644 index 849632882bbabd4e65dfb25c7dc984366bde5116..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/selfduplicatesuncached/children/excludedcontactids_param/valueProcess.js +++ /dev/null @@ -1,7 +0,0 @@ -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 diff --git a/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js b/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js deleted file mode 100644 index 8fa370156eae601ebddb1f7b6f024650a156de52..0000000000000000000000000000000000000000 --- a/entity/Person_entity/entityfields/selfduplicatesuncached/children/onlyshowcontactids_param/valueProcess.js +++ /dev/null @@ -1,60 +0,0 @@ -import("system.indexsearch"); -import("system.project"); -import("system.logging"); -import("system.vars"); -import("DuplicateScanner_lib"); -import("system.result"); - -var scannerName = "PersonDuplicates"; -var targetEntity = "Person_entity"; -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.STANDARD_CITY"); - vars.get("$field.STANDARD_ZIP"); - vars.get("$field.STANDARD_ADDRESS"); - vars.get("$field.FIRSTNAME"); - vars.get("$field.LASTNAME"); - vars.get("$field.PersAddresses.insertedRows") - vars.get("$field.PersAddresses.changedRows") - vars.get("$field.PersAddresses.deletedRows") - - 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 (duplicate) - { - return duplicate[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 diff --git a/entity/Person_entity/initFilterProcess.js b/entity/Person_entity/initFilterProcess.js index f64ef8977cc3f20a910f104d6196bfe90c43fc34..afecc1ede02d6f412681545610696b1efa17dc90 100644 --- a/entity/Person_entity/initFilterProcess.js +++ b/entity/Person_entity/initFilterProcess.js @@ -1,14 +1,14 @@ -import("system.entities"); +import("system.neon"); +import("system.vars"); import("Keyword_lib"); import("KeywordRegistry_basic"); import("system.result"); -import("system.vars"); -import("system.neon"); -if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER) +var filter = vars.get("$param.FilterPreSet_param"); +if(!filter && vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER) { var statusInactive = $KeywordRegistry.contactStatus$inactive(); - var filter = { + filter = JSON.stringify({ type: "group", operator: "AND", childs: [{ @@ -19,7 +19,9 @@ if (vars.get("$sys.presentationmode") === neon.CONTEXT_PRESENTATIONMODE_FILTER) key: statusInactive, value: KeywordUtils.getViewValue($KeywordRegistry.contactStatus(), statusInactive) }] - }; - - result.string(JSON.stringify(filter)); -} \ No newline at end of file + }); +} +if(filter) +{ + result.string(filter); +} diff --git a/entity/Person_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js b/entity/Person_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..6a53c389f9916191793616c6e838f781cd140e7c --- /dev/null +++ b/entity/Person_entity/recordcontainers/db/filterextensions/duplicates_filter/filterConditionProcess.js @@ -0,0 +1,16 @@ +import("system.result"); +import("Sql_lib"); +import("system.vars"); +import("DuplicateScanner_lib"); + +var sqlOperator = SqlUtils.getSqlConditionalOperator(vars.get("$local.operator")); + +result.string(newWhere( + "CONTACT.CONTACTID", + DuplicateScannerUtils.getDuplicateConditionalListSql( + ["Person_entity"], + vars.get("$local.rawvalue"), + sqlOperator, true + ), + SqlBuilder.IN() +)); diff --git a/entity/Person_entity/recordcontainers/db/onDBDelete.js b/entity/Person_entity/recordcontainers/db/onDBDelete.js index 56d4461823493222635f99ab42de361ec497f838..26d095805ed1fc2a0f944c3a99c4f7e49475057a 100644 --- a/entity/Person_entity/recordcontainers/db/onDBDelete.js +++ b/entity/Person_entity/recordcontainers/db/onDBDelete.js @@ -6,7 +6,7 @@ import("system.vars"); import("DuplicateScanner_lib"); var contactId = vars.get("$field.CONTACTID"); -DuplicateScannerUtils.deleteCachedDuplicate(contactId); +DuplicateScannerUtils.deleteHasDuplicateEntries("Person_entity", [contactId]); new AttributeRelationQuery(contactId, null, ContextUtils.getCurrentContextId()) .deleteAllAttributes(); diff --git a/entity/Person_entity/recordcontainers/db/onDBInsert.js b/entity/Person_entity/recordcontainers/db/onDBInsert.js index 3385c553d10f5332ad4c946ea239bf7bcdf4b6bc..8d1c5ddea0bed7f36862a8423508f130794a3a07 100644 --- a/entity/Person_entity/recordcontainers/db/onDBInsert.js +++ b/entity/Person_entity/recordcontainers/db/onDBInsert.js @@ -4,6 +4,9 @@ import("Sql_lib"); import("system.db"); import("DataPrivacy_lib"); import("system.vars"); +import("DuplicateScanner_lib"); + +DuplicateScannerUtils.updateHasDuplicateEntry("Person_entity"); //let targetEntity = "Person_entity"; let contactId = vars.get("$local.uid"); diff --git a/entity/Person_entity/recordcontainers/db/onDBUpdate.js b/entity/Person_entity/recordcontainers/db/onDBUpdate.js index 0c29899fd4393c2528219ef9dc213eb151d10b18..100f1f714f83179a745ab4c332591ab988c0e60b 100644 --- a/entity/Person_entity/recordcontainers/db/onDBUpdate.js +++ b/entity/Person_entity/recordcontainers/db/onDBUpdate.js @@ -8,6 +8,9 @@ import("Person_lib"); import("Communication_lib"); import("Entity_lib"); import("StandardObject_lib"); +import("DuplicateScanner_lib"); + +DuplicateScannerUtils.updateHasDuplicateEntry("Person_entity"); var localChanged = vars.get("$local.changed"); var orgChanged = false; diff --git a/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js b/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..41ef07ff58ec51eda018bed12cb5b0b2b657c348 --- /dev/null +++ b/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_email_communication.value/expression.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Communication_lib"); + +var res = CommUtil.getStandardSubSqlMail(); +result.string(res); \ No newline at end of file diff --git a/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js b/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..320ae40ad1ba794b4e759037fa0f382b9af67696 --- /dev/null +++ b/entity/Person_entity/recordcontainers/db/recordfieldmappings/standard_phone_communication.value/expression.js @@ -0,0 +1,5 @@ +import("system.result"); +import("Communication_lib"); + +var res = CommUtil.getStandardSubSqlPhone(); +result.string(res); \ No newline at end of file diff --git a/entity/QuickEntry_entity/QuickEntry_entity.aod b/entity/QuickEntry_entity/QuickEntry_entity.aod index 9ce66331b3242cbbba9001036cd3e65dc920d156..2c460f59b8748cc6d79d1046db867b5d3614130c 100644 --- a/entity/QuickEntry_entity/QuickEntry_entity.aod +++ b/entity/QuickEntry_entity/QuickEntry_entity.aod @@ -237,6 +237,7 @@ <entityParameter> <name>ContactIds_param</name> <valueProcess>%aditoprj%/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js</valueProcess> + <documentation>%aditoprj%/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/documentation.adoc</documentation> </entityParameter> <entityParameter> <name>WithPrivatePersons_param</name> diff --git a/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/documentation.adoc b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/documentation.adoc new file mode 100644 index 0000000000000000000000000000000000000000..667cdd59e70d81d319b110ff44e24478616dae1d --- /dev/null +++ b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/documentation.adoc @@ -0,0 +1,2 @@ +returns a list of duplicate ids +the index will be searched via the values entered in the edit frame \ No newline at end of file diff --git a/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js index 68ef6879ab48622e909ec092bb0314156746f96e..98b27caa01f480c5b39a7c62f68970a13519812b 100644 --- a/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js +++ b/entity/QuickEntry_entity/entityfields/organdpersduplicates/children/contactids_param/valueProcess.js @@ -2,6 +2,7 @@ import("system.indexsearch"); import("system.vars"); import("DuplicateScanner_lib"); import("system.result"); +import("IndexSearch_lib"); //trigger refresh vars.get("$field.FIRSTNAME"); @@ -11,11 +12,11 @@ var uid = vars.get("$field.UID"); var idsForEmptyResult = JSON.stringify(["nodata"]); var duplicateScans = []; -duplicateScans.push(["PersonDuplicates", "Person_entity", {"CONTACTID" : uid}]); +duplicateScans.push(["Person_entity", {"CONTACTID" : uid}]); vars.get("$field.Contacts.insertedRows").forEach(function (contact) { - duplicateScans.push(["PersonDuplicates", "Person_entity", contact]); + duplicateScans.push(["Person_entity", contact]); }); var organisationName = vars.get("$field.ORGANISATION_NAME"); @@ -34,7 +35,7 @@ if (organisationName || firstOrganisationAddress) address = firstOrganisationAddress["ADDRESS"]; } - duplicateScans.push(["OrganisationDuplicates", "Organisation_entity", { + duplicateScans.push(["Organisation_entity", { "CONTACTID" : uid, "NAME" : organisationName, "STANDARD_CITY" : city, @@ -43,9 +44,9 @@ if (organisationName || firstOrganisationAddress) }]); } -var duplicates = duplicateScans.reduce(function (duplicateArr, [scannerName, entity, fieldValues]) +var duplicates = duplicateScans.reduce(function (duplicateArr, [entity, fieldValues]) { - return duplicateArr.concat(_getDuplicates(scannerName, entity, fieldValues)); + return duplicateArr.concat(_getDuplicates(entity, fieldValues)); }, []); if (duplicates.length === 0) @@ -54,34 +55,22 @@ else result.string(JSON.stringify(duplicates)); -function _getDuplicates (pScannerName, pEntity, pEntityFieldValues) +function _getDuplicates(pEntity, pEntityFieldValues) { - var fieldsToLoad = DuplicateScannerUtils.getEntityFieldObjectFromConfig(pScannerName, pEntity); - if (fieldsToLoad == null) - return []; + var scanner = DuplicateScannerUtils.getScannerByEntity(pEntity); + var indexsearchFilter = IndexsearchFilterUtils.fromFilter(scanner.filter); + var entityFields = indexsearchFilter.getFields(); + entityFields.add(scanner.idField); var valuesToCheck = {}; - - var allFieldsToLoad = fieldsToLoad.entityFields.concat(fieldsToLoad.entityIdField); - allFieldsToLoad.forEach(function (field) - { + entityFields.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]; + : vars.exists("$field." + field) ? vars.get("$field." + field) : ""; + valuesToCheck[field] = fieldValue || ""; }); - return duplicateIds; + var indexPattern = indexsearchFilter.buildQuery(valuesToCheck); + var duplicateIds = DuplicateScannerUtils.getDuplicateIds(pEntity, indexPattern, valuesToCheck[scanner.idField]); + return DuplicateScannerUtils.filterIgnored(pEntity, valuesToCheck[scanner.idField], duplicateIds); } diff --git a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod index 1fc9a21e9d608dff16a653d76fef388a63123cab..1cf98cfdedf9bc9697cc8ae637205adb68bc21b2 100644 --- a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod +++ b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod @@ -8076,6 +8076,12 @@ <entry> <key>EML files can't be edited here. You can download, edit and reupload the template to change the content.</key> </entry> + <entry> + <key>The duplicate row corrosponding to %0 has been rebuild</key> + </entry> + <entry> + <key>Duplicaterow rebuild</key> + </entry> <entry> <key>Edit HTML</key> </entry> @@ -8406,6 +8412,28 @@ <entry> <key>Redirect needs a full Url with http/https</key> </entry> + <entry> + <key>${IGNORE_DUPLICATE}</key> + </entry> + <entry> + <key>${UNIGNORE_DUPLICATE}</key> + </entry> + <entry> + <key>View all duplicates from this scanner</key> + </entry> + <entry> + <key>Rebuild all duplicates from the selected scanners</key> + </entry> + <entry> + <key>View duplicates</key> + </entry> + <entry> + <key>Rebuild selected entries</key> + </entry> + <entry> + <key>Ignored</key> + <value>Ignoriert</value> + </entry> <entry> <key>Not enough room in campaignstep</key> </entry> diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod index 3a6dbc11f1f96b584186cc24f0c6b29d832e1b0c..c239d448334a65386356e07724d1066007037fb4 100644 --- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod +++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod @@ -10984,6 +10984,14 @@ Bitte Datumseingabe prüfen</value> <entry> <key>Calendar week</key> </entry> + <entry> + <key>Duplicaterow rebuild</key> + <value>Dubletten neu berechnet</value> + </entry> + <entry> + <key>The duplicate row corrosponding to %0 has been rebuild</key> + <value>Die Dupletten des %0 filters wurden neu berechnet</value> + </entry> <entry> <key>HTML Editor</key> </entry> @@ -11035,6 +11043,34 @@ Bitte Datumseingabe prüfen</value> <key>Internal responsible</key> <value>Intern: Gebietsverantwortlich</value> </entry> + <entry> + <key>${IGNORE_DUPLICATE}</key> + <value>Datensatz ist keine Dublette</value> + </entry> + <entry> + <key>${UNIGNORE_DUPLICATE}</key> + <value>Datensatz ist eine Dublette</value> + </entry> + <entry> + <key>Rebuild selected entries</key> + <value>Ausgewählte Einträge neu aufbauen</value> + </entry> + <entry> + <key>Rebuild all duplicates from the selected scanners</key> + <value>Ausgewählte Einträge neu aufbauen</value> + </entry> + <entry> + <key>View duplicates</key> + <value>Dubletten anzeigen</value> + </entry> + <entry> + <key>View all duplicates from this scanner</key> + <value>Alle Duplikate dieses Scanners anzeigen</value> + </entry> + <entry> + <key>Ignored</key> + <value>Ignoriert</value> + </entry> <entry> <key>Invalid attribute count</key> <value>Unzulässige Attributanzahl</value> diff --git a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod index 775c68dacdaa8027b09cc035d8132b22dd07b3d7..4f213025460dede50cbccfd744b40eaa5a74b697 100644 --- a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod +++ b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod @@ -8325,6 +8325,9 @@ <entry> <key>Invalid value</key> </entry> + <entry> + <key>Object not found</key> + </entry> <entry> <key>Value is too big, the maximum is %0</key> </entry> @@ -8490,6 +8493,29 @@ <entry> <key>Redirect needs a full Url with http/https</key> </entry> + <entry> + <key>${IGNORE_DUPLICATE}</key> + <value>Record is not a duplicate</value> + </entry> + <entry> + <key>${UNIGNORE_DUPLICATE}</key> + <value>Record is a duplicate</value> + </entry> + <entry> + <key>View all duplicates from this scanner</key> + </entry> + <entry> + <key>Rebuild all duplicates from the selected scanners</key> + </entry> + <entry> + <key>View duplicates</key> + </entry> + <entry> + <key>Rebuild selected entries</key> + </entry> + <entry> + <key>Ignored</key> + </entry> <entry> <key>Not enough room in campaignstep</key> </entry> diff --git a/neonContext/DuplicateOrganisation/DuplicateOrganisation.aod b/neonContext/DuplicateOrganisation/DuplicateOrganisation.aod new file mode 100644 index 0000000000000000000000000000000000000000..180a84c994ec66d9360520047ed577612fce6b84 --- /dev/null +++ b/neonContext/DuplicateOrganisation/DuplicateOrganisation.aod @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1"> + <name>DuplicateOrganisation</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <previewView>DuplicateOrganisationPreview_view</previewView> + <entity>DuplicateOrganisation_entity</entity> + <references> + <neonViewReference> + <name>c62620c2-33e3-4b76-bd76-0c1be1d516eb</name> + <view>DuplicateOrganisationFilter_view</view> + </neonViewReference> + <neonViewReference> + <name>512ef5c4-dc95-4ef1-81e0-d45479249c2e</name> + <view>DuplicateOrganisationEdit_view</view> + </neonViewReference> + <neonViewReference> + <name>07bbb126-8b4b-4f3c-a2c8-30409e0ee826</name> + </neonViewReference> + <neonViewReference> + <name>2e59d859-6b5f-4bb5-a47b-1225164140ed</name> + <view>DuplicateOrganisationPreview_view</view> + </neonViewReference> + </references> +</neonContext> diff --git a/neonContext/DuplicatePerson/DuplicatePerson.aod b/neonContext/DuplicatePerson/DuplicatePerson.aod new file mode 100644 index 0000000000000000000000000000000000000000..6d8e12f75aafc815feb17c94b9165e142be633e6 --- /dev/null +++ b/neonContext/DuplicatePerson/DuplicatePerson.aod @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1"> + <name>DuplicatePerson</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <previewView>DuplicatePersonPreview_view</previewView> + <entity>DuplicatePerson_entity</entity> + <references> + <neonViewReference> + <name>930c8a65-e92e-4222-9ca7-cbc663ebf274</name> + <view>DuplicatePersonFilter_view</view> + </neonViewReference> + <neonViewReference> + <name>f54e544a-2279-41ec-a7b3-44e11989a736</name> + <view>DuplicatePersonEdit_view</view> + </neonViewReference> + <neonViewReference> + <name>88155af6-074d-4683-b27e-20a8ef75dd8a</name> + </neonViewReference> + <neonViewReference> + <name>fb801ec5-ec73-4d6d-be3e-f2d78a57716f</name> + </neonViewReference> + <neonViewReference> + <name>596cd813-22e1-4ba6-96d5-fefefbb800cf</name> + <view>DuplicatePersonPreview_view</view> + </neonViewReference> + </references> +</neonContext> diff --git a/neonContext/DuplicateScannerResultFieldConfig/DuplicateScannerResultFieldConfig.aod b/neonContext/DuplicateScannerResultFieldConfig/DuplicateScannerResultFieldConfig.aod deleted file mode 100644 index 79d97363fbb75d25de88168f1dbd76713df82073..0000000000000000000000000000000000000000 --- a/neonContext/DuplicateScannerResultFieldConfig/DuplicateScannerResultFieldConfig.aod +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1"> - <name>DuplicateScannerResultFieldConfig</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <entity>DuplicateScannerResultFieldConfig_entity</entity> - <references> - <neonViewReference> - <name>4bab12e9-c4c3-450f-bfcf-8ecbec1f994c</name> - <view>DuplicateScannerResultFieldConfigEdit_view</view> - </neonViewReference> - </references> -</neonContext> diff --git a/neonContext/Duplicates/Duplicates.aod b/neonContext/Duplicates/Duplicates.aod deleted file mode 100644 index 5de0f3a1913c9bb49aecba2c8ea9998e23ad1faf..0000000000000000000000000000000000000000 --- a/neonContext/Duplicates/Duplicates.aod +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1"> - <name>Duplicates</name> - <title>Duplicates</title> - <majorModelMode>DISTRIBUTED</majorModelMode> - <filterView>DuplicatesOverview_view</filterView> - <entity>Duplicates_entity</entity> - <references> - <neonViewReference> - <name>d8994f4c-3abf-4ff1-8bdb-f12e527df655</name> - <view>PersonDuplicatesFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>de287a3b-1d6a-435c-b65e-d833c1751edf</name> - <view>DuplicatesOverview_view</view> - </neonViewReference> - <neonViewReference> - <name>5bdeb931-4e69-4520-bbc9-94fb17679331</name> - <view>PersonDublicatesTab_view</view> - </neonViewReference> - <neonViewReference> - <name>70cb2e96-5bc1-46cb-982f-b27db5d143d2</name> - <view>PersonClusterMain_view</view> - </neonViewReference> - <neonViewReference> - <name>7cdb6ca7-e99d-4eb6-897a-0953157bf62f</name> - <view>DuplicatesUnrelatedCluster_view</view> - </neonViewReference> - <neonViewReference> - <name>4b9a1a26-e14f-4246-b474-8bfb3e3a95b0</name> - <view>OrganisationDuplicatesTab_view</view> - </neonViewReference> - <neonViewReference> - <name>f9b46eab-7417-4f61-b7cd-dc772c04ddc0</name> - <view>OrganisationDuplicatesFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>d68a425a-037a-4725-8dc1-b0afac277bdd</name> - <view>OrganisationUnrelatedDuplicates_view</view> - </neonViewReference> - <neonViewReference> - <name>4c1fc406-1c83-4fb6-9059-e45ff5c80756</name> - <view>OrganisationClusterMain_view</view> - </neonViewReference> - <neonViewReference> - <name>2400acfd-50e4-472d-b69c-368b9d25b6c6</name> - <view>PersonClusterPreview_view</view> - </neonViewReference> - </references> -</neonContext> diff --git a/neonContext/DuplicatesUnrelated/DuplicatesUnrelated.aod b/neonContext/DuplicatesUnrelated/DuplicatesUnrelated.aod deleted file mode 100644 index 56bf4fd8574682d13f54fd2c5628fde361465b46..0000000000000000000000000000000000000000 --- a/neonContext/DuplicatesUnrelated/DuplicatesUnrelated.aod +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1"> - <name>DuplicatesUnrelated</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <entity>DuplicatesUnrelated_entity</entity> - <references> - <neonViewReference> - <name>0b5cbd31-cfa1-4ee8-8bd2-b4772ac95953</name> - <view>DuplicatesUnrelatedPersonFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>433a665e-63ac-4281-8f7c-08603362daf7</name> - <view>DuplicatesUnrelatedOrganisationFilter_view</view> - </neonViewReference> - </references> -</neonContext> diff --git a/neonContext/Person/Person.aod b/neonContext/Person/Person.aod index 1e80db9a3c4e51a1331d5a9a15196dde803ed08f..bc1cf78630928ad1994e887836ce2aaba9e2ca81 100644 --- a/neonContext/Person/Person.aod +++ b/neonContext/Person/Person.aod @@ -61,7 +61,6 @@ </neonViewReference> <neonViewReference> <name>2a6c44be-53e2-469e-84b3-615841bd4430</name> - <view>PersonDuplicateEditview_view</view> </neonViewReference> <neonViewReference> <name>627518cc-15b0-4f0d-b6f3-ec06172e7c4e</name> diff --git a/neonView/PersonDuplicateEditview_view/PersonDuplicateEditview_view.aod b/neonView/DuplicateOrganisationEdit_view/DuplicateOrganisationEdit_view.aod similarity index 55% rename from neonView/PersonDuplicateEditview_view/PersonDuplicateEditview_view.aod rename to neonView/DuplicateOrganisationEdit_view/DuplicateOrganisationEdit_view.aod index b331c5900d3b446f8632159bd837bb4968b85ef4..a2d93541e4f43880901a1cf4e3caad407d2b8ea1 100644 --- a/neonView/PersonDuplicateEditview_view/PersonDuplicateEditview_view.aod +++ b/neonView/DuplicateOrganisationEdit_view/DuplicateOrganisationEdit_view.aod @@ -1,6 +1,6 @@ <?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>PersonDuplicateEditview_view</name> + <name>DuplicateOrganisationEdit_view</name> <majorModelMode>DISTRIBUTED</majorModelMode> <layout> <noneLayout> @@ -9,38 +9,38 @@ </layout> <children> <tableViewTemplate> - <name>PersonDuplicateEditview_Table</name> + <name>table</name> <hideActions v="true" /> - <hideContentSearch v="true" /> - <iconField></iconField> - <entityField>#ENTITY</entityField> - <linkedColumns /> - <fixedFilterFields /> - <hideHeader v="true" /> - <title>Duplicates</title> + <isCreatable v="false" /> + <isDeletable v="false" /> + <isEditable v="false" /> <columns> <neonTableColumn> - <name>7e5410c5-ee05-4a5a-b168-8015d2d05834</name> - <entityField>#IMAGE</entityField> + <name>7cb9aa93-5aa1-4f52-a2a0-32723919af08</name> + <entityField>ORGNAME</entityField> </neonTableColumn> <neonTableColumn> - <name>ef22c1b8-1025-41b6-bb0b-99fe053e9ef9</name> - <entityField>FIRSTNAME</entityField> + <name>683fa228-8853-4def-b904-c10222136aec</name> + <entityField>STATUS</entityField> </neonTableColumn> <neonTableColumn> - <name>abfe3ce4-4fcc-4d61-93ec-156bd97ace3b</name> - <entityField>LASTNAME</entityField> + <name>bb6ddf6b-aa09-4b5b-814a-ae5ebf08a48a</name> + <entityField>CUSTOMERCODE</entityField> </neonTableColumn> <neonTableColumn> - <name>17d892a8-69fb-4bca-9629-d92ae80c72f8</name> - <entityField>STANDARD_PHONE_COMMUNICATION</entityField> + <name>a1630a4a-9989-4e9b-bb39-03a8432aef3f</name> + <entityField>TYPE</entityField> </neonTableColumn> <neonTableColumn> - <name>12006c11-e833-4ce1-9645-fdec0a8b6088</name> + <name>6295ed5c-35a8-4238-921e-65e29141883f</name> <entityField>STANDARD_EMAIL_COMMUNICATION</entityField> </neonTableColumn> <neonTableColumn> - <name>0231907e-8ae0-4f99-af1c-1bcb2f57bdb8</name> + <name>d6716d9e-19b7-4d2f-b7a9-61d0e3ef3ac5</name> + <entityField>STANDARD_PHONE_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>334a97a3-cad3-4ac5-b670-de7d587e2781</name> <entityField>STANDARD_ADDRESS</entityField> </neonTableColumn> </columns> diff --git a/neonView/DuplicateOrganisationFilter_view/DuplicateOrganisationFilter_view.aod b/neonView/DuplicateOrganisationFilter_view/DuplicateOrganisationFilter_view.aod new file mode 100644 index 0000000000000000000000000000000000000000..2e4b88985d02932f1eb78ced263ceb82b88c2adf --- /dev/null +++ b/neonView/DuplicateOrganisationFilter_view/DuplicateOrganisationFilter_view.aod @@ -0,0 +1,58 @@ +<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> + <name>DuplicateOrganisationFilter_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <filterable v="true" /> + <layout> + <groupLayout> + <name>layout</name> + </groupLayout> + </layout> + <children> + <tableViewTemplate> + <name>table</name> + <favoriteActionGroup1>filterActions</favoriteActionGroup1> + <isCreatable v="false" /> + <isDeletable v="false" /> + <isEditable v="false" /> + <columns> + <neonTableColumn> + <name>04253f56-c764-46a9-8795-6b7c58d9352b</name> + <entityField>PICTURE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>fc700556-7829-42be-b824-83d29e7dddf3</name> + <entityField>DUPLICATE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>6e753d4c-4f6a-4fbf-8af0-b76aa4ff8e76</name> + <entityField>ORGNAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>1368091a-0a65-4df6-b0fc-34031925e2bf</name> + <entityField>STATUS</entityField> + </neonTableColumn> + <neonTableColumn> + <name>d407d879-3f44-4656-a4bc-0149adcfec5e</name> + <entityField>CUSTOMERCODE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>166bdb5d-a822-4915-99aa-2ff1adca1bc9</name> + <entityField>TYPE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>4edfc696-78b0-4abd-8d03-39c02d0116da</name> + <entityField>STANDARD_PHONE_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>e7fb62b8-4d76-46cc-bf84-00230d2e6e56</name> + <entityField>STANDARD_EMAIL_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>93bc9275-aea3-436d-ab86-fd400a790538</name> + <entityField>STANDARD_ADDRESS</entityField> + </neonTableColumn> + </columns> + </tableViewTemplate> + </children> +</neonView> diff --git a/neonView/DuplicatesUnrelatedCluster_view/DuplicatesUnrelatedCluster_view.aod b/neonView/DuplicateOrganisationPreview_view/DuplicateOrganisationPreview_view.aod similarity index 51% rename from neonView/DuplicatesUnrelatedCluster_view/DuplicatesUnrelatedCluster_view.aod rename to neonView/DuplicateOrganisationPreview_view/DuplicateOrganisationPreview_view.aod index 670b2c939dc7b4c793999bf90f30c9503d4c0052..29f3a38fc42c1f47d2d21233941b8677b77ddfe7 100644 --- a/neonView/DuplicatesUnrelatedCluster_view/DuplicatesUnrelatedCluster_view.aod +++ b/neonView/DuplicateOrganisationPreview_view/DuplicateOrganisationPreview_view.aod @@ -1,17 +1,10 @@ <?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>DuplicatesUnrelatedCluster_view</name> + <name>DuplicateOrganisationPreview_view</name> <majorModelMode>DISTRIBUTED</majorModelMode> <layout> - <boxLayout> + <noneLayout> <name>layout</name> - </boxLayout> + </noneLayout> </layout> - <children> - <neonViewReference> - <name>484990d0-890b-48b8-8618-6f0fab367138</name> - <entityField>DuplicatesUnrelatedPersonConsumer</entityField> - <view>DuplicatesUnrelatedPersonFilter_view</view> - </neonViewReference> - </children> </neonView> diff --git a/neonView/DuplicatePersonEdit_view/DuplicatePersonEdit_view.aod b/neonView/DuplicatePersonEdit_view/DuplicatePersonEdit_view.aod new file mode 100644 index 0000000000000000000000000000000000000000..99e36d549f4ddd5e255077197ed28f53825b0c57 --- /dev/null +++ b/neonView/DuplicatePersonEdit_view/DuplicatePersonEdit_view.aod @@ -0,0 +1,61 @@ +<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> + <name>DuplicatePersonEdit_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <layout> + <noneLayout> + <name>layout</name> + </noneLayout> + </layout> + <children> + <tableViewTemplate> + <name>table</name> + <hideActions v="true" /> + <isCreatable v="false" /> + <isDeletable v="false" /> + <isEditable v="false" /> + <columns> + <neonTableColumn> + <name>fe9c534a-ba99-4805-996f-e76a6e77396e</name> + <entityField>LETTERSALUTATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>f8235c5b-6d13-4fd4-8522-96e38abaad1e</name> + <entityField>TITLE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>7b89e8a4-e9da-4d89-86cf-d35db86cf53c</name> + <entityField>FIRSTNAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>2b255370-1cd5-472f-8417-bde8c9f5743a</name> + <entityField>MIDDLENAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>a5188dac-9c63-44de-bb36-cb40689376c2</name> + <entityField>LASTNAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>7f0abf2a-5411-425e-8be8-16c57d19ba12</name> + <entityField>STATUS</entityField> + </neonTableColumn> + <neonTableColumn> + <name>55cdd3d2-548a-4a7f-9590-14413d0a6f34</name> + <entityField>ORGANISATION_ID</entityField> + </neonTableColumn> + <neonTableColumn> + <name>305716b3-d99b-4fea-8f8a-a71b9dbeeacd</name> + <entityField>STANDARD_EMAIL_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>446a5dea-a93f-4b1e-9b51-b8a00990972d</name> + <entityField>STANDARD_PHONE_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>1fedd9be-6492-4e89-ae8e-03bc212078e1</name> + <entityField>STANDARD_ADDRESS</entityField> + </neonTableColumn> + </columns> + </tableViewTemplate> + </children> +</neonView> diff --git a/neonView/DuplicatePersonFilter_view/DuplicatePersonFilter_view.aod b/neonView/DuplicatePersonFilter_view/DuplicatePersonFilter_view.aod new file mode 100644 index 0000000000000000000000000000000000000000..c1f6405a3cc09e5a8eda9af6abb92dcce3fa1f06 --- /dev/null +++ b/neonView/DuplicatePersonFilter_view/DuplicatePersonFilter_view.aod @@ -0,0 +1,70 @@ +<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> + <name>DuplicatePersonFilter_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <filterable v="true" /> + <layout> + <groupLayout> + <name>layout</name> + </groupLayout> + </layout> + <children> + <tableViewTemplate> + <name>table</name> + <favoriteActionGroup1>filterActions</favoriteActionGroup1> + <isCreatable v="false" /> + <isDeletable v="false" /> + <isEditable v="false" /> + <columns> + <neonTableColumn> + <name>c89d9927-c3f1-4e0c-b7fa-20d5e031f8b7</name> + <entityField>PICTURE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>c68a371e-0baa-4e6c-baee-424ca4f1d258</name> + <entityField>DUPLICATE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>42fb67ab-6bc9-4a02-9dad-10f54d9fe242</name> + <entityField>LETTERSALUTATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>59aa16e6-f599-4aff-8314-6b1a2c8cfd67</name> + <entityField>TITLE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>3fdd6165-1a1b-4521-a23f-631e5f88beb1</name> + <entityField>FIRSTNAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>a76bc050-eccd-4d2a-9190-2d994fcea7e3</name> + <entityField>MIDDLENAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>381ba7a3-1591-40a2-8837-0516db984bb7</name> + <entityField>LASTNAME</entityField> + </neonTableColumn> + <neonTableColumn> + <name>6b828d7c-7b3c-490f-98a1-41520dec0773</name> + <entityField>STATUS</entityField> + </neonTableColumn> + <neonTableColumn> + <name>47c28842-59ab-4f9c-9239-3fda9c6171e0</name> + <entityField>ORGANISATION_ID</entityField> + </neonTableColumn> + <neonTableColumn> + <name>68b9211a-b4e7-4b7b-b110-9d76d7e14bb2</name> + <entityField>STANDARD_PHONE_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>7a3fd399-0e19-4399-8df1-4f8d521b6729</name> + <entityField>STANDARD_EMAIL_COMMUNICATION</entityField> + </neonTableColumn> + <neonTableColumn> + <name>f54a6069-994c-4c77-b462-dc449e42ef49</name> + <entityField>STANDARD_ADDRESS</entityField> + </neonTableColumn> + </columns> + </tableViewTemplate> + </children> +</neonView> diff --git a/neonView/OrganisationUnrelatedDuplicates_view/OrganisationUnrelatedDuplicates_view.aod b/neonView/DuplicatePersonPreview_view/DuplicatePersonPreview_view.aod similarity index 50% rename from neonView/OrganisationUnrelatedDuplicates_view/OrganisationUnrelatedDuplicates_view.aod rename to neonView/DuplicatePersonPreview_view/DuplicatePersonPreview_view.aod index 0b23c809aafd9bedf13995b9049e1be41c8a6ea2..bd812680650a1960d3848cc068685b565b20f281 100644 --- a/neonView/OrganisationUnrelatedDuplicates_view/OrganisationUnrelatedDuplicates_view.aod +++ b/neonView/DuplicatePersonPreview_view/DuplicatePersonPreview_view.aod @@ -1,17 +1,10 @@ <?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>OrganisationUnrelatedDuplicates_view</name> + <name>DuplicatePersonPreview_view</name> <majorModelMode>DISTRIBUTED</majorModelMode> <layout> - <boxLayout> + <noneLayout> <name>layout</name> - </boxLayout> + </noneLayout> </layout> - <children> - <neonViewReference> - <name>edc0822d-4388-4e3d-93d7-2e46e32f5742</name> - <entityField>DuplicatesUnrelatedOrganisationConsumer</entityField> - <view>DuplicatesUnrelatedOrganisationFilter_view</view> - </neonViewReference> - </children> </neonView> diff --git a/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod b/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod index 5a99ae0226ac05f614f190c54732bedc624b4a5c..0a9b1256cbdc2176dacc927a459bcf2e66d599fb 100644 --- a/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod +++ b/neonView/DuplicateScannerFilter_view/DuplicateScannerFilter_view.aod @@ -11,6 +11,7 @@ <tableViewTemplate> <name>Filters</name> <entityField>#ENTITY</entityField> + <favoriteActionGroup1>FilterActions</favoriteActionGroup1> <isCreatable v="false" /> <isDeletable v="false" /> <isEditable v="true" /> @@ -23,11 +24,16 @@ <name>8615259b-de5b-493a-9c9d-2ff934ae1b8c</name> <entityField>ENTITY_TO_SCAN_NAME</entityField> </neonTableColumn> + <neonTableColumn> + <name>5ae7793f-353c-45ed-995e-5153216a3661</name> + <entityField>DUPLICATECOUNT</entityField> + </neonTableColumn> </columns> </tableViewTemplate> <treeTableViewTemplate> <name>Treetable</name> <entityField>#ENTITY</entityField> + <favoriteActionGroup1>FilterActions</favoriteActionGroup1> <isCreatable v="false" /> <isDeletable v="false" /> <columns> @@ -39,6 +45,10 @@ <name>6730d89f-4190-4767-b3a0-a63231477ed4</name> <entityField>ENTITY_TO_SCAN_NAME</entityField> </neonTreeTableColumn> + <neonTreeTableColumn> + <name>8cd35bc9-2954-4dca-b67b-083d3a27c45c</name> + <entityField>DUPLICATECOUNT</entityField> + </neonTreeTableColumn> </columns> </treeTableViewTemplate> </children> diff --git a/neonView/DuplicateScannerPreview_view/DuplicateScannerPreview_view.aod b/neonView/DuplicateScannerPreview_view/DuplicateScannerPreview_view.aod index 75c33f74429cbf649d0353217f044074c7a56a4a..bbb07db7e29b4b566072c7ca947047641f527f92 100644 --- a/neonView/DuplicateScannerPreview_view/DuplicateScannerPreview_view.aod +++ b/neonView/DuplicateScannerPreview_view/DuplicateScannerPreview_view.aod @@ -22,10 +22,6 @@ <name>b3ce81d4-bbf5-49ec-8a7a-38119ef6973f</name> <entityField>ENTITY_TO_SCAN_NAME</entityField> </entityFieldLink> - <entityFieldLink> - <name>ba6cde5d-7661-4662-9e92-dca40b597015</name> - <entityField>EXTERNAL_SERVICE_USAGE_ALLOWED</entityField> - </entityFieldLink> <entityFieldLink> <name>51bf489d-7536-4fc5-a187-8f2610b35b3d</name> <entityField>ID_FIELD_NAME</entityField> diff --git a/neonView/DuplicateScannerResultFieldConfigEdit_view/DuplicateScannerResultFieldConfigEdit_view.aod b/neonView/DuplicateScannerResultFieldConfigEdit_view/DuplicateScannerResultFieldConfigEdit_view.aod deleted file mode 100644 index 9798328f4161f04c56cd02f48166a9dce167306c..0000000000000000000000000000000000000000 --- a/neonView/DuplicateScannerResultFieldConfigEdit_view/DuplicateScannerResultFieldConfigEdit_view.aod +++ /dev/null @@ -1,24 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>DuplicateScannerResultFieldConfigEdit_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - </boxLayout> - </layout> - <children> - <genericMultipleViewTemplate> - <name>ResultFieldsConfig</name> - <autoNewRow v="true" /> - <entityField>#ENTITY</entityField> - <title>Result fields</title> - <columns> - <neonGenericMultipleTableColumn> - <name>e330572c-aa47-4c52-a760-3e8765ce3dd0</name> - <entityField>ENTITY_FIELD_NAME</entityField> - </neonGenericMultipleTableColumn> - </columns> - </genericMultipleViewTemplate> - </children> -</neonView> diff --git a/neonView/DuplicatesUnrelatedOrganisationFilter_view/DuplicatesUnrelatedOrganisationFilter_view.aod b/neonView/DuplicatesUnrelatedOrganisationFilter_view/DuplicatesUnrelatedOrganisationFilter_view.aod deleted file mode 100644 index 03a42bc169f8edf17595f673f799194149d4eb81..0000000000000000000000000000000000000000 --- a/neonView/DuplicatesUnrelatedOrganisationFilter_view/DuplicatesUnrelatedOrganisationFilter_view.aod +++ /dev/null @@ -1,30 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>DuplicatesUnrelatedOrganisationFilter_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - </boxLayout> - </layout> - <children> - <tableViewTemplate> - <name>UnrelatedOrganisations</name> - <hideContentSearch v="true" /> - <entityField>#ENTITY</entityField> - <isCreatable v="false" /> - <isEditable v="false" /> - <title>Unrelated organisation duplicates</title> - <columns> - <neonTableColumn> - <name>04681f8d-b941-4a66-be50-6ac08d6f52a4</name> - <entityField>SourceDuplicateDescription</entityField> - </neonTableColumn> - <neonTableColumn> - <name>b293ed18-ebf5-474e-8ec2-851a3562b4d5</name> - <entityField>UnrelatedDuplicateDescription</entityField> - </neonTableColumn> - </columns> - </tableViewTemplate> - </children> -</neonView> diff --git a/neonView/DuplicatesUnrelatedPersonFilter_view/DuplicatesUnrelatedPersonFilter_view.aod b/neonView/DuplicatesUnrelatedPersonFilter_view/DuplicatesUnrelatedPersonFilter_view.aod deleted file mode 100644 index 173666ab5f8a8ffb5ded2760fd5da08851e2e209..0000000000000000000000000000000000000000 --- a/neonView/DuplicatesUnrelatedPersonFilter_view/DuplicatesUnrelatedPersonFilter_view.aod +++ /dev/null @@ -1,30 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>DuplicatesUnrelatedPersonFilter_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - </boxLayout> - </layout> - <children> - <tableViewTemplate> - <name>UnrelatedPersons</name> - <hideContentSearch v="true" /> - <entityField>#ENTITY</entityField> - <isCreatable v="false" /> - <isEditable v="false" /> - <title>Unrelated person duplicates</title> - <columns> - <neonTableColumn> - <name>734re984-6a0b-4126-ab49-452e2b54f76d</name> - <entityField>SourceDuplicateDescription</entityField> - </neonTableColumn> - <neonTableColumn> - <name>8615259b-de5b-378u-945d-2ff934ae1b8c</name> - <entityField>UnrelatedDuplicateDescription</entityField> - </neonTableColumn> - </columns> - </tableViewTemplate> - </children> -</neonView> diff --git a/neonView/OrganisationClusterMain_view/OrganisationClusterMain_view.aod b/neonView/OrganisationClusterMain_view/OrganisationClusterMain_view.aod deleted file mode 100644 index 72d99cac595e86b488e878b16d1a700b90746e48..0000000000000000000000000000000000000000 --- a/neonView/OrganisationClusterMain_view/OrganisationClusterMain_view.aod +++ /dev/null @@ -1,23 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>OrganisationClusterMain_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - <direction>HORIZONTAL</direction> - </boxLayout> - </layout> - <children> - <neonViewReference> - <name>b25f94bf-9316-4438-be78-b8cf596440b3</name> - <entityField>DuplicateOrganisationsConsumer</entityField> - <view>OrganisationFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>5589bb81-171d-417b-a1b2-975144109d55</name> - <entityField>#ENTITY</entityField> - <view>DuplicatesUnrelatedCluster_view</view> - </neonViewReference> - </children> -</neonView> diff --git a/neonView/OrganisationDuplicatesFilter_view/OrganisationDuplicatesFilter_view.aod b/neonView/OrganisationDuplicatesFilter_view/OrganisationDuplicatesFilter_view.aod deleted file mode 100644 index c950c689201ccb9ecaf23a58f879058711e6c866..0000000000000000000000000000000000000000 --- a/neonView/OrganisationDuplicatesFilter_view/OrganisationDuplicatesFilter_view.aod +++ /dev/null @@ -1,32 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>OrganisationDuplicatesFilter_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - </boxLayout> - </layout> - <children> - <tableViewTemplate> - <name>OrganisationDuplicatesTable</name> - <hideContentSearch v="true" /> - <entityField>#ENTITY</entityField> - <favoriteActionGroup1>OrganisationOpenClusterDetailActionGroup</favoriteActionGroup1> - <favoriteActionGroup2>DuplicateClusterActionGroup</favoriteActionGroup2> - <isCreatable v="false" /> - <isDeletable v="false" /> - <isEditable v="false" /> - <columns> - <neonTableColumn> - <name>ebe8d904-449c-49bd-915c-5b4fee894bc2</name> - <entityField>CLUSTER_DESCRIPTION</entityField> - </neonTableColumn> - <neonTableColumn> - <name>48db4335-6bdb-4a6a-809e-f9c371733f85</name> - <entityField>COUNT_DUPLICATES_IN_CLUSTER</entityField> - </neonTableColumn> - </columns> - </tableViewTemplate> - </children> -</neonView> diff --git a/neonView/OrganisationDuplicatesTab_view/OrganisationDuplicatesTab_view.aod b/neonView/OrganisationDuplicatesTab_view/OrganisationDuplicatesTab_view.aod deleted file mode 100644 index ed86c8059d4920b5576f13406c5d4d4f86597b71..0000000000000000000000000000000000000000 --- a/neonView/OrganisationDuplicatesTab_view/OrganisationDuplicatesTab_view.aod +++ /dev/null @@ -1,24 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>OrganisationDuplicatesTab_view</name> - <title>Organisation duplicates</title> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - <direction>HORIZONTAL</direction> - </boxLayout> - </layout> - <children> - <neonViewReference> - <name>8b4ab951-afb3-4fac-915b-89226ab2f849</name> - <entityField>SelfOrganisationDuplicatesConsumer</entityField> - <view>OrganisationDuplicatesFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>d1e2ba4a-a5d6-4bba-a646-5918490e43a4</name> - <entityField>#ENTITY</entityField> - <view>OrganisationUnrelatedDuplicates_view</view> - </neonViewReference> - </children> -</neonView> diff --git a/neonView/OrganisationEdit_view/OrganisationEdit_view.aod b/neonView/OrganisationEdit_view/OrganisationEdit_view.aod index cf3a70c9a59119e3e829b6fe0acdc0b53b1a0d10..0e6500095ffe50942035ec4db7acb782dde76106 100644 --- a/neonView/OrganisationEdit_view/OrganisationEdit_view.aod +++ b/neonView/OrganisationEdit_view/OrganisationEdit_view.aod @@ -12,9 +12,9 @@ </layout> <children> <neonViewReference> - <name>f2fa0351-15af-4d1c-b7dd-0e42d9c7889f</name> - <entityField>SelfDuplicatesUncached</entityField> - <view>OrganisationNoNoiseTable_view</view> + <name>933cd72c-fd3a-4b91-aad6-8e129d6233e5</name> + <entityField>Duplicates</entityField> + <view>DuplicateOrganisationEdit_view</view> </neonViewReference> <genericViewTemplate> <name>Edit</name> diff --git a/neonView/OrganisationFilter_view/OrganisationFilter_view.aod b/neonView/OrganisationFilter_view/OrganisationFilter_view.aod index d0a3e39a2851e9ba458d22537779ff15dd1e7454..75d8254a1e1995cd430b2e0a4f8b50a1120a49b0 100644 --- a/neonView/OrganisationFilter_view/OrganisationFilter_view.aod +++ b/neonView/OrganisationFilter_view/OrganisationFilter_view.aod @@ -53,7 +53,6 @@ <entityField>#ENTITY</entityField> <favoriteActionGroup1>observeActionGroup</favoriteActionGroup1> <favoriteActionGroup2>filterViewActionGroup</favoriteActionGroup2> - <favoriteActionGroup3>DuplicateActions</favoriteActionGroup3> <linkedColumns> <element>NAME</element> </linkedColumns> @@ -102,7 +101,6 @@ <entityField>#ENTITY</entityField> <favoriteActionGroup1>observeActionGroup</favoriteActionGroup1> <favoriteActionGroup2>filterViewActionGroup</favoriteActionGroup2> - <favoriteActionGroup3>DuplicateActions</favoriteActionGroup3> <linkedColumns> <element>NAME</element> </linkedColumns> diff --git a/neonView/OrganisationMain_view/OrganisationMain_view.aod b/neonView/OrganisationMain_view/OrganisationMain_view.aod index 67ceac6a5cdafeb611c83c06583b634263c4b921..9457155195dafc5a55d96976ca5adb0ac88110a1 100644 --- a/neonView/OrganisationMain_view/OrganisationMain_view.aod +++ b/neonView/OrganisationMain_view/OrganisationMain_view.aod @@ -76,9 +76,9 @@ <view>LogHistoryFilter_view</view> </neonViewReference> <neonViewReference> - <name>cbac7602-9eba-4e5d-8617-3d0b33e55ca1</name> - <entityField>SelfDuplicatesUncached</entityField> - <view>OrganisationFilter_view</view> + <name>a5ab96e5-927c-4db3-b7ed-f0c0aadcb89b</name> + <entityField>Duplicates</entityField> + <view>DuplicateOrganisationFilter_view</view> </neonViewReference> </children> </neonView> diff --git a/neonView/PersonClusterMain_view/PersonClusterMain_view.aod b/neonView/PersonClusterMain_view/PersonClusterMain_view.aod deleted file mode 100644 index dcbd9badb6fe40452c89f4d4f54cef06fb5a0938..0000000000000000000000000000000000000000 --- a/neonView/PersonClusterMain_view/PersonClusterMain_view.aod +++ /dev/null @@ -1,23 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>PersonClusterMain_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - <direction>HORIZONTAL</direction> - </boxLayout> - </layout> - <children> - <neonViewReference> - <name>ff307d1c-9de7-4842-a697-05e783eca14b</name> - <entityField>DuplicatePersonsConsumer</entityField> - <view>PersonFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>0df195b2-9074-4734-b6df-be3bbf47050a</name> - <entityField>#ENTITY</entityField> - <view>DuplicatesUnrelatedCluster_view</view> - </neonViewReference> - </children> -</neonView> diff --git a/neonView/PersonClusterPreview_view/PersonClusterPreview_view.aod b/neonView/PersonClusterPreview_view/PersonClusterPreview_view.aod deleted file mode 100644 index 69ab192629f5b172f18bc89eaccf2b384fdfb3bb..0000000000000000000000000000000000000000 --- a/neonView/PersonClusterPreview_view/PersonClusterPreview_view.aod +++ /dev/null @@ -1,22 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>PersonClusterPreview_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - </boxLayout> - </layout> - <children> - <neonViewReference> - <name>156ebee5-6b13-462f-b956-0470f27a174b</name> - <entityField>DuplicatePersonsConsumer</entityField> - <view>PersonLookup_view</view> - </neonViewReference> - <neonViewReference> - <name>d8afce27-436c-42f0-a326-98bf6539b7bd</name> - <entityField>DuplicatesUnrelatedPersonConsumer</entityField> - <view>DuplicatesUnrelatedPersonFilter_view</view> - </neonViewReference> - </children> -</neonView> diff --git a/neonView/PersonDublicatesTab_view/PersonDublicatesTab_view.aod b/neonView/PersonDublicatesTab_view/PersonDublicatesTab_view.aod deleted file mode 100644 index c11bc1396a663aa39b2f17a1b691d239ddbd79c6..0000000000000000000000000000000000000000 --- a/neonView/PersonDublicatesTab_view/PersonDublicatesTab_view.aod +++ /dev/null @@ -1,24 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>PersonDublicatesTab_view</name> - <title>Person duplicates</title> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - <direction>HORIZONTAL</direction> - </boxLayout> - </layout> - <children> - <neonViewReference> - <name>c82a1ae2-9f8f-4149-8bac-5621136d779b</name> - <entityField>SelfPersonDuplicatesConsumer</entityField> - <view>PersonDuplicatesFilter_view</view> - </neonViewReference> - <neonViewReference> - <name>2aee29fb-9844-4e3a-a284-a04dadf9eadc</name> - <entityField>DuplicatesUnrelatedPersonConsumer</entityField> - <view>DuplicatesUnrelatedPersonFilter_view</view> - </neonViewReference> - </children> -</neonView> diff --git a/neonView/PersonDuplicatesFilter_view/PersonDuplicatesFilter_view.aod b/neonView/PersonDuplicatesFilter_view/PersonDuplicatesFilter_view.aod deleted file mode 100644 index 0e95f05f72286d568edddded65e4cd0d21031823..0000000000000000000000000000000000000000 --- a/neonView/PersonDuplicatesFilter_view/PersonDuplicatesFilter_view.aod +++ /dev/null @@ -1,48 +0,0 @@ -<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> - <name>PersonDuplicatesFilter_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <groupLayout> - <name>layout</name> - </groupLayout> - </layout> - <children> - <tableViewTemplate> - <name>PersonDuplicatesTable</name> - <hideContentSearch v="true" /> - <entityField>#ENTITY</entityField> - <favoriteActionGroup1>PersonOpenClusterDetailActionGroup</favoriteActionGroup1> - <isCreatable v="false" /> - <isDeletable v="false" /> - <isEditable v="false" /> - <isSaveable v="false" /> - <linkedFrame></linkedFrame> - <columns> - <neonTableColumn> - <name>7508e984-6a0b-4126-ab49-452e2b54f76d</name> - <entityField>CLUSTER_DESCRIPTION</entityField> - </neonTableColumn> - <neonTableColumn> - <name>8615259b-de5b-493a-945d-2ff934ae1b8c</name> - <entityField>COUNT_DUPLICATES_IN_CLUSTER</entityField> - </neonTableColumn> - </columns> - </tableViewTemplate> - <treeTableViewTemplate> - <name>Treetable</name> - <entityField>#ENTITY</entityField> - <favoriteActionGroup1>PersonOpenClusterDetailActionGroup</favoriteActionGroup1> - <columns> - <neonTreeTableColumn> - <name>060c69aa-242a-4141-acd1-b82a76d99521</name> - <entityField>CLUSTER_DESCRIPTION</entityField> - </neonTreeTableColumn> - <neonTreeTableColumn> - <name>67458881-0d45-406e-a362-852711b09bd1</name> - <entityField>COUNT_DUPLICATES_IN_CLUSTER</entityField> - </neonTreeTableColumn> - </columns> - </treeTableViewTemplate> - </children> -</neonView> diff --git a/neonView/PersonEdit_view/PersonEdit_view.aod b/neonView/PersonEdit_view/PersonEdit_view.aod index 1cc1577de9b2c4f38ee39b97d8e14d70a9d74921..27586ee38b4febc4820c68577184586052e44464 100644 --- a/neonView/PersonEdit_view/PersonEdit_view.aod +++ b/neonView/PersonEdit_view/PersonEdit_view.aod @@ -12,9 +12,9 @@ </layout> <children> <neonViewReference> - <name>589e5a82-e923-4b70-a983-5e104d2beb2b</name> - <entityField>SelfDuplicatesUncached</entityField> - <view>PersonDuplicateEditview_view</view> + <name>a6b55bfc-7b0c-4020-a78d-18ee9a1d2b26</name> + <entityField>Duplicates</entityField> + <view>DuplicatePersonEdit_view</view> </neonViewReference> <genericViewTemplate> <name>Edit</name> diff --git a/neonView/PersonFilter_view/PersonFilter_view.aod b/neonView/PersonFilter_view/PersonFilter_view.aod index c892033123ccd327c4dc49c13432e724b95843bf..2471b831c6a88a31be023611935956b63665d5e6 100644 --- a/neonView/PersonFilter_view/PersonFilter_view.aod +++ b/neonView/PersonFilter_view/PersonFilter_view.aod @@ -57,7 +57,6 @@ <entityField>#ENTITY</entityField> <favoriteActionGroup1>observeActionGroup</favoriteActionGroup1> <favoriteActionGroup2>filterViewActionGroup</favoriteActionGroup2> - <favoriteActionGroup3>DuplicateActions</favoriteActionGroup3> <linkedColumns> <element>FIRSTNAME</element> <element>LASTNAME</element> @@ -107,7 +106,6 @@ <entityField>#ENTITY</entityField> <favoriteActionGroup1>observeActionGroup</favoriteActionGroup1> <favoriteActionGroup2>filterViewActionGroup</favoriteActionGroup2> - <favoriteActionGroup3>DuplicateActions</favoriteActionGroup3> <linkedColumns> <element>FIRSTNAME</element> <element>LASTNAME</element> diff --git a/neonView/PersonMain_view/PersonMain_view.aod b/neonView/PersonMain_view/PersonMain_view.aod index 75dd326492217de020311cd9fc123d1b9ab258cc..fd3103de794c833a085cb47d2dce69320e77a528 100644 --- a/neonView/PersonMain_view/PersonMain_view.aod +++ b/neonView/PersonMain_view/PersonMain_view.aod @@ -65,9 +65,9 @@ <view>PersonMarketing_view</view> </neonViewReference> <neonViewReference> - <name>d128a678-b1c0-49d0-a74c-7860d3a85247</name> - <entityField>SelfDuplicatesUncached</entityField> - <view>PersonFilter_view</view> + <name>24ea8bc9-7ed0-4bed-a984-b1a9f3815c7c</name> + <entityField>Duplicates</entityField> + <view>DuplicatePersonFilter_view</view> </neonViewReference> <neonViewReference> <name>78a658a3-680b-4420-85a2-6e99ccff9f94</name> diff --git a/process/RebuildDuplicatesCache_serverProcess/RebuildDuplicatesCache_serverProcess.aod b/process/DuplicateMerge_lib/DuplicateMerge_lib.aod similarity index 65% rename from process/RebuildDuplicatesCache_serverProcess/RebuildDuplicatesCache_serverProcess.aod rename to process/DuplicateMerge_lib/DuplicateMerge_lib.aod index 078550c7200171ccff2e26f2515a616016da481a..bc0eb0235229e83f6e13f3215fd29f3dcb46bc1a 100644 --- a/process/RebuildDuplicatesCache_serverProcess/RebuildDuplicatesCache_serverProcess.aod +++ b/process/DuplicateMerge_lib/DuplicateMerge_lib.aod @@ -1,9 +1,9 @@ <?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.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> - <name>RebuildDuplicatesCache_serverProcess</name> + <name>DuplicateMerge_lib</name> <majorModelMode>DISTRIBUTED</majorModelMode> - <process>%aditoprj%/process/RebuildDuplicatesCache_serverProcess/process.js</process> + <process>%aditoprj%/process/DuplicateMerge_lib/process.js</process> <variants> - <element>EXECUTABLE</element> + <element>LIBRARY</element> </variants> </process> diff --git a/process/DuplicateMerge_lib/process.js b/process/DuplicateMerge_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..5b83ce3f225e20ebee0e6ca40ff7bc859dea99b3 --- /dev/null +++ b/process/DuplicateMerge_lib/process.js @@ -0,0 +1,368 @@ +import("Communication_lib"); +import("Sql_lib"); +import("system.db"); +import("ActivityTask_lib"); +import("KeywordRegistry_basic"); +import("system.translate"); +import("DuplicateScanner_lib"); + +/** + * Methods for duplicate merging. + * Do not create an instance of this! + * + * @class + */ +function DuplicateMergeUtils() {} + +/* + * + * Merges the source person into the target person. + * This + * - replaces the source's with the target's contactid in a predefined set of tables. + * - resets the standard communications of the source contact and keeps the ones of the target. + * - updates participants of campaigns and removes obsolet ones(which would be duplicates) + * - deletes the source person and contact + * - deletes the duplicate record, if one exists + * - deletes all unrelated-duplicate-relations containing the source contact id + * + * @param {String} pSourceContactId The contact to be integrated into another + * @param {String} pTargetContactId The contact in which the source gets integrated + * @returns {Boolean} if the merge was sucessful + */ +DuplicateMergeUtils.mergePerson = function(pSourceContactId, pTargetContactId) +{ + var sourcePersonId = newSelect("PERSON_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pSourceContactId) + .cell(); + var targetPersonId = newSelect("PERSON_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pTargetContactId) + .cell(); + + DuplicateMergeUtils._deleteUniqueAttributes(pSourceContactId, pTargetContactId); + var isLinkedDataUpdated = DuplicateMergeUtils._migrateLinkedContactData(pSourceContactId, pTargetContactId); + var isParticipantsUpdated = DuplicateMergeUtils._migrateParticipantsToNewContact("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", + pSourceContactId, pTargetContactId); + DuplicateMergeUtils._updateOtherContacts(pSourceContactId, sourcePersonId, targetPersonId); + + var deleteStatements = []; + if (sourcePersonId != targetPersonId) + { + deleteStatements.push(newWhere("PERSON.PERSONID", sourcePersonId).buildDeleteStatement()); + } + + deleteStatements.push(newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement()); + deleteStatements = deleteStatements.concat(DuplicateMergeUtils._buildDeleteCachedUnrelatedDuplicateQuery(pSourceContactId)); + + //update binary + var metaData = db.getBinaryMetadata("CONTACT", "DOCUMENT", pSourceContactId, true, SqlUtils.getBinariesAlias()); + metaData.forEach(function (binaryMetaData) + { + db.updateBinaryAssignment(binaryMetaData.id, "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); + }); + var deletedRows = db.deletes(deleteStatements) + + DuplicateScannerUtils.deleteHasDuplicateEntries("Person_entity", [pSourceContactId, pTargetContactId]); + var dupIds = DuplicateScannerUtils.getDuplicateIdsByEntityScanner("Person_entity", pTargetContactId); + if(dupIds.length > 0) + { + DuplicateScannerUtils.insertHasDuplicateEntry("Person_entity", pTargetContactId, dupIds.length); + } + + return isLinkedDataUpdated || isParticipantsUpdated || deletedRows > 0; +} + +DuplicateMergeUtils.mergeOrganisation = function(pSourceContactId, pTargetContactId) +{ + var sourceOrganisationId = newSelect("ORGANISATION_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pSourceContactId) + .cell(); + + var targetOrganisationId = newSelect("ORGANISATION_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pTargetContactId) + .cell(); + + DuplicateMergeUtils._deleteUniqueAttributes(pSourceContactId, pTargetContactId); + DuplicateMergeUtils._migrateLinkedContactData(pSourceContactId, pTargetContactId); + DuplicateMergeUtils._migrateParticipantsToNewContact("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", + pSourceContactId, pTargetContactId); + DuplicateMergeUtils._migratePersonsToNewOrganisation(sourceOrganisationId, targetOrganisationId); + + var deleteStatements = []; + deleteStatements.push(newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement()); + deleteStatements = deleteStatements.concat(DuplicateMergeUtils._buildDeleteOrganisationAndContactQuery(sourceOrganisationId, pSourceContactId)); + deleteStatements = deleteStatements.concat(DuplicateMergeUtils._buildDeleteCachedUnrelatedDuplicateQuery(pSourceContactId)); + + //update binary + var metaData = db.getBinaryMetadata("CONTACT", "DOCUMENT", pSourceContactId, true, SqlUtils.getBinariesAlias()); + metaData.forEach(function (binaryMetaData) + { + db.updateBinaryAssignment(binaryMetaData.id, "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); + }); + var deletedRows = db.deletes(deleteStatements) + + DuplicateScannerUtils.deleteHasDuplicateEntries("Organisation_entity", [pSourceContactId, pTargetContactId]); + var dupIds = DuplicateScannerUtils.getDuplicateIdsByEntityScanner("Organisation_entity", pTargetContactId); + if(dupIds.length > 0) + { + DuplicateScannerUtils.insertHasDuplicateEntry("Organisation_entity", pTargetContactId, dupIds.length); + } + + return deletedRows >= 2; +} + +DuplicateMergeUtils.createMergeSuccessActivity = function(pSourceContactId, pTargetContactId, pCurrentContactId, pContext) +{ + var activityDataForInsert = { + subject: translate.withArguments("A %0 record has been merged", [pContext]), + content: translate.withArguments("%0 with ID \"%1\" has been integrated into the %0 with the ID \"%2\"", [pContext, pSourceContactId, pTargetContactId]), + //categoryKeywordId: $KeywordRegistry.ac + directionKeywordId: $KeywordRegistry.activityDirection$internal(), + responsibleContactId: pCurrentContactId + }; + var activityLinks = [[pContext, pTargetContactId]]; + + return ActivityUtils.insertNewActivity(activityDataForInsert, activityLinks, null, db.getCurrentAlias()); +} + +/* + * Persons get reassigned to new organisation + * + * @returns {Boolean} If records have been updated + */ +DuplicateMergeUtils._migratePersonsToNewOrganisation = function (pSourceOrganisationId, pTargetOrganisationId) +{ + var updateCount = newWhereIfSet(["CONTACT", "ORGANISATION_ID"], pSourceOrganisationId) + .and("PERSON_ID is not null") + .updateFields(new Map().set("ORGANISATION_ID", pTargetOrganisationId), "CONTACT"); + + var deleteCount = newWhere(["CONTACT", "ORGANISATION_ID"], pSourceOrganisationId) + .and("PERSON_ID is not null") + .deleteData(); + return updateCount > 0 || deleteCount > 0; +} + +/* + * Person and organisations keep the max_count = 1 attribute of the target and the one from the source is deleted + * + * @returns {Boolean} If records have been deleted + */ +DuplicateMergeUtils._deleteUniqueAttributes = function (pSourceContactId, pTargetContactId) +{ + var targetAttrUnique = newSelect("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID") + .from("AB_ATTRIBUTERELATION") + .join("AB_ATTRIBUTEUSAGE on AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID = AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID") + .where("AB_ATTRIBUTEUSAGE.MAX_COUNT = 1") + .and("AB_ATTRIBUTERELATION.OBJECT_ROWID", pTargetContactId) + .table(); + var deleteCount = 0; + + targetAttrUnique.forEach(function(attribute){ + deleteCount += newWhereIfSet("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", attribute) + .and("AB_ATTRIBUTERELATION.OBJECT_ROWID", pSourceContactId) + .deleteData(); + }); + + return deleteCount > 0; +} + +DuplicateMergeUtils._migrateLinkedContactData = function (pSourceContactId, pTargetContactId) +{ + var updateStatements = new Map(); + var currentAlias = db.getCurrentAlias(); + + var setStandardsStatements = []; + var [standardPhone, standardMail, standardAddressId] = newSelect([ + "(" + CommUtil.getStandardSubSqlPhone() + ")", + "(" + CommUtil.getStandardSubSqlMail() + ")", + "CONTACT.ADDRESS_ID" + ]) + .from("CONTACT") + .where("CONTACT.CONTACTID", pTargetContactId) + .arrayRow(); + + //if the targetContact already has a standard phone comm, set the comm from the sourceContact as not standard + if (standardPhone) + { + setStandardsStatements.push( + newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId) + .and("COMMUNICATION.MEDIUM_ID", CommUtil.getMediumIdsByCategory("PHONE"), SqlBuilder.IN()) + .buildUpdateStatement({"ISSTANDARD" : "0"}) + ); + } + if (standardMail) + { + setStandardsStatements.push( + newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId) + .and("COMMUNICATION.MEDIUM_ID", CommUtil.getMediumIdsByCategory("EMAIL"), SqlBuilder.IN()) + .buildUpdateStatement({"ISSTANDARD" : "0"}) + ); + } + //set the standardaddress of the sourceContact as standard of the targetContact if it doesn't have one set yet + if(!standardAddressId) + { + var sourceStandard = newSelect("CONTACT.ADDRESS_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pSourceContactId) + .cell(); + setStandardsStatements.push(newWhere("CONTACT.CONTACTID", pTargetContactId).buildUpdateStatement({"ADDRESS_ID": sourceStandard})); + } + + updateStatements.set(currentAlias, setStandardsStatements); + + DuplicateMergeUtils._getLinkedTableInfos(pTargetContactId).forEach(function ([tableName, columnName, additionalCondition, dbAlias]) + { + if (!dbAlias) + { + dbAlias = currentAlias; + } + + if (!updateStatements.has(dbAlias)) + { + updateStatements.set(dbAlias, []); + } + var statements = updateStatements.get(dbAlias); + + var updateValues = {}; + updateValues[columnName] = pTargetContactId; + + var updateCondition = new SqlBuilder(dbAlias).where([tableName, columnName], pSourceContactId).andIfSet(additionalCondition); + + //push must be used here to keep the reference + statements.push(updateCondition.buildUpdateStatement(updateValues, tableName)); + }); + + var totalChanges = 0; + + updateStatements.forEach(function (statements, alias) + { + totalChanges += db.updates(statements, alias); + }); + + totalChanges += newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId).deleteData(); //delete leftover communications from the source contact + totalChanges += new AttributeRelationQuery(pSourceContactId).deleteAllAttributes(); //delete leftover attributes + + return totalChanges > 0; +} + +/* + * All records with contactId = sourceContactId get updated, which are not assigned to the same "group" as the targetContactId. + * This is because otherwise there would now be in total two "participants" in the same "group" as opposed to one before. + * Also if they already are in the same "group" those records shouldn't be updated because it would lead to the same outcome. + * + * @returns {Boolean} If records have been updated + */ +DuplicateMergeUtils._migrateParticipantsToNewContact = function (pTableName, pContactIdColumn, pAssignableIdColumn, pSourceContactId, pTargetContactId) +{ + var excludedIds = newSelect(pAssignableIdColumn) + .from(pTableName) + .where([pTableName, pContactIdColumn], pTargetContactId) + .arrayColumn(); + + var updateCount = newWhereIfSet([pTableName, pAssignableIdColumn], excludedIds, SqlBuilder.NOT_IN()) + .and([pTableName, pContactIdColumn], pSourceContactId) + .updateFields(new Map().set(pContactIdColumn, pTargetContactId), pTableName); + + var deleteCount = newWhere([pTableName, pContactIdColumn], pSourceContactId) + .tableName(pTableName) + .deleteData(); + + return updateCount > 0 || deleteCount > 0; +} + +/* + * Update other contacts from the source + * + * @returns {Boolean} If records have been updated + */ +DuplicateMergeUtils._updateOtherContacts = function (pSourceContactId, sourcePersonId, targetPersonId) +{ + var otherContacts = newSelect("PERSON.PERSONID") + .from("PERSON") + .join("CONTACT", "CONTACT.PERSON_ID = PERSON.PERSONID") + .where("PERSON.PERSONID", sourcePersonId) + .and("CONTACT.CONTACTID", pSourceContactId, SqlBuilder.NOT_EQUAL()) + .table(); + var updateCount = 0; + + otherContacts.forEach(function(person){ + updateCount += newWhere("CONTACT.PERSON_ID", person) + .updateFields({"PERSON_ID" : targetPersonId}, "CONTACT"); + }); + + return updateCount > 0; +} + +DuplicateMergeUtils._buildDeleteCachedUnrelatedDuplicateQuery = function(pSourceContactId) +{ + return [ + newWhere("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pSourceContactId).buildDeleteStatement(), + newWhere("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pSourceContactId).buildDeleteStatement() + ]; +} + +DuplicateMergeUtils._buildDeleteOrganisationAndContactQuery = function(pSourceOrganisationId, pSourceContactId) +{ + return [ + newWhere("ORGANISATION.ORGANISATIONID", pSourceOrganisationId).buildDeleteStatement(), + newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement() + ]; +} + +/* + * Contains all Tables and their fields which may contain the contact id to be replaced for the data alias + * + * @returns {String[[]]} Array in the format [TableName, ContactIdColumnName, AdditionalCondition, alias] + */ +DuplicateMergeUtils._getLinkedTableInfos = function(pTargetContactId) +{ + //don't use communications that the target already has + var targetComms = newSelect("COMMUNICATION.ADDR") + .from("COMMUNICATION") + .where("COMMUNICATION.CONTACT_ID", pTargetContactId) + .arrayColumn(); + + var communicationDedupCondition = targetComms.length > 0 + ? newWhere("COMMUNICATION.ADDR", targetComms, SqlBuilder.NOT_IN()) + : ""; + + return[ + ["AB_APPOINTMENTLINK", "OBJECT_ROWID"], + ["AB_CTILOG", "CONTACT_ID"], + ["AB_OBJECTRELATION", "AB_OBJECTRELATIONID"], + ["AB_OBJECTRELATION", "OBJECT1_ROWID"], + ["AB_OBJECTRELATION", "OBJECT2_ROWID"], + ["AB_LOGHISTORY", "TABLENAMEID"], + ["ADDRESS", "CONTACT_ID"], + ["BULKMAILRECIPIENT", "CONTACT_ID"], + ["BULKMAIL", "TESTING_CONTACT_ID"], + ["CAMPAIGN", "EMPLOYEE_CONTACT_ID"], + ["CAMPAIGNSTEP", "EMPLOYEE_CONTACT_ID"], + ["COMMRESTRICTION", "CONTACT_ID"], + ["COMMRESTRICTION", "EMPLOYEE_INVOLVED"], + ["COMMUNICATION", "CONTACT_ID", communicationDedupCondition], + ["COMPETITION", "CONTACT_ID"], + ["CONTRACT", "CONTACT_ID"], + ["LETTERRECIPIENT", "CONTACT_ID"], + ["OBJECTMEMBER", "CONTACT_ID"], + ["OFFER", "CONTACT_ID"], + ["PRODUCT", "CONTACT_ID"], + ["PRODUCTPRICE", "CONTACT_ID"], + ["SALESORDER", "CONTACT_ID"], + ["SALESPROJECT", "CONTACT_ID"], + ["TASK", "REQUESTOR_CONTACT_ID"], + ["TASK", "EDITOR_CONTACT_ID"], + ["TASKLINK", "OBJECT_ROWID"], + ["ACTIVITY", "RESPONSIBLE"], + ["DSGVO", "CONTACT_ID"], + ["DSGVOINFO", "CONTACT_ID"], + ["TIMETRACKING", "CONTACT_ID"], + ["ACTIVITYLINK", "OBJECT_ROWID"], + ["AB_ATTRIBUTERELATION", "OBJECT_ROWID"], + + ["ASYS_CALENDARLINK", "DBID", "", SqlUtils.getSystemAlias()] + ]; +} diff --git a/process/DuplicateScanner_lib/process.js b/process/DuplicateScanner_lib/process.js index e26053edc6cb753291a842212cbfcef324124799..be805bc2aecc3323fb0e536f545da25e8d2b5762 100644 --- a/process/DuplicateScanner_lib/process.js +++ b/process/DuplicateScanner_lib/process.js @@ -1,20 +1,12 @@ import("system.project"); -import("ActivityTask_lib"); -import("KeywordRegistry_basic"); -import("system.translate"); -import("system.datetime"); -import("JditoFilter_lib"); -import("system.process"); import("system.util"); import("system.vars"); -import("system.net"); -import("system.logging"); import("system.db"); import("system.entities"); import("Sql_lib"); import("system.indexsearch"); -import("Communication_lib"); -import("Attribute_lib"); +import("IndexSearch_lib"); +import("system.process"); /** * Methods for duplicate scanning. @@ -24,1356 +16,250 @@ import("Attribute_lib"); */ function DuplicateScannerUtils() {} -/* - * Loads all prefilters for a scanner in the form of arrays in an array. - * Single filter record: [FILTER_CONDITION, COUNT_CHARACTERS_TO_USE, MAX_RESULTS_THRESHOLD] - * - * @param {String} pFilterName Name of the filter - * @param {String} pTargetEntity Entity which has been configured - * @returns {String[[]]} Array of arrays containing the configured values - */ -DuplicateScannerUtils.loadFilters = function(pFilterName, pTargetEntity) -{ - let query = newSelect("FILTER_CONDITION, COUNT_CHARACTERS_TO_USE, MAX_RESULTS_THRESHOLD") - .from("DUPLICATESCANNERPREFILTERCONFIG") - .join("DUPLICATESCANNER", "DUPLICATESCANNER.ID = DUPLICATESCANNERPREFILTERCONFIG.DUPLICATESCANNER_ID") - .where("DUPLICATESCANNER.FILTER_NAME", pFilterName) - .and("DUPLICATESCANNER.ENTITY_TO_SCAN_NAME", pTargetEntity); - - return query.table(); -} - -/* - * Deletes the cached duplicate for the given id. - * If there would only remain one item in the cluster after deletion, the whole cluster including the duplicate gets deleted. - * In this case, all records marked as unrelated duplicate will be deleted aswell. - * - * @param {String} pDuplicateId Id of the duplicate to delete - */ -DuplicateScannerUtils.deleteCachedDuplicate = function(pDuplicateId) -{ - var [countDuplicatesInClusterWithoutParameterId, clusterId] = newSelect("count(ID), CLUSTERID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.CLUSTERID", newSelect("CLUSTERID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateId).build(), - SqlBuilder.IN()) - .and("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateId, SqlBuilder.NOT_EQUAL()) - .groupBy("CLUSTERID") - .arrayRow(); - - //If only one duplicate would be remaining, - //the whole cluster has to be deleted because there are no more duplicates. - //Otherwise delete just the single duplicate. The parameterized duplicate has been excluded via sql - //therefore check for smaller/equals 1 - if(countDuplicatesInClusterWithoutParameterId <= 1) - { - newWhere("DUPLICATECLUSTERS.CLUSTERID", clusterId).deleteData(); - newWhere("UNRELATEDDUPLICATES.CLUSTERID", clusterId).deleteData(); - } - else - { - newWhereIfSet("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateId).deleteData(); - - //Delete all records where this duplicateId is mentioned - DuplicateScannerUtils.deleteAllUnrelatedDuplicateRelations(pDuplicateId); - } -} - -/* - * Deletes all Clusters for the given target Entity. - * No records markes as unrelated duplicate are being deleted. - * - * @param {String} pTargetEntity Entity which has been configured - * @return Count of deleted rows - */ -DuplicateScannerUtils.deleteClustersByTargetEntity = function(pTargetEntity) -{ - return newWhereIfSet("DUPLICATECLUSTERS.TARGET_ENTITY", pTargetEntity).deleteData(); -} - -/* - * Updates the duplicate relations markes as unrelated for the given target entity. - * All ClusterIds get updated with the new values if the same combination of duplicateIds - * still exists in the DUPLICATECLUSTERS table. - * - * Afterwards, all records which contain a nonexistend clusterId are being deleted - * - * @param {String} pTargetEntity Name of Entity whose duplicates should be updated +/** + * Returns an sql condition which returns a list of duplicate ids. + * The count is checked using pCount and pOperator. + * This function can be used for FilterExtensions + * + * @param {string[]} pEntities target entities + * @param {number} pCount value of count (will be applied to the operator) + * @param {string} pOperator the operator in sql notation e.g.: # = ? + * @param {boolean} pSubtractIgnored if set the ignored duplicates will be subtracted + * + * @returns {SqlBuilder} subselect selecting the duplicate ids */ -DuplicateScannerUtils.refreshUnrelatedDuplicateRelations = function(pTargetEntity) +DuplicateScannerUtils.getDuplicateConditionalListSql = function(pEntities, pCount, pOperator, pSubtractIgnored) { - /* - * Update all records with the current valid clusterId where the same duplicateId combination exists - */ - let clusterIdChanges = newSelect("dc1.CLUSTERID, ud.CLUSTERID") - .from("UNRELATEDDUPLICATES", "ud") - .join("DUPLICATECLUSTERS", "dc1.DUPLICATEID = ud.SOURCEDUPLICATEID", "dc1") - .join("DUPLICATECLUSTERS", "dc2.DUPLICATEID = ud.UNRELATEDDUPLICATEID", "dc2") - .where(["DUPLICATECLUSTERS", "TARGET_ENTITY", "dc1"], pTargetEntity) - .table(); - - let updateStatements = clusterIdChanges.map(function ([newClusterId, oldClusterId]) - { - return newWhere("UNRELATEDDUPLICATES.CLUSTERID", oldClusterId).buildUpdateStatement({ - "CLUSTERID": newClusterId - }, "UNRELATEDDUPLICATES"); - }); + var subselect = newSelect("count(*)").from("UNRELATEDDUPLICATES") + .where("UNRELATEDDUPLICATES.DUPLICATETYPE = HASDUPLICATE.OBJECT_TYPE") + .and("UNRELATEDDUPLICATES.SOURCEDUPLICATEID = HASDUPLICATE.OBJECT_ROWID"); - db.updates(updateStatements); - - /* - * All unrelated duplicate ids that still exist in a cluster, have been updated with the new cluster id. - * All records with a nonexistend clusterid can now be deleted because they haven't been detected as a duplicate any more. - */ - newWhere("UNRELATEDDUPLICATES.CLUSTERID", newSelect("dc1.CLUSTERID").from("DUPLICATECLUSTERS", "dc1"), SqlBuilder.NOT_IN()) - .deleteData(); -} - -/* - * Creates a relation between two duplicates which means they are unrelated. - * They will not appear in each others duplicate tab any more. - * To remove this relation use DuplicateScannerUtils.deleteUnrelatedDuplicateRelation - * - * @param {String} pSourceContactId Id of first duplicate - * @param {String} pUnrelatedContactId Id of second duplicate - * @param {String} pClusterId Id of the cluster in which the duplicates are aggregated - * @returns {String} Number of Records inserted - */ -DuplicateScannerUtils.createUnrelatedDuplicateRelation = function(pSourceContactId, pUnrelatedContactId, pClusterId) -{ - return new SqlBuilder().insertFields({ - "UNRELATEDDUPLICATEID": pUnrelatedContactId, - "SOURCEDUPLICATEID": pSourceContactId, - "CLUSTERID": pClusterId - }, "UNRELATEDDUPLICATES", "ID"); -} - -/* - * Gets the cluster id in which the given duplicate id exists - * - * @param {String} pDuplicateId whose cluster id should be searched - * @returns {String} Cluster id - */ -DuplicateScannerUtils.getClusterId = function(pDuplicateId) -{ - return newSelect("CLUSTERID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateId) - .cell(); -} - -/* - * Deletes the "unrelated" relation between two duplicates - * - * @param {String} pSourceDuplicateId Id of the source duplicate - * @param {String} pUnrelatedDuplicateId Id of the source duplicate - * @returns {String} Number of records deleted - */ -DuplicateScannerUtils.deleteUnrelatedDuplicateRelation = function(pSourceDuplicateId, pUnrelatedDuplicateId) -{ - return newWhere("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pSourceDuplicateId) - .and("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pUnrelatedDuplicateId) - .deleteData(); -} - -/* - * Deletes all relations to a duplicate id wether the id is the source or the unrelated duplicate in the relation - * - * @param {String} pDuplicateId Duplicate id whose "unrelated" relations are to delete - * @returns {String} Number of records deleted - */ -DuplicateScannerUtils.deleteAllUnrelatedDuplicateRelations = function(pDuplicateId) -{ - DuplicateScannerUtils.deleteUnrelatedDuplicateRelation(pDuplicateId, pDuplicateId); + var countSql = pSubtractIgnored ? "(HASDUPLICATE.DUPLICATECOUNT - (" + subselect.toString() + "))" : "(HASDUPLICATE.DUPLICATECOUNT)"; + return newSelect("HASDUPLICATE.OBJECT_ROWID").from("HASDUPLICATE") + .where("HASDUPLICATE.OBJECT_TYPE", pEntities, SqlBuilder.IN()) + .and(pOperator.replace("#", countSql).replace("?", pCount)); } -/* - * Loads all other duplicates from the cluster in which the parameterized duplicate is located - * - * @param {String} pDuplicateId - * @returns {String[]} Array of duplicate ids - */ -DuplicateScannerUtils.getCachedDuplicatesForDuplicateId = function(pDuplicateId) -{ - return newSelect("DUPLICATEID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.CLUSTERID", newSelect("CLUSTERID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateId), - SqlBuilder.IN()) - .and("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateId, SqlBuilder.NOT_EQUAL()) - .and("DUPLICATECLUSTERS.DUPLICATEID", newSelect("UNRELATEDDUPLICATEID") - .from("UNRELATEDDUPLICATES") - .where("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pDuplicateId), - SqlBuilder.NOT_IN()) - .and("DUPLICATECLUSTERS.DUPLICATEID", newSelect("SOURCEDUPLICATEID") - .from("UNRELATEDDUPLICATES") - .where("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pDuplicateId), - SqlBuilder.NOT_IN()) - .arrayColumn(); -} - -/* - * Returns all duplicate ids which haven't been marked as unrelated for the given cluster id. - * - * @param {String} pClusterId The clusters id - * @return {String[]} Array of duplicate ids excluding those marked as unrelated - */ -DuplicateScannerUtils.getCachedDuplicatesForClusterId = function(pClusterId) -{ - return newSelect("DUPLICATEID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.DUPLICATEID", newSelect("UNRELATEDDUPLICATEID").from("UNRELATEDDUPLICATES"), - SqlBuilder.NOT_IN()) - .and("DUPLICATECLUSTERS.CLUSTERID", pClusterId) - .arrayColumn(); -} - -/* - * Recreates the cached duplicate clusters based on the configured pattern. <br /> - * The old clusters have to be deleted manually beforehand or by using "deleteClustersByTargetEntity".<br /> - * If there have already been ignored relations between duplicate records, it's advised to call "refreshUnrelatedDuplicateRelations" after the recreation of the duplicates cache.<br /> - * Please check the documentation of the params on how to get the infos required.<br /> - * <br /> - * If the usage of an external webservice has been activated, the search will be executed beforehand and the results will then be given to the pFormatValuesConsumeWebserviceCallback via parameter.<br /> - * To access the values it is advised to run thru the parameter like an array and access its value by key which is the index field name. The entity<br /> - * field names can be converted using DuplicateScannerUtils.translateEntityToIndexFields. For further infos see the example section below.<br /> - * <br /> - * Attention!<br /> - * If it is configured to use the external webservice callback the values have to be in the same format as they are in the parameter of the callback.<br /> - * <br /> - * @param {String} pFilterName Name of the filter to use - * @param {String} pTargetEntity The target entity which has been assigned to the filters configuration - * @param {String} pRecordsBlockSize The values which are checked get loaded in blocks. - * @param {String} pFormatValuesConsumeWebserviceCallback Null if no external service is used otherwise a function with one parameter. - * @return {Int} Count of duplicate clusters created - * - * @example - * var filterName = "PersonDuplicates"; - * var targetEntity = "Person_entity"; - * var recordBlockSize = DuplicateScannerUtils.getBlockSize(); - * - * let formatToJsonAndCallWsCallback = function(pPossibleDuplicatesRay) - * { - * let indexResultFields = DuplicateScannerUtils.TranslateEntityToIndexFields(targetEntity, resultFields) - * - * //Run thru every duplicate result an read out the resultfields - * for (let i = 0; i < pPossibleDuplicatesRay.length; i++) - * { - * for (let b = 0; b < resultFields.length; b++) - * { - * let entityFieldName = resultFields[b]; - * let indexFieldName = indexResultFields[entityFieldName]; - * //format values - * } - * } - * //call webservice - * //reformat results to same structure as before - * return pPossibleDuplicatesRay; - * }; - * - * DuplicateScannerUtils.DeleteDuplicateClustersByTargetEntity(targetEntity); - * - * DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, pRecordsBlockSize, formatToJsonAndCallWsCallback); - * - * DuplicateScannerUtils.RefreshUnrelatedDuplicateRelations(targetEntity); +/** + * Ignores duplicates corrosponding to the type, the source, and the duplicate ids + * + * @param {string} pDuplicateType the duplicate type e.g.: Organisation_entity + * @param {string} pSourceDuplicateId the source duplicate id + * @param {string[]} pUnrelatedDuplicateIds the target duplicate ids + * @param {boolean} pIgnore weather the duplicate should be ignored or unignored */ -DuplicateScannerUtils.rebuildDuplicatesCache = function(pFilterName, pTargetEntity, - pRecordsBlockSize, pFormatValuesConsumeWebserviceCallback) +DuplicateScannerUtils.updateIgnored = function(pDuplicateType, pSourceDuplicateId, pUnrelatedDuplicateIds, pIgnore) { - let useExternalWebservice = _DuplicateScannerUtils._isUseExternalWebservice(pFilterName, pTargetEntity); - let resultFields = DuplicateScannerUtils.getResultFields(filterName, targetEntity); - - let indexPattern = _DuplicateScannerUtils._loadIndexPattern(pFilterName, pTargetEntity); - let entityFieldConfigs = _DuplicateScannerUtils._loadEntityFieldConfigsFromPattern(indexPattern); - let entityIdField = _DuplicateScannerUtils._loadEntityIdField(pFilterName, pTargetEntity); + newWhere("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pSourceDuplicateId) + .and("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pUnrelatedDuplicateIds, SqlBuilder.IN()) + .deleteData(); - let alreadyIdentifiedIds = []; + var TABLE_NAME = "UNRELATEDDUPLICATES"; + var COLUMN_NAMES = ["ID", "DUPLICATETYPE", "SOURCEDUPLICATEID", "UNRELATEDDUPLICATEID"]; + var COLUMN_TYPES = db.getColumnTypes(TABLE_NAME, COLUMN_NAMES); - let entityFields = _DuplicateScannerUtils._loadEntityFieldsFromFieldConfigs(entityFieldConfigs); - - entityFields.push(entityIdField); - - let targetRecords = DuplicateScannerUtils.getEntityRecords(pTargetEntity, entityFields, 0, pRecordsBlockSize); - - let currentRecordIndex = pRecordsBlockSize; - while(targetRecords.length > 0) + var statements = []; + pUnrelatedDuplicateIds.forEach(function(currId) { - - foundDuplicateIds = DuplicateScannerUtils.scanRecords(pTargetEntity, targetRecords, - entityFieldConfigs, resultFields, useExternalWebservice, - pFormatValuesConsumeWebserviceCallback, alreadyIdentifiedIds, indexPattern, entityIdField); - - if (foundDuplicateIds) - alreadyIdentifiedIds = alreadyIdentifiedIds.concat(foundDuplicateIds); - - if(targetRecords.length < pRecordsBlockSize) - { - break; - } - targetRecords = DuplicateScannerUtils.getEntityRecords(pTargetEntity, entityFields, - currentRecordIndex, pRecordsBlockSize); - - currentRecordIndex += pRecordsBlockSize; - } -} - -DuplicateScannerUtils.scanRecords = function(pTargetEntity, pTargetRecordsData, - pEntityFieldConfigs, pResultFields, pUseExternalWebservice, pFormatValuesConsumeWebserviceCallback, pAlreadyIdentifiedIds, pIndexPattern, pEntityIdField) -{ - let foundDuplicateIds = []; - - //If the contact id loading query has no results, stop. - //No ids should be deleted if an error has been made in this query. - if(pTargetRecordsData.length <= 0) - return foundDuplicateIds; - - //First it gets checked if the current id has already been identified. If that's the case it'll continue with the next. - //Otherwise an object gets build in the form of ["FilterFieldName" = "FilterFieldValueFromQuery"] with which a scan for possible duplicates get's started - var duplicatesToInsertQueries = []; - for (let b = 0; b < pTargetRecordsData.length; b++) + var columnValues = [util.getNewUUID(), pDuplicateType, pSourceDuplicateId, currId]; + statements.push([TABLE_NAME, COLUMN_NAMES, COLUMN_TYPES, columnValues]); + }); + if(pIgnore) { - let entityFieldValuesRay = DuplicateScannerUtils.buildEntityFieldConfigs(pEntityFieldConfigs, pTargetRecordsData[b]); - - //The first field in this Array must always be the configured id field. This is ensured using onValidation-logic - let idValue = pTargetRecordsData[b][pEntityIdField]; - - //If the current Id has already been identified, continue - if(pAlreadyIdentifiedIds.indexOf(pTargetRecordsData[b][pEntityIdField]) > -1) - { - continue; - } - - let foundDuplicates = _DuplicateScannerUtils._scanForDuplicates(pTargetEntity, - entityFieldValuesRay, pResultFields, idValue, pFormatValuesConsumeWebserviceCallback, pUseExternalWebservice, pIndexPattern) - - if(foundDuplicates == null || foundDuplicates.length == 0) - { - continue; - } - //Insert all found duplicate ids into an cache array because those ids don't have to be checked again lateron. - for (let i = 0; i < foundDuplicates.length; i++) - { - let localId = foundDuplicates[i][indexsearch.FIELD_ID]; - foundDuplicateIds.push(localId); - } - - pAlreadyIdentifiedIds = pAlreadyIdentifiedIds.concat(foundDuplicateIds); - - //The duplicates list contains only the found duplicates to the original id, therefore it get's added manually - foundDuplicateIds.push(pTargetRecordsData[b][pEntityIdField]); - - let insertQueriesRay = _DuplicateScannerUtils._createInsertDuplicatesClusterQuery(foundDuplicateIds, pTargetEntity) - duplicatesToInsertQueries = duplicatesToInsertQueries.concat(insertQueriesRay); - foundDuplicateIds = []; + db.inserts(statements); } - db.inserts(duplicatesToInsertQueries, db.getCurrentAlias(), 10 * datetime.ONE_MINUTE); - return foundDuplicateIds; } -/* - * Searches for a cluster which contains the duplicates specified by the parameterized array. <br /> - * The contents of the cluster have to be identical, if no fitting cluster could be found an empty string is returned. +/** + * Removes ignored duplicates from an array of duplicate ids * - * @param {String} pNewRecordId The id of the record which was used to scan for duplicates - * @param {String[]} pDuplicateIds Duplicate ids used to search for a cluster containing them - * @param {String} pTargetEntity Entity which has been configured - * @returns {String} A clusterid if a matching cluster has been found, otherwise "" - */ -DuplicateScannerUtils.cacheNewScanResults = function(pNewRecordId, pDuplicateIds, pTargetEntity) -{ - let duplicateIds = []; - //Run thru every duplicate result and read out the id. - //Do it now to have a simple array on all usages lateron. - for (let i = 0; i < pDuplicateIds.length; i++) - { - let duplicateContactId = pDuplicateIds[i][indexsearch.FIELD_ID]; - duplicateIds.push(duplicateContactId); - } - - let clusterId = DuplicateScannerUtils.getClusterWithIdenticalDuplicates(duplicateIds); - - //If no cluster has beend found, create a new one with all found duplicateIds, - //otherwise add the id to the existing cluster - let idRayToInsert = []; - if(clusterId == undefined || clusterId == null || clusterId == "") - { - idRayToInsert = duplicateIds; - idRayToInsert.push(pNewRecordId); - } - else - idRayToInsert.push(pNewRecordId); - - insertQueriesRay = _DuplicateScannerUtils._createInsertDuplicatesClusterQuery(idRayToInsert, pTargetEntity, clusterId) - - return db.inserts(insertQueriesRay); -} - -/* - * Searches for a cluster which contains the duplicates specified by the parameterized array. <br /> - * The contents of the cluster have to be identical, if no fitting cluster could be found an empty string is returned. + * @param {string} pTargetEntity the target entity e.g.: Organisation_entity + * @param {string} pTargetUid the source duplicate id + * @param {string} pIdArray the array of duplicate ids * - * @param {String[]} pDuplicateIds Duplicate ids which should be in the same cluster - * @returns {String} Id of the cluster which contains all given duplicate ids or "" + * @returns {string[]} the filtered duplicate ids */ -DuplicateScannerUtils.getClusterWithIdenticalDuplicates = function(pDuplicateIds) +DuplicateScannerUtils.filterIgnored = function(pTargetEntity, pTargetUid, pIdArray) { - let RESULT_NO_CLUSTER_FOUND = ""; - - if(pDuplicateIds.length < 1) - return RESULT_NO_CLUSTER_FOUND; - - let clusterIdSelect = newSelect("distinct CLUSTERID") - .from("DUPLICATECLUSTERS") - .where(); - - for (let i = 0; i < pDuplicateIds.length; i++) + if(pIdArray.length == 0) { - clusterIdSelect.and("DUPLICATECLUSTERS.CLUSTERID", newSelect("CLUSTERID").from("DUPLICATECLUSTERS").where("DUPLICATECLUSTERS.DUPLICATEID", pDuplicateIds[i]), - SqlBuilder.IN()); + return pIdArray; } - - let foundClusterId = clusterIdSelect.cell(); - - if(foundClusterId == null || foundClusterId == "") - return RESULT_NO_CLUSTER_FOUND; - - let duplicatesInCluster = newSelect("DUPLICATEID") - .from("DUPLICATECLUSTERS") - .where("DUPLICATECLUSTERS.CLUSTERID", foundClusterId) - .arrayColumn(); - - /* - * A cluster has been searched which contains all duplicate ids as specified via parameter. - * There's the possibility that this cluster contains even more duplicates than specified via the parameter. - * In this case, the cluster and the parameterized duplicateids are not identical - * which means a new cluster has to be created. - */ - if(pDuplicateIds.length != duplicatesInCluster.length) - return RESULT_NO_CLUSTER_FOUND; - else - return foundClusterId; + var ignoreTable = newSelect(["UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID"]) + .from("UNRELATEDDUPLICATES") + .where("UNRELATEDDUPLICATES.DUPLICATETYPE", pTargetEntity) + .and("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pTargetUid) + .and("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pIdArray, SqlBuilder.IN()) + .arrayColumn(); + return pIdArray.filter(function(curr) { + return !ignoreTable.includes(curr); + }); } -DuplicateScannerUtils.getEntityRecords = function(pTargetEntity, pEntityFields, pStartRow, pCountRecordsToLoad) -{ - let getRowsConfig = entities.createConfigForLoadingRows() - .entity(pTargetEntity) - .fields(pEntityFields) - .count(pCountRecordsToLoad) - .startrow(pStartRow); - return entities.getRows(getRowsConfig); -} -/* - * Loads the configured resultfields as array - * - * @param {String} pFilterName Name of the filter - * @param {String} pTargetEntity Entity which has been configured - * @returns {String[]} Resultfields as array +/** + * Deletes duplicates corrosponding to the given type and ids + * + * @param {string} pObjectType the duplicate type e.g.: Organisation_entity + * @param {string[]} pObjectRowIds the duplicate ids e.g. some organisation ids */ -DuplicateScannerUtils.getResultFields = function(pFilterName, pTargetEntity) +DuplicateScannerUtils.deleteHasDuplicateEntries = function(pObjectType, pObjectRowIds) { - return newSelect("dsrfc.ENTITY_FIELD_NAME") - .from("DUPLICATESCANNERRESULTFIELDCONFIG dsrfc") - .join("DUPLICATESCANNER", "DUPLICATESCANNER.ID = dsrfc.DUPLICATESCANNER_ID") - .where("DUPLICATESCANNER.FILTER_NAME", pFilterName) - .and("DUPLICATESCANNER.ENTITY_TO_SCAN_NAME", pTargetEntity) - .arrayColumn(); + newWhere("HASDUPLICATE.OBJECT_TYPE", pObjectType) + .and("HASDUPLICATE.OBJECT_ROWID", pObjectRowIds, SqlBuilder.IN()) + .deleteData(); } -/* - * Scans for duplicates based on the configured pattern and the selected id field<br /> - * All values to the used placeholders have to be present in "pValuesToCheck"<br /> - * First all placeholders in the pattern will be replaced with their respective values. - * Then, the pattern is extended wo exclude the record on which the search is based on. - *<br /> - * If the usage of an external webservice has been activated, the result will then be given to the pFormatValuesConsumeWebserviceCallback via parameter.<br /> - * To access the values it is advised to run thru the parameter like an array and access its value by key which is the index field name. The entity - * field names can be converted using DuplicateScannerUtils.translateEntityToIndexFields - * <br /> - * <br /> - * Attention!<br /> - * If it's a single scanForDuplicates call it doesn't matter what the callback returns because after the callback, no more modifications follow before - * returning the data.<br /> - * If it's inside the RebuildCache the values have to be in the same format as the parameter - * - * @param {String} pFilterName Name of the filter - * @param {String} pTargetEntity Respective target entity - * @param {{"key", "value"}} pValuesToCheck An object with key value pairs which hold the name of the entity field as key and it's value as value. See the example "valuesToCheck" - * @param {function} pFormatValuesConsumeWebserviceCallback Null if no external service is used otherwise a function with one parameter. - * If the function is called is based on the configuration of the current scanner - * @returns {[["key", "value"]]} Array of Key-Value-Pairs based on the configured resultfields, if an external webservices was used - * the structure is defined by the parameterized function "pFormatValuesConsumeWebserviceCallback" - * - * @example - * var filterName = "PersonDuplicates"; - * let targetEntity = "Person_entity"; - * let valuesToCheck = {}; - * var entityModel = project.getEntityStructure(targetEntity); +/** + * Manually inserts a duplicate entry by the given type, id and count * - * //Read the values of all available entity fields and write the fieldname/value combination - * //as key/value pairs into an object. This is used to trigger the scan for duplicates - * let fieldValue = ""; - * let entityFields = []; - * for (fieldname in entityModel.fields) - * { - * field = entityModel.fields[fieldname]; - * if(field.fieldType == project.ENTITYFIELDTYPE_FIELD) - * { - * fieldValue = vars.get("$field." + field.name); - * - * if(fieldValue != null && fieldValue != "") - * { - * valuesToCheck[field.name] = fieldValue; - * } - * } - * } - * - * let formatToJsonAndCallWsCallback = function(pPossibleDuplicatesRay) - * { - * let indexResultFields = DuplicateScannerUtils.TranslateEntityToIndexFields(targetEntity, resultFields) - * - * //Run thru every duplicate result an read out the resultfields - * for (let i = 0; i < pPossibleDuplicatesRay.length; i++) - * { - * for (let b = 0; b < resultFields.length; b++) - * { - * let entityFieldName = resultFields[b]; - * let indexFieldName = indexResultFields[entityFieldName]; - * //format values - * } - * } - * //call webservice - * //reformat results to same structure as before - * return pPossibleDuplicatesRay; - * }; - * - * //The result values can be accessed as seen above in "formatToJsonAndCallWsCallback" - * DuplicateScannerUtils.ScanForDuplicates(filterName, targetEntity, valuesToCheck, - * formatToJsonAndCallWsCallback); + * @param {string} pObjectType the type e.g.: Organisation_entity + * @param {string} pObjectRowId the id e.g. a organisation id + * @param {number} pCount the count / how many duplicates are there */ -DuplicateScannerUtils.scanForDuplicates = function(pFilterName, pTargetEntity, pValuesToCheck, pFormatValuesConsumeWebserviceCallback) +DuplicateScannerUtils.insertHasDuplicateEntry = function(pObjectType, pObjectRowId, pCount) { - let useExternalWebservice = _DuplicateScannerUtils._isUseExternalWebservice(pFilterName, pTargetEntity); - let resultFields = DuplicateScannerUtils.getResultFields(pFilterName, pTargetEntity); - - let indexPattern = _DuplicateScannerUtils._loadIndexPattern(pFilterName, pTargetEntity); - let entityFieldConfigs = _DuplicateScannerUtils._loadEntityFieldConfigsFromPattern(indexPattern); - let entityIdField = _DuplicateScannerUtils._loadEntityIdField(pFilterName, pTargetEntity); - - let entityFieldConfigValuesRay = DuplicateScannerUtils.buildEntityFieldConfigs(entityFieldConfigs, pValuesToCheck); - - //The first field in this Array must always be the configured id field. - let idValue = pValuesToCheck[entityIdField]; - - return _DuplicateScannerUtils._scanForDuplicates(pTargetEntity, - entityFieldConfigValuesRay, resultFields, idValue, - pFormatValuesConsumeWebserviceCallback, useExternalWebservice, indexPattern) + var COLUMNS = [ + "HASDUPLICATEID", + "OBJECT_TYPE", "OBJECT_ROWID", + "DUPLICATECOUNT" + ]; + var COLUMN_TYPES = db.getColumnTypes("HASDUPLICATE", COLUMNS); + var values = [ + util.getNewUUID(), + pObjectType, pObjectRowId, + pCount.toFixed(0) + ]; + db.insertData("HASDUPLICATE", COLUMNS, COLUMN_TYPES, values); } -/* - * Executes a indexsearch.lookupIndexField for eacht entity field in the parameterized array - * and returns it as Map. - * - * @param {String} pEntityName ...Name of the entity - * @param {String[]} pEntityFields Array of the entities Fields to translate to index fields - * @returns Map-like object where (key = entity field) and (value = index field) - * - * @example - * let entityResultFields = ["LASTNAME"]; - * let entityIndexFields = DuplicateScannerUtils.translateEntityToIndexFields("Person_entity", entityResultFields); - * +/** + * Updates HasDuplicate by entity (used in onInser and onUpdate) + * + * @param {string} pTargetEntity the target entity e.g.: Organisation_entity */ -DuplicateScannerUtils.translateEntityToIndexFields = function(pEntityName, pEntityFields) +DuplicateScannerUtils.updateHasDuplicateEntry = function(pTargetEntity) { - let entityIndexFields = {}; - for (let i = 0; i < pEntityFields.length; i++) - { - let entityFieldName = pEntityFields[i]; + var scanner = DuplicateScannerUtils.getScannerByEntity(pTargetEntity); + var indexsearchFilter = IndexsearchFilterUtils.fromFilter(scanner.filter); - //todo remove if api has been fixed - if(entityFieldName.startsWith(pEntityName)) - entityFieldName = entityFieldName.replace(pEntityName + ".", ""); - - let indexField = indexsearch.lookupIndexField(pEntityName, entityFieldName); - entityIndexFields[pEntityFields[i]] = indexField; - } - return entityIndexFields; -} + var fields = indexsearchFilter.getFields(); + fields.add(scanner.idField); + fields = Array.from(fields); -/* - * - * Merges the source person into the target person. - * This - * - replaces the source's with the target's contactid in a predefined set of tables. - * - resets the standard communications of the source contact and keeps the ones of the target. - * - updates participants of campaigns and removes obsolet ones(which would be duplicates) - * - deletes the source person and contact - * - deletes the duplicate record, if one exists - * - deletes all unrelated-duplicate-relations containing the source contact id - * - * @param {String} pSourceContactId The contact to be integrated into another - * @param {String} pTargetContactId The contact in which the source gets integrated - * @returns {Boolean} if the merge was sucessful - */ -DuplicateScannerUtils.mergePerson = function(pSourceContactId, pTargetContactId) -{ - var sourcePersonId = newSelect("PERSON_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pSourceContactId) - .cell(); - var targetPersonId = newSelect("PERSON_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pTargetContactId) - .cell(); - - _DuplicateScannerUtils._deleteUniqueAttributes(pSourceContactId, pTargetContactId); - var isLinkedDataUpdated = _DuplicateScannerUtils._migrateLinkedContactData(pSourceContactId, pTargetContactId); - var isParticipantsUpdated = _DuplicateScannerUtils._migrateParticipantsToNewContact("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", - pSourceContactId, pTargetContactId); - _DuplicateScannerUtils._updateOtherContacts(pSourceContactId, sourcePersonId, targetPersonId); - - var deleteStatements = []; - if (sourcePersonId != targetPersonId) - deleteStatements.push(newWhere("PERSON.PERSONID", sourcePersonId).buildDeleteStatement()); - - deleteStatements.push(newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement()); - deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteCachedUnrelatedDuplicateQuery(pSourceContactId)); - - //update binary - var metaData = db.getBinaryMetadata("CONTACT", "DOCUMENT", pSourceContactId, true, SqlUtils.getBinariesAlias()); - metaData.forEach(function (binaryMetaData) + var entityObj = {}; + fields.forEach(function(field) { - db.updateBinaryAssignment(binaryMetaData.id, "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); + entityObj[field] = vars.get("$field." + field); }); - var deletedRows = db.deletes(deleteStatements) - - DuplicateScannerUtils.deleteCachedDuplicate(pSourceContactId); - - return isLinkedDataUpdated || isParticipantsUpdated || deletedRows > 0; -} - -DuplicateScannerUtils.createMergeSuccessActivity = function(pSourceContactId, pTargetContactId, pCurrentContactId, pContext) -{ - var activityDataForInsert = { - subject: translate.withArguments("A %0 record has been merged", [pContext]), - content: translate.withArguments("%0 with ID \"%1\" has been integrated into the %0 with the ID \"%2\"", [pContext, pSourceContactId, pTargetContactId]), - //categoryKeywordId: $KeywordRegistry.ac - directionKeywordId: $KeywordRegistry.activityDirection$internal(), - responsibleContactId: pCurrentContactId - }; - var activityLinks = [[pContext, pTargetContactId]]; - - return ActivityUtils.insertNewActivity(activityDataForInsert, activityLinks, null, db.getCurrentAlias()); -} - -DuplicateScannerUtils.mergeOrganisation = function(pSourceContactId, pTargetContactId) -{ - var sourceOrganisationId = newSelect("ORGANISATION_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pSourceContactId) - .cell(); - - var targetOrganisationId = newSelect("ORGANISATION_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pTargetContactId) - .cell(); + var selfId = entityObj[scanner.idField]; - _DuplicateScannerUtils._deleteUniqueAttributes(pSourceContactId, pTargetContactId); - _DuplicateScannerUtils._migrateLinkedContactData(pSourceContactId, pTargetContactId); - _DuplicateScannerUtils._migrateParticipantsToNewContact("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", - pSourceContactId, pTargetContactId); - _DuplicateScannerUtils._migratePersonsToNewOrganisation(sourceOrganisationId, targetOrganisationId); + var indexPattern = indexsearchFilter.buildQuery(entityObj); + var ids = DuplicateScannerUtils.getDuplicateIds(pTargetEntity, indexPattern, selfId); - var deleteStatements = []; - deleteStatements.push(newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement()); - deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteOrganisationAndContactQuery(sourceOrganisationId, pSourceContactId)); - deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteCachedUnrelatedDuplicateQuery(pSourceContactId)); - - //update binary - var metaData = db.getBinaryMetadata("CONTACT", "DOCUMENT", pSourceContactId, true, SqlUtils.getBinariesAlias()); - metaData.forEach(function (binaryMetaData) + DuplicateScannerUtils.deleteHasDuplicateEntries(pTargetEntity, [selfId]); + if(ids.length > 0) { - db.updateBinaryAssignment(binaryMetaData.id, "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); - }); - var deletedRows = db.deletes(deleteStatements) - - DuplicateScannerUtils.deleteCachedDuplicate(pSourceContactId); - - return deletedRows >= 2; -} - -/* - * Creates an array of arrays containing the entity field config paired with it's value. - * - * @param {[]} pDuplicateFieldsConfig An Array with the configured fields in the form of [ENTITY_FIELD, IS_ID, USE_FOR_SEARCH]. @see LoadDuplicateIndexFieldsConfiguration() - * @param {{"key", "value"}} pTargetRecordData One record containing the values for the configured fields. It's in the format of {"key(=EntityFieldName", "Value"} - * @return {[[]]} An array of arrays containing the entity field config an its value. ["{entityfield: FIRSTNAME}", "PETER"]] - * @example - * pDuplicateFieldsConfig - * ["CONTACTID", true, false] - * ["FIRSTNAME", false, true] - * - * pTargetRecordData - * ["d786045c-8b21-4f22-b6d9-72be9f61c04d", "PETER"] - * - * => ["{entityfield: FIRSTNAME}", "PETER"]] - */ -DuplicateScannerUtils.buildEntityFieldConfigs = function(pDuplicateFieldsConfig, pTargetRecordData) -{ - let INDEX_CONFIG_ENTITY_FIELD = 0; - let entityFieldConfigValuesRay = []; - /* - * Based on the parameterized filter field names and the values loaded via the query, - * an array which contains records in the style of ["FilterFieldName", "FilterFieldValueFromQuery"] gets created. - * This is mandatory to run the scan for this record. - */ - for (let i = 0; i < pDuplicateFieldsConfig.length; i++) - { - let fieldConfig = JSON.parse("{" + pDuplicateFieldsConfig[i] + "}"); - let entityField = fieldConfig.entityfield; - let entityFieldValue = pTargetRecordData[entityField]; - if(entityFieldValue == null) - entityFieldValue = ""; - else - entityFieldValue = indexsearch.escapeString(entityFieldValue).trim(); - - let exclude = fieldConfig.exclude; - if (exclude) - { - exclude.forEach(function (excludeValue) - { - entityFieldValue = " " + entityFieldValue + " "; - entityFieldValue = entityFieldValue.replace(new RegExp( " " + excludeValue + " ", "gi"), ""); - }); - } - - let valuelength = fieldConfig.length; - if ( valuelength ) - entityFieldValue = entityFieldValue.substr(0, parseInt(valuelength)) + "*"; - - let empty = fieldConfig.empty; - if ( !empty && entityFieldValue == "") - entityFieldValue = "*"; - - entityFieldValue = entityFieldValue.trim(); - entityFieldConfigValuesRay.push([pDuplicateFieldsConfig[i], entityFieldValue]); + DuplicateScannerUtils.insertHasDuplicateEntry(pTargetEntity, selfId, ids.length); } - return entityFieldConfigValuesRay.length > 0 ? entityFieldConfigValuesRay : [["", ""]]; -} - -DuplicateScannerUtils.getBlockSize = function() -{ - return project.getPreferenceValue("custom.duplicates.dataBlockSize", "5000"); -} - -DuplicateScannerUtils.getEntityFieldsFromConfig = function(pFilterName, pTargetEntity) -{ - let indexPattern = _DuplicateScannerUtils._loadIndexPattern(pFilterName, pTargetEntity); - if(indexPattern == null || indexPattern == "") - return []; - let fieldConfigs = _DuplicateScannerUtils._loadEntityFieldConfigsFromPattern(indexPattern); - if(fieldConfigs == null || fieldConfigs.length < 1) - return []; - let entityFields = _DuplicateScannerUtils._loadEntityFieldsFromFieldConfigs(fieldConfigs); - let entityIdField = _DuplicateScannerUtils._loadEntityIdField(pFilterName, pTargetEntity); - entityFields.push(entityIdField); - return entityFields; } /** - * Loads the configured entity fields required for the given duplicate scanner. + * Returns a DuplicateScanner object by the given entity name + * + * @param {string} pTargetEntity a entity name e.g.: Organisation_entity * - * @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> + * @returns {object} the scanner object */ -DuplicateScannerUtils.getEntityFieldObjectFromConfig = function (pFilterName, pTargetEntity) +DuplicateScannerUtils.getScannerByEntity = function(pTargetEntity) { - var indexPattern = _DuplicateScannerUtils._loadIndexPattern(pFilterName, pTargetEntity); - if (!indexPattern) - return null; - var fieldConfigs = _DuplicateScannerUtils._loadEntityFieldConfigsFromPattern(indexPattern); - if (fieldConfigs == null || fieldConfigs.length === 0) - return null; - + var duplicateScanner = newSelect([ + "DUPLICATESCANNER.ID_FIELD_NAME", + "DUPLICATESCANNER.SCAN_PATTERN" + ]) + .from("DUPLICATESCANNER") + .where("DUPLICATESCANNER.ENTITY_TO_SCAN_NAME", pTargetEntity).arrayRow(); + var filterObj = JSON.parse(duplicateScanner[1]).filter; return { - entityFields : _DuplicateScannerUtils._loadEntityFieldsFromFieldConfigs(fieldConfigs), - entityIdField : _DuplicateScannerUtils._loadEntityIdField(pFilterName, pTargetEntity) + idField: duplicateScanner[0], + filter: filterObj }; } -DuplicateScannerUtils.getUnrelatedRelationsForDuplicate = function(pDuplicateId) -{ - let unrelatedIds = []; - let duplicateIds = newSelect("SOURCEDUPLICATEID, UNRELATEDDUPLICATEID") - .from("UNRELATEDDUPLICATES") - .where("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pDuplicateId) - .or("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pDuplicateId) - .table(); - let sourceDuplicateId = ""; - let unrelatedDuplicateId = ""; - for (let i = 0; i < duplicateIds.length; i++) - { - sourceDuplicateId = duplicateIds[i][0]; - unrelatedDuplicateId = duplicateIds[i][1]; - - if(sourceDuplicateId != null && sourceDuplicateId != "" && sourceDuplicateId != pDuplicateId) - { - unrelatedIds.push(sourceDuplicateId); - } - else if(unrelatedDuplicateId != null && unrelatedDuplicateId != "" && unrelatedDuplicateId != pDuplicateId) - { - unrelatedIds.push(unrelatedDuplicateId); - } - } - return unrelatedIds; -} - -function _DuplicateScannerUtils() {} - -_DuplicateScannerUtils._loadEntityFieldsFromFieldConfigs = function(pEntityFieldConfigs) -{ - let fieldNames = []; - for (let i = 0; i < pEntityFieldConfigs.length; i++) - { - let fieldConfig = JSON.parse("{" + pEntityFieldConfigs[i] + "}"); - let entityField = fieldConfig.entityfield; - fieldNames.push(entityField); - } - return fieldNames; -} - -/* - * Gets the Pattern for the scanner - * A pattern usually contains placeholders in the style of "{entityFieldName]" +/** + * Calls the index search and returns the found duplicate ids * - * @param {String} pScannerName Name of the filter to use - * @param {String} pTargetEntity The target entity which has been assigned to the filters configuration - * @returns {String} Scan pattern as string - */ -_DuplicateScannerUtils._loadIndexPattern = function(pScannerName, pTargetEntity) -{ - let scanPattern = _DuplicateScannerUtils._filterToScanPattern( newSelect("SCAN_PATTERN") - .from("DUPLICATESCANNER") - .where("DUPLICATESCANNER.FILTER_NAME", pScannerName) - .and("DUPLICATESCANNER.ENTITY_TO_SCAN_NAME", pTargetEntity) - .cell()); - scanPattern = scanPattern.trim(); - return scanPattern; -} - - -_DuplicateScannerUtils._filterToScanPattern = function(filterString) { - if (filterString == "") - return ""; - try - { - var filter = JSON.parse(filterString); - } - catch (err) - { - return ""; - } - return _DuplicateScannerUtils._filterChildsToScanPattern(filter.filter.childs, filter.filter.operator); -} - -_DuplicateScannerUtils._filterChildsToScanPattern = function(childs, operator) { - let group = ""; - for (let i = 0; i < childs.length; i++) { - if (group != ""){ - group = group + " " + operator+ " "; - } - if (childs[i].type == "row"){ - group = group + _DuplicateScannerUtils._filterChildsRowToScanPattern(childs[i].name, childs[i].operator, childs[i].value); - } else if (childs[i].type == "group") { - group = group + "(" + _DuplicateScannerUtils._filterChildsToScanPattern(childs[i].childs, childs[i].operator) + ")"; - } - } - return group; -} - -_DuplicateScannerUtils._filterChildsRowToScanPattern = function (name, operator, value) { - let row = ""; - row = row + name.toLowerCase() + ':({"entityfield":"' + name + '"'; - switch (operator){ - case "NOT_EQUAL": - case "CONTAINSNOT": - row = row + ', "exclude":' + value; - case "ISNOTNULL": - row = row + ', "empty":' + false; - } - row = row + '})'; - return row; -} - -_DuplicateScannerUtils._loadEntityFieldConfigsFromPattern = function(indexPattern) -{ - return indexPattern.match(/[^{}]+(?=\})/g); -} - -_DuplicateScannerUtils._replacePlaceholderForValuesInPattern = function(pIndexPattern, pEntityFieldValueRays) -{ - let INDEX_ENTITY_FIELD_NAME = 0; - let INDEX_ENTITY_FIELD_VALUE = 1; - - let placeholder = ""; - let fieldValue = ""; - for (let i = 0; i < pEntityFieldValueRays.length; i++) - { - placeholder = "{" + pEntityFieldValueRays[i][INDEX_ENTITY_FIELD_NAME] + "}"; - fieldValue = pEntityFieldValueRays[i][INDEX_ENTITY_FIELD_VALUE]; - pIndexPattern = pIndexPattern.replace(placeholder, fieldValue); - } - return pIndexPattern; -} - -_DuplicateScannerUtils._loadEntityIdField = function(pFilterName, pTargetEntity) -{ - return newSelect("ID_FIELD_NAME") - .from("DUPLICATESCANNER") - .where("DUPLICATESCANNER.FILTER_NAME", pFilterName) - .and("DUPLICATESCANNER.ENTITY_TO_SCAN_NAME", pTargetEntity) - .cell(); -} - -/* - * @see DuplicateScannerUtils.scanForDuplicates for the documentation - */ -_DuplicateScannerUtils._scanForDuplicates = function(pTargetEntity, pEntityFieldConfigValuesRay, -pResultFields, pRecordIdValueToIgnore, pFormatValuesConsumeWebserviceCallback, pUseExternalWebservice, pIndexPattern) -{ - //No filterfields/indexpattern => No indexsearch - if(pEntityFieldConfigValuesRay.length < 1 || pIndexPattern == null || pIndexPattern == "") - return null; - - let possibleDuplicates = []; - let ignoreSourceRecordPattern = _DuplicateScannerUtils._getIgnoreSourceRecordPattern(pRecordIdValueToIgnore); - - let indexPatternWithValues = _DuplicateScannerUtils._replacePlaceholderForValuesInPattern(pIndexPattern, pEntityFieldConfigValuesRay); - - possibleDuplicates = _DuplicateScannerUtils._callIndexSearch(pTargetEntity, indexPatternWithValues, ignoreSourceRecordPattern, pResultFields, 100); - - if(possibleDuplicates == null) - return null; - - possibleDuplicates = possibleDuplicates[indexsearch.HITS]; - - if(pUseExternalWebservice && pFormatValuesConsumeWebserviceCallback != null) - possibleDuplicates = pFormatValuesConsumeWebserviceCallback.apply(this, [possibleDuplicates]); - - return possibleDuplicates; -} - -/* - * Returns a bool which say wether or not an external service should be used - * - * @param {String} pFilterName Name of the filter - * @param {String} pTargetEntity Entity which has been configured - * @returns {Bool} True = use, False = no use - */ -_DuplicateScannerUtils._isUseExternalWebservice = function(pFilterName, pTargetEntity) -{ - let isUseWebservice = newSelect("EXTERNAL_SERVICE_USAGE_ALLOWED") - .from("DUPLICATESCANNER") - .where("DUPLICATESCANNER.FILTER_NAME", pFilterName) - .and("DUPLICATESCANNER.ENTITY_TO_SCAN_NAME", pTargetEntity) - .cell(); - return isUseWebservice != 0; -} - -/* - * Runs the indexsearch based on the given parameters. - * If the "pEntityFieldValueRays" is empty, no search will be executed. + * @param {string} pTargetEntity search for duplicate in this entity e.g.: Organisation_entity + * @param {string} pIndexPattern the index pattern as string + * @param {string=} pSelfId optionally the own id (this will be excluded) * - * @param {String} pTargetEntity Entity which has been configured - * @param {String} pIndexPatternWithValues The pattern used to search. Has to contain the values already. - * @param {String} pIdFilter The filter pattern used to exclude the current record from the result. - * @param {String} pResultFields The result field config. Use "DuplicateScannerUtils.getResultFields" - * @param {String} pResultSetRows todo - * @returns {[["key", "value"]] || null} Array of Key-Value-Pairs based on the configured pResultFields, if no pattern exists null + * @returns {string[]} the duplicate ids */ -_DuplicateScannerUtils._callIndexSearch = function(pTargetEntity, pIndexPatternWithValues, pIdFilter, pResultFields, pResultSetRows) +DuplicateScannerUtils.getDuplicateIds = function(pTargetEntity, pIndexPattern, pSelfId) { - //logging.log(pIndexPatternWithValues) - //The indexPattern can't be null because it is required to run the search. - if(pIndexPatternWithValues == null || pIndexPatternWithValues == "") - return null; let indexQuery = indexsearch.createIndexQuery() - .setPattern(pIndexPatternWithValues) - .setEntities([pTargetEntity]) -// .addFilter(pIdFilter); - // .setDefaultOperator(indexsearch.OPERATOR_AND) - //.addSearchFields("Person_entity.FIRSTNAME", "Person_entity.LASTNAME", "Person_entity.CONTACTID") - //.setRows(pResultSetRows); - if(pIdFilter && pIdFilter.length > 0) - indexQuery.addFilter(pIdFilter); - - indexQuery = _DuplicateScannerUtils._setResultFields(indexQuery, pResultFields); - return indexsearch.searchIndex(indexQuery); -} - -/* - * Sets each field of the given array as resultfield on the given query. - * Supports indexsearch internal fields aswell - * (indexsearch.FIELD_ID, indexsearch.FIELD_TITLE, indexsearch.FIELD_TYPE, indexsearch.FIELD_DESCRIPTION) - * - * @param {IndexQuery} pIndexQuery An indexquery created with "indexsearch.createIndexQuery()" - * @param {String[]} pResultFields The result field config. Use "DuplicateScannerUtils.getResultFields" - * @returns {IndexQuery} IndexQuery with the resultfields set - */ -_DuplicateScannerUtils._setResultFields = function(pIndexQuery, pResultFields) -{ - let resultIndexFields = []; - let resultFields = []; - for (let i = 0; i < pResultFields.length; i++) + .setPattern(pIndexPattern) + .setEntities([pTargetEntity]); + if(pSelfId) { - if(pResultFields[i] == indexsearch.FIELD_ID - || pResultFields[i] == indexsearch.FIELD_TITLE - || pResultFields[i] == indexsearch.FIELD_TYPE - || pResultFields[i] == indexsearch.FIELD_DESCRIPTION) - { - resultIndexFields.push(pResultFields[i]); - } - else - resultFields.push(pResultFields[i]); + // creates -_local_id_:"<pSelfId>" + var optFilter = indexsearch.buildPattern(indexsearch.createPatternConfig().minus( + indexsearch.createPhraseTerm(pSelfId) + .setIndexField(indexsearch.FIELD_ID) + )); + indexQuery = indexQuery.addFilter(optFilter); } - if(resultIndexFields.length > 0) - pIndexQuery = pIndexQuery.addResultIndexFields(resultIndexFields); - - if(resultFields.length > 0) + var indexResult = indexsearch.searchIndex(indexQuery); + indexResult = indexResult[indexsearch.HITS]; + if(indexResult === null || indexResult.length === 0) { - pIndexQuery = pIndexQuery.addResultFields(resultFields); + return []; } - return pIndexQuery; -} - -/* - * Creates the queries to insert new duplicates into a new cluster based on the pDuplicatesRay - * - * @param {String[]} pDuplicatesRay Array of duplicate ids - * @param {String} pTargetEntity Entity which has been configured - * @returns {String[]} Array of insert queries - */ -_DuplicateScannerUtils._createInsertDuplicatesClusterQuery = function (pDuplicatesRay, pTargetEntity) -{ - return _DuplicateScannerUtils._createInsertDuplicatesClusterQuery(pDuplicatesRay, pTargetEntity, util.getNewUUID()); -} - -/* - * Creates the queries to insert new duplicates into a new cluster based on the pDuplicatesRay - * - * @param {String[]} pDuplicatesRay Array of duplicate ids - * @param {String} pTargetEntity Entity which has been configured - * @param {String} pClusterId Clusters id to which the duplicates are in relation - * @returns {String[]} Array of insert queries - */ -_DuplicateScannerUtils._createInsertDuplicatesClusterQuery = function (pDuplicatesRay, pTargetEntity, pClusterId) -{ - let duplicatesToInsertQueries = []; - let cols = ["ID", "CLUSTERID", "DUPLICATEID", "TARGET_ENTITY"]; - if (!pClusterId) - pClusterId = util.getNewUUID(); - - for (let i = 0; i < pDuplicatesRay.length; i++) + var duplicateIds = []; + indexResult.forEach(function(curr) { - let newId = util.getNewUUID(); - let valuesToInsert = [newId, pClusterId, pDuplicatesRay[i], pTargetEntity]; - - duplicatesToInsertQueries.push(["DUPLICATECLUSTERS", cols, null, valuesToInsert]); - } - return duplicatesToInsertQueries; -} - -/* - * Deletes all clusters - * - * @returns {String} Count of records deleted - */ -_DuplicateScannerUtils._deleteDuplicateClusters = function () -{ - return db.deleteData("DUPLICATECLUSTERS"); -} - -/* - * All records with contactId = sourceContactId get updated, which are not assigned to the same "group" as the targetContactId. - * This is because otherwise there would now be in total two "participants" in the same "group" as opposed to one before. - * Also if they already are in the same "group" those records shouldn't be updated because it would lead to the same outcome. - * - * @returns {Boolean} If records have been updated - */ -_DuplicateScannerUtils._migrateParticipantsToNewContact = function (pTableName, pContactIdColumn, pAssignableIdColumn, pSourceContactId, pTargetContactId) -{ - var excludedIds = newSelect(pAssignableIdColumn) - .from(pTableName) - .where([pTableName, pContactIdColumn], pTargetContactId) - .arrayColumn(); - - var updateCount = newWhereIfSet([pTableName, pAssignableIdColumn], excludedIds, SqlBuilder.NOT_IN()) - .and([pTableName, pContactIdColumn], pSourceContactId) - .updateFields(new Map().set(pContactIdColumn, pTargetContactId), pTableName); - - var deleteCount = newWhere([pTableName, pContactIdColumn], pSourceContactId) - .tableName(pTableName) - .deleteData(); - - return updateCount > 0 || deleteCount > 0; -} - -_DuplicateScannerUtils._buildDeleteOrganisationAndContactQuery = function(pSourceOrganisationId, pSourceContactId) -{ - return [ - newWhere("ORGANISATION.ORGANISATIONID", pSourceOrganisationId).buildDeleteStatement(), - newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement() - ]; -} - -_DuplicateScannerUtils._buildDeleteCachedUnrelatedDuplicateQuery = function(pSourceContactId) -{ - return [ - newWhere("UNRELATEDDUPLICATES.SOURCEDUPLICATEID", pSourceContactId).buildDeleteStatement(), - newWhere("UNRELATEDDUPLICATES.UNRELATEDDUPLICATEID", pSourceContactId).buildDeleteStatement() - ]; -} - -/* - * Creates a filter pattern which excludes results with the provided id from the search. - * - * @param {String} pRecordIdValueToIgnore The id value to exclude from the result. - * @returns {String} Filter pattern which excludes hits with the given id (index) from the result. - */ -_DuplicateScannerUtils._getIgnoreSourceRecordPattern = function(pRecordIdValueToIgnore) -{ - // creates -_local_id_:"<pRecordIdValueToIgnore>" - if(pRecordIdValueToIgnore && pRecordIdValueToIgnore.length > 0) - return indexsearch.buildPattern(indexsearch.createPatternConfig().minus( - indexsearch.createPhraseTerm(pRecordIdValueToIgnore) - .setIndexField(indexsearch.FIELD_ID) - )); - return null; -} - -_DuplicateScannerUtils._migrateLinkedContactData = function (pSourceContactId, pTargetContactId) -{ - var updateStatements = new Map(); - var currentAlias = db.getCurrentAlias(); - - var setStandardsStatements = []; - var [standardPhone, standardMail, standardAddressId] = newSelect([ - "(" + CommUtil.getStandardSubSqlPhone() + ")", - "(" + CommUtil.getStandardSubSqlMail() + ")", - "CONTACT.ADDRESS_ID" - ]) - .from("CONTACT") - .where("CONTACT.CONTACTID", pTargetContactId) - .arrayRow(); - - //if the targetContact already has a standard phone comm, set the comm from the sourceContact as not standard - if (standardPhone) - { - setStandardsStatements.push( - newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId) - .and("COMMUNICATION.MEDIUM_ID", CommUtil.getMediumIdsByCategory("PHONE"), SqlBuilder.IN()) - .buildUpdateStatement({"ISSTANDARD" : "0"}) - ); - } - if (standardMail) - { - setStandardsStatements.push( - newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId) - .and("COMMUNICATION.MEDIUM_ID", CommUtil.getMediumIdsByCategory("EMAIL"), SqlBuilder.IN()) - .buildUpdateStatement({"ISSTANDARD" : "0"}) - ); - } - //set the standardaddress of the sourceContact as standard of the targetContact if it doesn't have one set yet - if(!standardAddressId) - { - var sourceStandard = newSelect("CONTACT.ADDRESS_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pSourceContactId) - .cell(); - setStandardsStatements.push(newWhere("CONTACT.CONTACTID", pTargetContactId).buildUpdateStatement({"ADDRESS_ID": sourceStandard})); - } - - updateStatements.set(currentAlias, setStandardsStatements); - - _DuplicateScannerUtils._getLinkedTableInfos(pTargetContactId).forEach(function ([tableName, columnName, additionalCondition, dbAlias]) - { - if (!dbAlias) - dbAlias = currentAlias; - - if (!updateStatements.has(dbAlias)) - updateStatements.set(dbAlias, []); - var statements = updateStatements.get(dbAlias); - - var updateValues = {}; - updateValues[columnName] = pTargetContactId; - - var updateCondition = new SqlBuilder(dbAlias).where([tableName, columnName], pSourceContactId).andIfSet(additionalCondition); - - //push must be used here to keep the reference - statements.push(updateCondition.buildUpdateStatement(updateValues, tableName)); + duplicateIds.push(curr[indexsearch.FIELD_ID]); }); - - var totalChanges = 0; - - updateStatements.forEach(function (statements, alias) - { - totalChanges += db.updates(statements, alias); - }); - - totalChanges += newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId).deleteData(); //delete leftover communications from the source contact - totalChanges += new AttributeRelationQuery(pSourceContactId).deleteAllAttributes(); //delete leftover attributes - - return totalChanges > 0; -} - -/* - * Contains all Tables and their fields which may contain the contact id to be replaced for the data alias - * - * @returns {String[[]]} Array in the format [TableName, ContactIdColumnName, AdditionalCondition, alias] - */ -_DuplicateScannerUtils._getLinkedTableInfos = function(pTargetContactId) -{ - //don't use communications that the target already has - var targetComms = newSelect("COMMUNICATION.ADDR") - .from("COMMUNICATION") - .where("COMMUNICATION.CONTACT_ID", pTargetContactId) - .arrayColumn(); - - var communicationDedupCondition = targetComms.length > 0 - ? newWhere("COMMUNICATION.ADDR", targetComms, SqlBuilder.NOT_IN()) - : ""; - - return[ - ["AB_APPOINTMENTLINK", "OBJECT_ROWID"], - ["AB_CTILOG", "CONTACT_ID"], - ["AB_OBJECTRELATION", "AB_OBJECTRELATIONID"], - ["AB_OBJECTRELATION", "OBJECT1_ROWID"], - ["AB_OBJECTRELATION", "OBJECT2_ROWID"], - ["AB_LOGHISTORY", "TABLENAMEID"], - ["ADDRESS", "CONTACT_ID"], - ["BULKMAILRECIPIENT", "CONTACT_ID"], - ["BULKMAIL", "TESTING_CONTACT_ID"], - ["CAMPAIGN", "EMPLOYEE_CONTACT_ID"], - ["CAMPAIGNSTEP", "EMPLOYEE_CONTACT_ID"], - ["COMMRESTRICTION", "CONTACT_ID"], - ["COMMRESTRICTION", "EMPLOYEE_INVOLVED"], - ["COMMUNICATION", "CONTACT_ID", communicationDedupCondition], - ["COMPETITION", "CONTACT_ID"], - ["CONTRACT", "CONTACT_ID"], - ["LETTERRECIPIENT", "CONTACT_ID"], - ["OBJECTMEMBER", "CONTACT_ID"], - ["OFFER", "CONTACT_ID"], - ["PRODUCT", "CONTACT_ID"], - ["PRODUCTPRICE", "CONTACT_ID"], - ["SALESORDER", "CONTACT_ID"], - ["SALESPROJECT", "CONTACT_ID"], - ["TASK", "REQUESTOR_CONTACT_ID"], - ["TASK", "EDITOR_CONTACT_ID"], - ["TASKLINK", "OBJECT_ROWID"], - ["ACTIVITY", "RESPONSIBLE"], - ["DSGVO", "CONTACT_ID"], - ["DSGVOINFO", "CONTACT_ID"], - ["TIMETRACKING", "CONTACT_ID"], - ["ACTIVITYLINK", "OBJECT_ROWID"], - ["AB_ATTRIBUTERELATION", "OBJECT_ROWID"], - - ["ASYS_CALENDARLINK", "DBID", "", SqlUtils.getSystemAlias()] - ]; -} - - -/* - * Returns wether or not a value should be substring'd - * - * @return true if pCountCharsOfValueToUse is a number, greater than 0 and smaller than the values length - */ -_DuplicateScannerUtils._isValueLongerThanCharsToUse = function(pValueLength, pCountCharsOfValueToUse) -{ - return !isNaN(pCountCharsOfValueToUse) - && pCountCharsOfValueToUse > 0 - && pValueLength > pCountCharsOfValueToUse; -} - -/* - * Returns wether or not the parameter isnt null and a number or not - * - * @param {String} pCountCharsOfValueToUse Hopefully a number - * @returns {String} True if parameter isnt null and a number, False if it's null or no number - */ -_DuplicateScannerUtils._isNotNullAndANumber = function(pCountCharsOfValueToUse) -{ - return pCountCharsOfValueToUse != null && !isNaN(pCountCharsOfValueToUse); + return duplicateIds; } -/* - * Persons get reassigned to new organisation - * - * @returns {Boolean} If records have been updated +/** + * Does the same as getDuplicateIds but takes in a scanner (by entity) instead of an indexPattern + * + * @param {string} pTargetEntity the entity name used to get the Scanner + * @param {string} pTargetUid all variables will be loaded by this id + * + * @returns {string[]} the duplicate ids */ -_DuplicateScannerUtils._migratePersonsToNewOrganisation = function (pSourceOrganisationId, pTargetOrganisationId) +DuplicateScannerUtils.getDuplicateIdsByEntityScanner = function(pTargetEntity, pTargetUid) { - var updateCount = newWhereIfSet(["CONTACT", "ORGANISATION_ID"], pSourceOrganisationId) - .and("PERSON_ID is not null") - .updateFields(new Map().set("ORGANISATION_ID", pTargetOrganisationId), "CONTACT"); - - var deleteCount = newWhere(["CONTACT", "ORGANISATION_ID"], pSourceOrganisationId) - .and("PERSON_ID is not null") - .deleteData(); - return updateCount > 0 || deleteCount > 0; -} + var scanner = DuplicateScannerUtils.getScannerByEntity(pTargetEntity); + var indexsearchFilter = IndexsearchFilterUtils.fromFilter(scanner.filter); -/* - * Person and organisations keep the max_count = 1 attribute of the target and the one from the source is deleted - * - * @returns {Boolean} If records have been deleted - */ -_DuplicateScannerUtils._deleteUniqueAttributes = function (pSourceContactId, pTargetContactId) -{ - var targetAttrUnique = newSelect("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID") - .from("AB_ATTRIBUTERELATION") - .join("AB_ATTRIBUTEUSAGE on AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID = AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID") - .where("AB_ATTRIBUTEUSAGE.MAX_COUNT = 1") - .and("AB_ATTRIBUTERELATION.OBJECT_ROWID", pTargetContactId) - .table(); - var deleteCount = 0; + var targetLoadConfig = entities.createConfigForLoadingRows() + .entity(pTargetEntity) + .uid(pTargetUid) // Array.from because getFields returns a Set + .fields(Array.from(indexsearchFilter.getFields())); + var targetRow = entities.getRow(targetLoadConfig); - targetAttrUnique.forEach(function(attribute){ - deleteCount += newWhereIfSet("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", attribute) - .and("AB_ATTRIBUTERELATION.OBJECT_ROWID", pSourceContactId) - .deleteData(); - }); - - return deleteCount > 0; + var indexPattern = indexsearchFilter.buildQuery(targetRow); + return DuplicateScannerUtils.getDuplicateIds(pTargetEntity, indexPattern, pTargetUid); } -/* - * Update other contacts from the source - * - * @returns {Boolean} If records have been updated +/** + * Does the same as getDuplicateIdsByEntityScanner + * but the variables are directly passed in as an argument + * + * @param {string} pTargetEntity the entity name used to get the Scanner + * @param {object} pEntityObj the variables (should include all variables needed to build the indexPattern) + * + * @returns {string[]} the duplicate ids */ - _DuplicateScannerUtils._updateOtherContacts = function (pSourceContactId, sourcePersonId, targetPersonId) +DuplicateScannerUtils.getDuplicateIdsByEntityObj = function(pTargetEntity, pEntityObj) { - var otherContacts = newSelect("PERSON.PERSONID") - .from("PERSON") - .join("CONTACT", "CONTACT.PERSON_ID = PERSON.PERSONID") - .where("PERSON.PERSONID", sourcePersonId) - .and("CONTACT.CONTACTID", pSourceContactId, SqlBuilder.NOT_EQUAL()) - .table(); - var updateCount = 0; - - otherContacts.forEach(function(person){ - updateCount += newWhere("CONTACT.PERSON_ID", person) - .updateFields({"PERSON_ID" : targetPersonId}, "CONTACT"); - }); - - return updateCount > 0; -} \ No newline at end of file + var scanner = DuplicateScannerUtils.getScannerByEntity(pTargetEntity); + var indexsearchFilter = IndexsearchFilterUtils.fromFilter(scanner.filter); + var indexPattern = indexsearchFilter.buildQuery(pEntityObj); + return DuplicateScannerUtils.getDuplicateIds(pTargetEntity, indexPattern, pEntityObj[scanner.idField]); +} diff --git a/process/IndexSearch_lib/process.js b/process/IndexSearch_lib/process.js index be35ad2cc8c0a24fabe76bd57ba2a64d9ce9b310..549aefab097b21cce5c1053bf013ec1daa322be9 100644 --- a/process/IndexSearch_lib/process.js +++ b/process/IndexSearch_lib/process.js @@ -1,3 +1,4 @@ +import("system.indexsearch"); import("system.neon"); import("system.text"); @@ -160,4 +161,146 @@ IndexsearchUtils.createAffectedInfoContainer = function(changedIdValue, changedT } }); return res; -} \ No newline at end of file +} + + +/** + * Static utility class for constructing an indexsearch pattern + */ +function IndexsearchFilterUtils() {} + +/** + * Creates an indexsearch pattern from a filter + * + * @param {object} pFilter the filter in json representation + * + * @returns {IndexsearchFilterGroup|IndexsearchFilterRow} the IndexSearchFilter object + */ +IndexsearchFilterUtils.fromFilter = function(pFilter) +{ + if(pFilter["type"] == "group") + { + return IndexsearchFilterGroup.fromFilter(pFilter["childs"], pFilter["operator"]); + } + else if(pFilter["type"] == "row") + { + return IndexsearchFilterRow.fromFilter(pFilter["name"], pFilter["operator"], pFilter["value"]); + } + throw new Error("Unknown filter node type: " + pFilter["type"]); +} + + +/** + * The IndexsearchFilterGroup object represents the a filter group + * and is able to generate the corrosponding indexsearch pattern + * + * @param {(IndexsearchFilterGroup|IndexsearchFilterRow)[]} pChilds the child filterrows/filtergroups + * @param {string} pOperator the operator for the group can either be 'AND' or 'OR' + */ +function IndexsearchFilterGroup(pChilds, pOperator) +{ + this.childs = pChilds; + this.operator = pOperator; +} + +/** + * Returns all fiels as Set witch are required for this filter + * + * @returns {Set<string>} The fields as Set + */ +IndexsearchFilterGroup.prototype.getFields = function() +{ + var fields = new Set(); + for(let i = 0; i < this.childs.length; i++) + { + this.childs[i].getFields().forEach(fields.add, fields); + } + return fields; +} + +/** + * Builds the index pattern and returns it as string + * + * @param {Record<string, string>} pFieldValues the field name value pair + */ +IndexsearchFilterGroup.prototype.buildQuery = function(pFieldValues) +{ + return this.childs.map(function(curr) { return curr.buildQuery(pFieldValues); }) + .filter(function(curr) { return curr != null; }) + .join(" " + this.operator + " "); +} + +/** + * Creates a new IndexsearchFilterGroup object + * + * @param {(IndexsearchFilterGroup|IndexsearchFilterRow)[]} pChilds the child filterrows/filtergroups + * @param {string} pOperator the operator for the group can either be 'AND' or 'OR' + */ +IndexsearchFilterGroup.fromFilter = function(pChilds, pOperator) +{ + return new IndexsearchFilterGroup( + pChilds.map(function(curr) + { + return IndexsearchFilterUtils.fromFilter(curr); + }), + pOperator + ); +} + + +function IndexsearchFilterRow(pName, pEmpty, pExclude) +{ + this.name = pName; + this.empty = pEmpty; + this.exclude = pExclude; +} + +/** + * Returns all fiels as Set witch are required for this filter + * + * @returns {Set<string>} The fields as Set + */ +IndexsearchFilterRow.prototype.getFields = function() +{ + return new Set([this.name]); +} + +/** + * Builds the index pattern and returns it as string + * + * @param {Record<string, string>} pFieldValues the field name value pair + */ +IndexsearchFilterRow.prototype.buildQuery = function(pFieldValues) +{ + var fieldValue = pFieldValues[this.name] ? pFieldValues[this.name] : ""; + if(!this.empty && fieldValue == "") + { + return null; + } + var valueStr = fieldValue; + for(let i = 0; i < this.exclude.length; i++) + { + valueStr = valueStr.replace(new RegExp(this.exclude[i], "gi"), ""); + } + return this.name.toLowerCase() + ':("' + indexsearch.escapeString(valueStr.trim().replace(/\s+/g, "")) + '")'; +} + +/** + * Creates a new IndexsearchFilterGroup object + * + * @param {string} pName the row field name + * @param {string} pOperator the operator for the group can either be 'AND' or 'OR' + * @param {string} pValue the field valze + */ +IndexsearchFilterRow.fromFilter = function(pName, pOperator, pValue) +{ + if(pOperator == "NOT_EQUAL" || pOperator == "CONTAINSNOT") + { + return new IndexsearchFilterRow(pName, true, JSON.parse(pValue)); + } + else if(pOperator == "ISNOTNULL") + { + return new IndexsearchFilterRow(pName, false, []); + } + throw new Error("Unknown filterrow operator: " + pOperator); +} diff --git a/process/Leadimport_lib/process.js b/process/Leadimport_lib/process.js index d37c1f78ac48bda146e8791629958fe82d076044..1859595621f80e74f67d3b93cbd5959a5746cb60 100644 --- a/process/Leadimport_lib/process.js +++ b/process/Leadimport_lib/process.js @@ -918,19 +918,19 @@ LeadImportUtils.scanLeadDups = function(pAllContactData) if (orgObj != undefined && Object.keys(orgObj).length > 0)//checks if there is an ORGANISATIONDUPLICATE { - let scanResultsOrg = DuplicateScannerUtils.scanForDuplicates("OrganisationDuplicates", "Organisation_entity", orgObj, null); - if (scanResultsOrg != null && scanResultsOrg.length > 0) + let scanResultsOrg = DuplicateScannerUtils.getDuplicateIdsByEntityObj("Organisation_entity", orgObj); + if (scanResultsOrg.length > 0) { - let insertCountOrg = DuplicateScannerUtils.cacheNewScanResults(orgObj["CONTACTID"], scanResultsOrg, "Organisation_entity"); + DuplicateScannerUtils.insertHasDuplicateEntry("Organisation_entity", orgObj["CONTACTID"], scanResultsOrg); dupOrg = true; } } if (persObj != undefined && Object.keys(persObj).length > 0)//checks if there is an PERSONDUPLICATE { - let scanResultsPers = DuplicateScannerUtils.scanForDuplicates("PersonDuplicates", "Person_entity", persObj, null); - if (scanResultsPers != null && scanResultsPers.length > 0) + let scanResultsPers = DuplicateScannerUtils.getDuplicateIdsByEntityObj("Person_entity", persObj); + if (scanResultsPers.length > 0) { - let insertCountPers = DuplicateScannerUtils.cacheNewScanResults(persObj["CONTACTID"], scanResultsPers, "Person_entity"); + DuplicateScannerUtils.insertHasDuplicateEntry("Person_entity", persObj["CONTACTID"], scanResultsPers); dupPers = true; } } diff --git a/process/RebuildAllDuplicateCaches_serverProcess/process.js b/process/RebuildAllDuplicateCaches_serverProcess/process.js deleted file mode 100644 index b08a0abf9efa74df1e609510b0cd09d63a06d1bf..0000000000000000000000000000000000000000 --- a/process/RebuildAllDuplicateCaches_serverProcess/process.js +++ /dev/null @@ -1,55 +0,0 @@ -import("system.logging"); -import("DuplicateScanner_lib"); - -/* - * Serverprocess to reload duplicates - * - * Step #1: - * All Clusters for the specified targetEntity are deleted. - * Relations between records marked as unrelated are not deleted at this time - * - * Step #2 - * The duplicates cache gets rebuilt - * - * Step #3: - * All unrelated relations between duplicate ids hold their respective cluster id. - * If the same combination of duplicate ids exists in the duplicate cache after the rebuild, - * it is assumed that those records remain duplicates. - * Therefore the saved clusterId gets refreshed with the newly created clusters id. - * - * Relations between unrelated duplicates are deleted, if no cluster contains the same combination of duplicate ids - */ - -// Build Person duplicate cache - -var filterName = "PersonDuplicates"; -var targetEntity = "Person_entity"; -var recordBlockSize = DuplicateScannerUtils.getBlockSize(); - -logging.log(filterName + ": Delete duplicates -> "); -DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity); - -logging.log(filterName + ": Recalculate duplicates -> "); -DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, recordBlockSize, null); - -logging.log(filterName + ": Refresh unrelated duplicates -> "); -DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - -logging.log(filterName + ": Done rebuilding "); - - -// Build Organisation duplicate cache - -filterName = "OrganisationDuplicates"; -targetEntity = "Organisation_entity"; - -logging.log(filterName + ": Delete duplicates -> "); -DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity); - -logging.log(filterName + ": Recalculate duplicates -> "); -DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, recordBlockSize, null); - -logging.log(filterName + ": Refresh unrelated duplicates -> "); -DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - -logging.log(filterName + ": Done rebuilding "); \ No newline at end of file diff --git a/process/RebuildDuplicatesCache_serverProcess/process.js b/process/RebuildDuplicatesCache_serverProcess/process.js deleted file mode 100644 index 1e7f532f75a8b4a36d1c06de9cc4503bf6f74c62..0000000000000000000000000000000000000000 --- a/process/RebuildDuplicatesCache_serverProcess/process.js +++ /dev/null @@ -1,40 +0,0 @@ -import("system.logging"); -import("system.vars"); -import("DuplicateScanner_lib"); - -/* - * Serverprocess to reload duplicates - * - * Step #1: - * All Clusters for the specified targetEntity are deleted. - * Relations between records marked as unrelated are not deleted at this time - * - * Step #2 - * The duplicates cache gets rebuilt - * - * Step #3: - * All unrelated relations between duplicate ids hold their respective cluster id. - * If the same combination of duplicate ids exists in the duplicate cache after the rebuild, - * it is assumed that those records remain duplicates. - * Therefore the saved clusterId gets refreshed with the newly created clusters id. - * - * Relations between unrelated duplicates are deleted, if no cluster contains the same combination of duplicate ids - */ - -// Build Person duplicate cache - -var filterName = vars.get("$local.filterName"); -var targetEntity = vars.get("$local.targetEntity"); -var recordBlockSize = DuplicateScannerUtils.getBlockSize(); - -logging.log(filterName + ": Delete duplicates -> "); -DuplicateScannerUtils.deleteClustersByTargetEntity(targetEntity); - -logging.log(filterName + ": Recalculate duplicates -> "); -DuplicateScannerUtils.rebuildDuplicatesCache(filterName, targetEntity, recordBlockSize, null); - -logging.log(filterName + ": Refresh unrelated duplicates -> "); -DuplicateScannerUtils.refreshUnrelatedDuplicateRelations(targetEntity); - -logging.log(filterName + ": Done rebuilding "); - diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js index 119068f4adadba3b78e1bc061fde6a4c7d40d473..383117664ccc7b40a93923d2a82b7694222b9d79 100644 --- a/process/Sql_lib/process.js +++ b/process/Sql_lib/process.js @@ -4657,4 +4657,33 @@ SqlUtils.escapeVars = function (pValue) if (typeof(pValue) == "string" && pValue.charAt(0) == "$") return "$" + pValue; return pValue; -} \ No newline at end of file +} + +SqlUtils.getSqlConditionalOperator = function(pOperator) +{ + switch(parseInt(pOperator)) + { + case 1: + return SqlBuilder.EQUAL(); + case 2: + return SqlBuilder.NOT_EQUAL(); + + case 4: + return SqlBuilder.LESS(); + case 5: + return SqlBuilder.LESS_OR_EQUAL(); + + case 3: + return SqlBuilder.GREATER(); + case 6: + return SqlBuilder.GREATER_OR_EQUAL(); + + case 11: + return "# is not null"; + case 12: + return "# is null"; + + default: + throw new Error("Unsupported operator " + pOperator); + } +} diff --git a/process/rebuildDuplicates_serverProcess/process.js b/process/rebuildDuplicates_serverProcess/process.js new file mode 100644 index 0000000000000000000000000000000000000000..14369cff4552605e18c828601491e101ceced696 --- /dev/null +++ b/process/rebuildDuplicates_serverProcess/process.js @@ -0,0 +1,74 @@ +import("Sql_lib"); +import("system.db"); +import("system.entities"); +import("system.project"); +import("system.util"); +import("system.logging"); +import("system.notification"); +import("system.translate"); +import("system.vars"); +import("IndexSearch_lib"); +import("DuplicateScanner_lib"); + +var pFilterName = vars.get("$local.filterName"); +var pTargetEntity = vars.get("$local.targetEntity"); +var pTargetIdField = vars.get("$local.targetIdField"); +var pFilter = JSON.parse(vars.get("$local.filter")); + +newWhere("HASDUPLICATE.OBJECT_TYPE", pTargetEntity).deleteData(); +var indexsearchFilter = IndexsearchFilterUtils.fromFilter(pFilter); + +var TABLE_NAME = "HASDUPLICATE"; +var COLUMNS = [ + "HASDUPLICATEID", + "OBJECT_TYPE", "OBJECT_ROWID", + "DUPLICATECOUNT" +]; +var COLUMN_TYPES = db.getColumnTypes(TABLE_NAME, COLUMNS); + +var batchSize = parseInt(project.getPreferenceValue("custom.duplicates.dataBlockSize", "5000")); +var batchPos = 0; +while(true) +{ + var inserts = []; + + var entityFields = indexsearchFilter.getFields(); + entityFields.add(pTargetIdField); + var entityRowsConfig = entities.createConfigForLoadingRows() + .entity(pTargetEntity) + .fields(Array.from(entityFields)) + .startrow(batchPos).count(batchSize); + var entityRows = entities.getRows(entityRowsConfig); + + entityRows.forEach(function(entityRow) + { + var currEntityRowId = entityRow[pTargetIdField]; + var indexPattern = indexsearchFilter.buildQuery(entityRow); + var duplicates = DuplicateScannerUtils.getDuplicateIds(pTargetEntity, indexPattern, currEntityRowId); + + if(duplicates.length > 0) + { + var values = [ + util.getNewUUID(), + pTargetEntity, currEntityRowId, + duplicates.length.toFixed(0) + ]; + inserts.push([TABLE_NAME, COLUMNS, COLUMN_TYPES, values]); + } + }); + + db.inserts(inserts); + if(entityRows.length < batchSize) + { + break; + } + batchPos += batchSize; +} + +notification.addNotificationWith(notification.createConfig() +.addUserWithId(vars.get("$sys.user")) +.notificationType("_____SYSTEM_NOTIFICATION_MESSAGE") +.caption(translate.text("Duplicaterow rebuild")) +.description(translate.withArguments("The duplicate row corrosponding to %0 has been rebuild", [pFilterName]))); + +logging.log(pFilterName + " has been rebuild"); diff --git a/process/RebuildAllDuplicateCaches_serverProcess/RebuildAllDuplicateCaches_serverProcess.aod b/process/rebuildDuplicates_serverProcess/rebuildDuplicates_serverProcess.aod similarity index 50% rename from process/RebuildAllDuplicateCaches_serverProcess/RebuildAllDuplicateCaches_serverProcess.aod rename to process/rebuildDuplicates_serverProcess/rebuildDuplicates_serverProcess.aod index 11432054cc7356358ef25e9d7b657ca5973bba2f..3a72b0bfc4f0fb92cacba8dbda2b2d68acbd3386 100644 --- a/process/RebuildAllDuplicateCaches_serverProcess/RebuildAllDuplicateCaches_serverProcess.aod +++ b/process/rebuildDuplicates_serverProcess/rebuildDuplicates_serverProcess.aod @@ -1,8 +1,12 @@ <?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.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> - <name>RebuildAllDuplicateCaches_serverProcess</name> + <name>rebuildDuplicates_serverProcess</name> + <description>ATTENTION! +This process can only be executed within the client. +The action (Rebuild selected entries) can be found in the administration context "Duplicate Configuration".</description> <majorModelMode>DISTRIBUTED</majorModelMode> - <process>%aditoprj%/process/RebuildAllDuplicateCaches_serverProcess/process.js</process> + <process>%aditoprj%/process/rebuildDuplicates_serverProcess/process.js</process> + <alias>Data_alias</alias> <variants> <element>EXECUTABLE</element> </variants>