Depuis la version 2.0 il est possible de faire du travail avant, après ou à la place de la recherche du numéro de l’appelant.
Il est possible de créer un Contact en amont si vous en avez besoin avant de le rechercher par notre méthode par exemple. Il est aussi possible de créer ou rechercher un Case et de retourner cet Id au lieu de notre recherche. Ou alors de complètement revoir vous même l’intégralité de la recherche selon vos propres besoins ex : recherche différente en fonction du profil de l’opérateur.
2 - Activer mes redéfinitions
Créer une classe qui hérite de AxiaVM.Extension :
global class MyAxialysExtension extends AxiaVM.Extension {
// code d'extension ici
}
Pour utiliser cette extension de code il faut insérer dans la table AxiaVM__ConfigCTI__c un élément avec
Name = 'redefined_class'et AxiaVM__value__c = 'MyAxialysExtension'
3 - Méthode de recherche (appel entrant / sortant)
Version supérieure à 2.0
La recherche reçoit un JSON sérialisé en entrée et renvoie une Liste d’Id. Le json contient actuellement deux valeurs :
{
"e164": "[numéro_appelant]" , // numéro en String de l'appelant, ex : 33123456789
"numSVI": "[numéro_appelé]" // numéro en String du SVI appelé
}
Le retour doit être une List<Id> même si il n’y a qu’un seul élément.
Exemple de redéfinition
Dans cet exemple, on souhaite créer un Case et l’ouvrir à la place duContact. On va exécuter le code parent, créer le Case et renvoyer l’Id de ce 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 supérieure à 2.30
Vue globale de la classe SeekPhone
Dans le connecteur du Voice-management, SeekPhone est utilisé afin de rechercher le numéro de l’appelant dans les différents objets possibles (Account, Contact, Lead) et de retourner le résultat trouvé à la fenêtre du Voice-management. Selon les options on peut au choix :
créer la fiche si on ne la trouve pas
ouvrir une fenêtre de recherche
ne rien faire du tout.
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}
});
}
}
Le retour conditionne l’affichage en sortie du Voice-management :
OBJECT : Ouvre un objet Salesforce peut importe sa classe. values est un tableau d’un seul élément qui contient l’id de l’objet.
SEARCH : Ouvre la recherche peut importe le résultat. values est un tableau d’un seul élément qui contient le critère à recherche (par exemple le numéro de l’appelant).
URL : Ouvre une page sur un composant Lightning de votre organisation. values est un tableau d’un seul élément qui contient l’url à ouvrir.
NOTHING : Ne fait rien du tout.
Redéfinition du comportement de SeekPhone.get
Pour faire du travail en amont, en aval, ou à la place de SeekPhone.get, il est possible d’utiliser l’héritage de cette sorte
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);
}
}
Les éléments présents dans la variable js sont les suivants :
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 - Méthode d’ajout de Tâche
Version supérieure à 2.0
La recherche reçoit un JSON sérialisé en entrée et renvoie une Liste d’Id. Le json contient actuellement deux valeurs :
{
"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
}
Le retour doit être l’Id de la tache.
Exemple de redéfinition
Dans cet exemple, on souhaite ajouter la tache à l’appel et non au Contact de l’appelant. On va exécuter le code de la recherche de l’apelant et trouver le Case associé et y attacher la tâche.
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 supérieure à 2.30
Depuis cette version il existe deux JSON différent en fonction d’une tâche d’appel ou d’une tâche d’envoi de sms :
JSON de tâche d’appel
{
"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"
}
JSON de tâche d’un SMS
{
"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"
}
Exemple de redéfinition d’une Tache
Dans cet exemple, on souhaite ajouter la tache à l’appel et non au Contact de l’appelant. On va exécuter le code de la recherche de l’appelant et trouver le Case associé et y attacher la tâche.
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)
}
}
Exemple pour gérer le personalAccount
Dans cet exemple on ne souhaite pas rajouter de tâches, et créer un personnal account si on n’a aucune correspondance en base.
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);
}
}
}
5 - Déclarer ma classe d’extension
Procédure temporaire, dans une version ultérieure sera ajouté un sélecteur dans la page de paramétrage du Voice Management.
On va signaler au connecteur VoiceManagement qu’il y a une classe d’extension.
Il faut pour ce faire définir un nouvel objet dans la table AxiaVM__ConfigCTI__c. Par exemple pour une classe MyNewExtension qui étend AxiaVM.Extension, il faut créer un objet AxiaVM__ConfigCTI__c où Name = "redefined_class" et AxiaVM__value__c = "MyNewExtension".
6 - Conseils pour analyser un soucis
Nous vous conseillons dans un premier temps de vous assurer que la classe que vous souhaitez utiliser surcharge bien celle par défault. Si vous créez vos propres objets, bien configurer à « Aucun » l’objet à crééer. Ensuite, assurez-vous que vous n’auriez pas mis en place des contrôles qui empêcheraient la création de l’objet que vous souhaiteriez créer.
S’assurer que votre votre classe surcharge bien celle d’origine
Assurez-vous que le nom de la classe créé surcharge bien la méthode d’origine en ajoutant un “System.debug”, ou regardez dans les logs de la “developer console”, le chargement, vous devriez voir des lignes du style 12:15:18:373 VARIABLE_SCOPE_BEGIN [22]|this|PersonnalAccountExtension|true|false avec le nom de la classe surchargée.
Si vous ne voyez pas votre classe, merci de bien vérifier via le workbench ou via la console en lançant la commande suivante qu’il n’y a bien qu’une classe “redefined_class” et qu’elle est bien renseignée avec le nom que vous avez donné à votre classe.
Select Id, Name,AxiaVM__value__c From AxiaVM__ConfigCTI__c Where Name = 'redefined_class'
S’assurer que vous n’avez pas mis en place des contrôles bloquants
Contrôles qui seraient susceptibles d’empêcher de faire certaines opérations, qui ne seraient pas définies, si par exemple vous avez rendu obligatoire la configuration d’une adresse sur une fiche client.
Enfin, afin de vous aider à debuger, vous avez la possiblité de visualiser le statut de traitement des échanges avec salesforces en ouvrant l’écran de développement de votre navigateur. Vous pourrez vérifier le statut de retour, et en cas d’échec l’erreur ayant provoqué le soucis.