Since version 2.0 you can do your work before, after, or instead of the caller ID lookup.
You can create a contact upstream if you need it before searching for it using our method, for example.
It is also possible to create or search for a case and return this ID instead of our search.
Or if you want, you can redesign the entire search yourself to suit your own needs, e.g. different searches depending on the operator profile.
2 - Activate my redefinitions
Create a class that inherits from AxiaVM.Extension:
global class MyAxialysExtension extends AxiaVM.Extension {
// code d'extension ici
}
To use this code extension you must insert in the table AxiaVM__ConfigCTI__c an element with
Name = 'redefined_class'and AxiaVM__value__c = 'MyAxialysExtension'
The search receives a serialised JSON as input and returns an ID list.
The json currently contains two values:
{
"e164": "[numéro_appelant]" , // numéro en String de l'appelant, ex : 33123456789
"numSVI": "[numéro_appelé]" // numéro en String du SVI appelé
}
The return must be a List even if there is only one item in it.
Example of redefinition
In this example, we want to create a case and open it instead of the contact.
We will run the parent code, create the case, and return the ID for this case.
global class MyAxialysExtension extends AxiaVM.Extension {
/**
* @description Retourne une liste d'Id d'objet à ouvrir.
*
* - un seul élément retourné sera ouvert
* - plusieurs va déclencher une ouverture de la recherche avec le numéro de téléphone
*
* @param params Chaine en Json sérialisé qui contient les paramètres
* @return List
*/
global override List getIdsByPhone(String params) {
// si je veux récupérer les champs du paramètre js
Map tmp = (Map) JSON.deserializeUntyped(params);
String e164 = (String) tmp.get('e164');
String numSVI = (String) tmp.get('numSVI');
// On peut faire du travail en amont
// On peut récupérer le résultat de la méthode parente
List s = super.getIdsByPhone(params);
// On peut aussi faire quelque chose après pour changer le retours ou ajouter un Objet
Case c = new Case(
Subject = 'Appel en cours',
Origin = 'Phone',
ContactId = s[0],
Status = 'Working',
SVI__c = numSVI
);
insert c;
return new List{ c.Id };
}
}
Version greater than 2.30
SeekPhone class overview
In the Voice-management connector, SeekPhone is used to search for the caller’s number in the different possible objects (Account , Contact, Lead) and return the result found to the Voice-management window. Depending on the options, you can choose:
< ul dir="auto" data-sourcepos="91:1-94:0">
create form if not found
< li data-sourcepos="92:1-92:34">open a search window
do nothing at all.
global class SeekPhone {
/**
* Recherche la fiche de l'appelant et permet plusieurs comportement différents en retour
* @param js JSON Sérialisé, (voir plus bas)
* @return JSON sérialisé. Il est conseillé d'utiliser le helper SeekPhone.response
*/
public static String get(string js) {
// travail de recherche
return SeekPhone.response('OBJECT', (String) objectId);
}
/**
* Helper qui fournit une réponse en JSON sérialisé pour retours de la méthode `get`
* @params type Type d'ouverture que l'on va demander au voicemanagement pour l'appel
* valeurs possibles : 'OBJECT'|'SEARCH'|'URL'|'NOTHING'
* @param value id ou numéro de téléphone à rechercher
*/
global static String response (String type, String value) {
return json.serialize(new Map {
'type' => type,
'values' => new List{value}
});
}
}
The feedback conditions the output display of Voice-management:
OBJECT: Opens a Salesforce object regardless of its class. values is a single element array that contains the object id.
SEARCH : Opens the search regardless of the result. values is a one-element array that contains the search criteria (e.g. caller number).
URL: Opens a page on a Lightning component in your organization. values is a single element array that contains the url to open.
NOTHING code>: Does nothing at all.
Redefining the Behavior of SeekPhone.get
To do upstream, downstream work , or instead of SeekPhone.get, it is possible to use inheritance of this sort
global class MyAxialysExtension extends AxiaVM.Extension {
global override String seekPhone(String js) {
// ici je peux faire du travail en amont
[...]
// execution du code parent (optionnel)
String s = super.seekPhone(js);
// ici je peux faire du travail en aval
[...]
// selon mon retour je peux changer complètement le comportement
return AxiaVM.SeekPhone.response('SOBJECT|URL|SEARCH...', value);
}
}
The elements present in the js variable are as follows:
global class CaseExtensionExample extends AxiaVM.Extension{
global override String seekPhone(String params) {
// We need the results of parent call
String previous = super.seekPhone(params);
Map res = (Map) JSON.deserializeUntyped(previous);
String result_type = (String) res.get('type');
List
4 - Method of adding Task
Version greater than 2.0
The search receives a serialised JSON as input and returns an ID list.
The json currently contains two values:
{
"e164": "[numéro_appelant]" , // numéro en String de l'appelant, ex : 33123456789
"dt": "[date_appel]", // date de l'appel au format DateTime apex
"id_appel": "[id_appel_voice]", // id de l'appel du voice management
"inOut": "[appel_entrant_sortant]", // "in" ou "out" sens de l'appel
"op_email": "[email_operateur]", // email de l'opérateur pour attribution
"groupe": "[groupe]", // nom du groupe qui à eu l'appel
"duree": "[temps_de_l_appel]", // temps de l'appel en secondes
"rec": "[presence_enregistrement]" // Booléen qui détermine la présence d'un enregistrement
}
The return must be the Id of the task.
Example of redefinition
In this example, we want to add the task to the call and not to the caller’s contact. We will run the caller search code, find the associated case and attach the task to it.
global class MyNewExtension extends AxiaVM.Extension {
global override Id recordTask(String params) {
Map tmp = (Map) JSON.deserializeUntyped(params);
String e164 = (String) tmp.get('e164');
String op_email = (String) tmp.get('op_email');
String op_email = (String) tmp.get('op_email');
Datetime date = (Datetime) tmp.get('dt');
String inout = (String) tmp.get('inOut');
User[] operator = [SELECT Id FROM User WHERE FederationIdentifier =: op_email ];
List> tmp = [FIND :e164 IN PHONE FIELDS RETURNING Contact(Id)];
Id id_contact = tmp[0].Id;
Case c = [SELECT Id FROM Case WHERE ContactId =: id_contact];
Task task = new Task(
OwnerId = operator.Id,
CreatedDate = date,
WhatId = c.Id,
Subject = 'appel ' + inout
//[...]
);
insert task;
return new List{ task.Id };
}
}
Version greater than 2.30
Since this version there are two different JSONs depending on a call task or a sms sending task:< /p>
Invoke Task JSON
{
"e164": "numero appelant",
"id_call": "id de l'appel",
"inOut": "in|out",
"group_name": "Nom du groupe",
"call_duration": 123, // en secondes
"wait_duration": 0, // en secondes
"type": 'CALL',
"numSVI": "numéro du SVI",
"objectType": "Classe de l'objet dans lequel l'opérateur est ex : Account|Contact|Lead|Case...",
"recordId": "Id de l'objectType",
"url": "URL de l'objectType",
"mode": "task|cron",
"op_mail": "operator@email",
"begin_ts": 159269489, // timestamp du début de l'appel
"end_ts": 159269489, // timestamp de fin de l'appel
"transferred_to": "info de transfert"
}
SMS Task JSON
{
"e164": "numero appelant",
"content": "contenu du sms (optionnel)",
"success": true|false, // si tout c'est bien passé pendant l'envoie
"objectType": "Classe de l'objet dans lequel l'opérateur est ex : Account|Contact|Lead|Case...",
"recordId": "Id de l'objectType",
"url": "URL de l'objectType",
"type": "SMS",
"mode": "task",
"op_mail": "operator@email"
}
Example of redefining a Task
In this example, we want to add the task to the call and not to the caller’s Contact. We will run the caller lookup code and find the associated Case and attach the task to it.
global class MyNewExtension extends AxiaVM.Extension {
global override Id recordTask(String params) {
Map tmp = (Map) JSON.deserializeUntyped(params);
String e164 = (String) tmp.get('e164');
String op_mail = (String) tmp.get('op_mail');
String inout = (String) tmp.get('inOut');
String type = (String) tmp.get('type');
//[...]
if (type == 'CALL') {
User[] operator = [SELECT Id FROM User WHERE FederationIdentifier =: op_mail ];
List> tmp = [FIND :e164 IN PHONE FIELDS RETURNING Contact(Id)];
Id id_contact = tmp[0].Id;
Case c = [SELECT Id FROM Case WHERE ContactId =: id_contact];
Task task = new Task(
OwnerId = operator.Id,
WhatId = c.Id,
Subject = 'appel ' + inout == 'in' ? 'entrant' : 'sortant'
//[...]
);
insert task;
return task.Id ;
}
return super.recordTask(params)
}
}
Example to manage the personalAccount
In this example we do not want to add tasks, and create a personal account if we have no correspondence in the database.
global with sharing class PersonnalAccountExtension extends AxiaVM.Extension {
private static Pattern numPattern = Pattern.compile('^(([a-zA-Z]+:)?\\d{1,16})|([a-zA-Z]+)$');
global override Id recordTask(String params) {
// Don’t create task
return null ;
}
global override String seekPhone(String params) {
// We need the results of parent call
//
Map tmp = (Map) JSON.deserializeUntyped(
params
);
String e164 = (String) tmp.get('e164');
String previous = super.seekPhone(params);
Map res = (Map) JSON.deserializeUntyped(previous);
String result_type = (String) res.get('type');
List result_values = (List) res.get('values');
if( result_type == 'SOBJECT') {
return previous;
} else {
Id personAccountRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('PersonAccount').getRecordTypeId();
Account account = new Account();
account.RecordTypeId = personAccountRecordTypeId;
account.FirstName = 'Axialys';
account.LastName = 'Axialys';
account.PersonMailingStreet='Paris'; // fill with contact details
account.PersonMailingPostalCode='75015';
account.PersonMailingCity='Paris';
account.PersonHomePhone=e164;
account.PersonMobilePhone=e164;
insert account;
System.debug('CreationOK');
return AxiaVM.SeekPhone.response('SOBJECT', account.ID);
}
}
}
6 - Declare my extension class
This is a temporary procedure; in later versions a selector will be added to the Voice Management settings page.
We will indicate to the VoiceManagement connector that there is an extension class
To do this, a new item must be defined in the AxiaVM__ConfigCTI__c.
.table.
For example, for a class MyNewExtension which extends AxiaVM.Extension, you must create an item AxiaVM__ConfigCTI__c where Name = "redefined_class" et AxiaVM__value__c = "MyNewExtension".
6 - Tips for analyzing a problem
We advise you first to make sure that the class you want to use overrides the default one. If you create your own objects, set the object to create to “None”. Next, make sure you don’t have any checks in place that prevent the creation of the object you want to create.
Make sure your your class overrides the original one
< li data-sourcepos="63:2-63:351">Make sure that the name of the class created overrides the original method by adding a “System.debug”, or look in the logs of the “developer console”, loading, you should see lines like 12:15:18:373 VARIABLE_SCOPE_BEGIN [22]|this|PersonnalAccountExtension|true|false with the name of the overloaded class.
If you don’t see your class, please check via the workbench or via the console by running the following command that there is only ‘a “redefined_class” class and that it is well populated with the name you gave to your class.
Select Id, Name,AxiaVM__value__c From AxiaVM__ConfigCTI__c Where Name = 'redefined_class'
Make sure you don’t have blocking controls in place
Checks that could prevent certain operations from being carried out, which would not be defined, if for example you made it mandatory to configure an address on a customer file.
Finally, to help you debug, you can view the processing status of exchanges with salesforces by opening the development screen of your browser. You will be able to check the return status, and in case of failure the error that caused the problem.