From 0f67a53bfc1a869f2ba46d5dc356360b397aded2 Mon Sep 17 00:00:00 2001
From: "j.goderbauer" <j.goderbauer@adito.de>
Date: Thu, 21 Nov 2019 15:26:38 +0100
Subject: [PATCH] =?UTF-8?q?[Projekt:=20Entwicklung=20-=20Neon][TicketNr.:?=
 =?UTF-8?q?=201046306][CTI=20-=20Reverse=20suche=20=C3=BCber=20Index]=20#?=
 =?UTF-8?q?=20Conflicts:=20#=09entity/Organisation=5Fentity/recordcontaine?=
 =?UTF-8?q?rs/index/query.js=20#=09entity/Person=5Fentity/recordcontainers?=
 =?UTF-8?q?/index/query.js=20#=09process/IncomingCallExecutor=5Flib/proces?=
 =?UTF-8?q?s.js?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../recordcontainers/index/query.js           |  8 +-
 .../recordcontainers/index/query.js           |  8 +-
 process/IncomingCallExecutor_lib/process.js   | 97 +++++++++++++++----
 process/ctiServerEvents/process.js            | 14 ++-
 4 files changed, 103 insertions(+), 24 deletions(-)

diff --git a/entity/Organisation_entity/recordcontainers/index/query.js b/entity/Organisation_entity/recordcontainers/index/query.js
index bb097e6fc4..0dc0d18978 100644
--- a/entity/Organisation_entity/recordcontainers/index/query.js
+++ b/entity/Organisation_entity/recordcontainers/index/query.js
@@ -6,7 +6,13 @@ import("system.db");
 import("Sql_lib");
 import("Communication_lib");
 
-//TODO: refactor to new SqlBuilder
+/*
+Before changing the results that are returned here:
+Please keep in mind that some of the indexfields are used in other modules (like the duplicate-scanner or cti-call-handler). 
+You may want to check out if your change affects other modules. However adding more fields should not be a problem therefor.
+ */
+
+var sqlQuery, sqlHelper, queryCondition, affectedIds;
 var CommMediumPhoneIds = db.array( db.COLUMN, "select KEYID from AB_KEYWORD_ENTRY join ab_keyword_attributerelation on AB_KEYWORD_ENTRYID = AB_KEYWORD_ENTRY_ID "
         + " join ab_keyword_attribute on AB_KEYWORD_ATTRIBUTEID = AB_KEYWORD_ATTRIBUTE_ID and CHAR_VALUE = 'TELEPHONE' and AB_KEYWORD_ATTRIBUTE.CONTAINER = 'CommunicationMedium'");
 var sqlHelper = new SqlMaskingUtils();
diff --git a/entity/Person_entity/recordcontainers/index/query.js b/entity/Person_entity/recordcontainers/index/query.js
index 67167891a8..d3029e1734 100644
--- a/entity/Person_entity/recordcontainers/index/query.js
+++ b/entity/Person_entity/recordcontainers/index/query.js
@@ -6,7 +6,13 @@ import("system.db");
 import("Sql_lib");
 import("Communication_lib");
 
-//TODO: refactor to new SqlBuilder
+/*
+Before changing the results that are returned here:
+Please keep in mind that some of the indexfields are used in other modules (like the duplicate-scanner or cti-call-handler). 
+You may want to check out if your change affects other modules. However adding more fields should not be a problem therefor.
+ */
+
+var sqlQuery, sqlHelper, queryCondition, affectedIds;
 var CommMediumPhoneIds = db.array( db.COLUMN, "select KEYID from AB_KEYWORD_ENTRY join ab_keyword_attributerelation on AB_KEYWORD_ENTRYID = AB_KEYWORD_ENTRY_ID "
         + " join ab_keyword_attribute on AB_KEYWORD_ATTRIBUTEID = AB_KEYWORD_ATTRIBUTE_ID and CHAR_VALUE = 'TELEPHONE' and AB_KEYWORD_ATTRIBUTE.CONTAINER = 'CommunicationMedium'");
 var sqlHelper = new SqlMaskingUtils();
diff --git a/process/IncomingCallExecutor_lib/process.js b/process/IncomingCallExecutor_lib/process.js
index eb8b65edb8..1a606d9a62 100644
--- a/process/IncomingCallExecutor_lib/process.js
+++ b/process/IncomingCallExecutor_lib/process.js
@@ -1,3 +1,4 @@
+import("system.entities");
 import("system.datetime");
 import("system.util");
 import("system.notification");
@@ -7,9 +8,36 @@ import("system.tools");
 import("system.db");
 import("Sql_lib");
 import("system.cti");
+import("system.indexsearch");
 
-//TODO: comment library
+//TODO: comment library completely
 
+/**
+ * object for processing cti-calls
+ * Within the constructor, data is collected but not further processed. To perform operations, different methods are provide (lke the .execute-function)
+ *
+ * @class 
+ * @param {Object} pCallData object with all the basic call information, the object needs the following parameters: <ul>
+ * 	<li>action:for example: vars.get("$local.action")                                                                </li>
+ * 	<li>callId:uid of the call, requried, for example: vars.get("$local.callID")                                                                </li>
+ * 	<li>localAddress:the target phone address, for example: vars.get("$local.localAddress")                                                    </li>
+ * 	<li>localId:id of the call target, for example: vars.get("$local.localID")                                                              </li>
+ * 	<li>callAddress:the source phone address, for example: vars.get("$local.callAddress")                                                      </li>
+ * 	<li>isIncomingCall:Boolean:is the call an incoming or outgoing call, for example: vars.getString("$local.callIsIncoming") == "true"                                </li>
+ * 	<li>state:Number:callstate-constant (Ringing, talking, etc.), for example: Number(vars.get("$local.callState"))                                                      </li>
+ * 	<li>privateData:Object: additional telephony data when the tapi does provide it, for example: vars.exists("$local.privateData2") ? vars.get("$local.privateData2") : null         </li>
+ * 	<li>isConnectedCall:Boolean:specifies if the call was redirected ("connected") from somebody else</li>
+ *   </ul>
+ * 
+ * @example 
+ * 
+ * var ic = new IncomingCallExecutor(callData);
+ * //most of the logic happens in the handler-fuctions
+ * ic.setHandlerRinging(ringingHandlerFn);
+ * ic.setHandlerTalking(talkingHandlerFn);
+ * ic.setHandlerDisconnect(disconnectingHandlerFn);
+ * ic.execute();
+ */
 function IncomingCallExecutor(pCallData)
 {
     this.callData = pCallData;
@@ -98,9 +126,9 @@ IncomingCallExecutor.prototype.getNotificationBaseConfig = function(pUserName)
         return null;
     
     var notificationConfig = notification.createConfig()
-        .addUserWithId(pUserName)
-        .contentId(this.callData.callId)
-        .notificationType("_____SYSTEM_NOTIFICATION_PHONECALL");
+    .addUserWithId(pUserName)
+    .contentId(this.callData.callId)//group all notifications of one call together
+    .notificationType("_____SYSTEM_NOTIFICATION_PHONECALL");
                                          
     return notificationConfig;
 };
@@ -180,22 +208,49 @@ IncomingCallExecutor._callstateToText = function(pCallstate)
 IncomingCallExecutor._getContactsFromNumber = function(pNumber, pContactIds)
 {
     var phoneNumber = pNumber;
-    if (!phoneNumber && !pContactIds)
+    if (!phoneNumber && (!pContactIds || pContactIds.length == 0))//either of one needs to be specified, otherwise we wouldn't know what to search
         return [];
-    /*
-    var config = entities.createConfigForLoadingRows().entity("Communication_entity")
-                                                      .fields(["CONTACT_ID"])
-                                                      .provider("PhoneCommunications")
-                                                      .addParameter("Address_param", phoneNumber);
-    var rows = entities.getRows(config);
-    var contactIds = rows.map(function (e){
-        return e.CONTACT_ID;
-    });
     
-    config = entities.createConfigForLoadingRows().entity("AnyContact_entity")
-                                                  .fields(["CONTACTID", "ORGANISATION_ID", "ORGANISATION_NAME", "PERSON_ID", "ISOLANGUAGE", "PERSON_FULL_NAME"])
-                                                  .uids(contactIds);
-    rows = entities.getRows();
+    var contactIds = pContactIds || [];
+
+    if (pNumber)
+    {
+        /*
+        Searching for a number is done via the index because
+        - it's fast
+        - there is no need to sanitize stored phone-addresses or the phone-address we are looking for 
+          (this is done by the api automatically when the indexFieldType is configured as "TELEPHONE")
+        - the pattern and terms can be configured very detailed (fuzzy, wildcards, boosting) etc. to ensure that the best results are delivered
+          (currently a basic search is done because this has proven to return the best results till now; however this may need to be adjusted to a connected telephy-system)
+        
+        Searching the index is done via the indexsearch-methods and not an index-record container because we need to serach for both personContacts 
+        and organisationContacts. There exists a entity "AnyContact_entity" which represents personContacts- and organisationContacts-data 
+        but that entity has no index record container defined at the moment. So instead searching multiple IndexGroups is done here.
+        Because different indexGroup store data differently, we cannot directly load usage-data like the organisation-name, person-name and so on
+        from the index. Instead we are only loading the ID-field (which is the contactId in both groups) and then load the usage-data for these
+        contactIds.
+        */
+        var patternConfig = indexsearch.createPatternConfig();
+        var searchTerm = indexsearch.createTerm(pNumber).setIndexField("phone");
+        patternConfig.plus(searchTerm);
+        var pattern = indexsearch.buildPatternString(patternConfig);
+        var indexQuery = indexsearch.createIndexQuery().setPattern(pattern)
+                                                       .addIndexGroups("Person", "Organisation")
+                                                       .addResultIndexFields([indexsearch.FIELD_ID]);
+        var indexResult = indexsearch.searchIndex(indexQuery);
+        if (indexResult.HITS)
+            contactIds = contactIds.concat(indexResult.HITS.map(function (e){return e[indexsearch.FIELD_ID];}));
+    }
+    
+    if (contactIds.length == 0)
+        return [];
+    
+    //load entities does not work in serverProcesses at the moment, so instead use a traditional sql-query: //TODO: change this after #1047680 is done
+    /*
+    var config = entities.createConfigForLoadingRows().entity("AnyContact_entity")
+                                                      .fields(["CONTACTID", "ORGANISATION_ID", "ORGANISATION_NAME", "PERSON_ID", "ISOLANGUAGE", "PERSON_FULL_NAME"])
+                                                      .uids(contactIds);
+    rows = entities.getRows(config);
     return rows;
     */
     //load entities does not work here, so use instead a traditional sql-query:
@@ -204,12 +259,12 @@ IncomingCallExecutor._getContactsFromNumber = function(pNumber, pContactIds)
                         .join("ORGANISATION", "ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID")
                         .leftJoin("PERSON", "CONTACT.PERSON_ID = PERSON.PERSONID")
                         .join("COMMUNICATION", "COMMUNICATION.CONTACT_ID = CONTACT.CONTACTID")
-                        .whereIfSet("COMMUNICATION.ADDR", phoneNumber)
+                        .where("CONTACT.STATUS", $KeywordRegistry.contactStatus$active())
                         .and("COMMUNICATION.CONTACT_ID", pContactIds, SqlBuilder.IN())
-                        .and("CONTACT.STATUS", $KeywordRegistry.contactStatus$active())
                         .table();
     
-    return contacts.map(function (e){//map to the result how the entities-methods would return it to have less effort later
+    //map to the result how the entities-methods would return it to have less effort later when the mentioned ticket is done
+    return contacts.map(function (e){
         return {
             CONTACTID: e[0],
             ORGANISATION_ID: e[1], 
diff --git a/process/ctiServerEvents/process.js b/process/ctiServerEvents/process.js
index d199d0b837..cceed473a5 100644
--- a/process/ctiServerEvents/process.js
+++ b/process/ctiServerEvents/process.js
@@ -62,7 +62,7 @@ var ringingHandlerFn = function()
                 notificationConfig.linkInfo(text.encodeMS([affectedContext, affectedContactId]));
             }
 
-
+            
             notification.addNotificationWith(notificationConfig);
             return null;
         }, this);
@@ -140,6 +140,14 @@ var disconnectingHandlerFn = function()
         var notificationConfig = this.getNotificationBaseConfig(userObj[tools.NAME]);
         if (notificationConfig == null)
             return null;
+
+        if (this.contactsCall.length > 0)
+        {
+            var affectedContext = this.contactsCall[0].PERSON_ID.trim() == "" ? "Organisation" : "Person";
+            var affectedContactId = this.contactsCall[0].CONTACTID;
+            notificationConfig.linkInfo(text.encodeMS([affectedContext, affectedContactId]));
+        }
+        
         notificationConfig.description(desc).caption(title);
 
         notification.addNotificationWith(notificationConfig);
@@ -162,6 +170,10 @@ var callData = {
     ,isConnectedCall: false
 };
 
+//for testing only:
+//callData.callAddress = "01731858728";
+//callData.localAddress = "PJSIP/212";
+
 var ic = new IncomingCallExecutor(callData);
 //ic.logData();
 ic.setHandlerRinging(ringingHandlerFn);
-- 
GitLab