Filtrage des tiers dans le CRM Web [Fiche expert]

Version minimumDate de mise à jour
5.4

18/05/2021 

Résumé

Nous avons identifié qu'un important problème de performance était lié au filtrage des tiers mis en place dans la version Winter'20(5.3). C'est pourquoi nous avons revu notre moyen de filtrer l'accès aux tiers.

Comment ça fonctionnait avant

Le script Global.OnLogin (à chaque fois que l'on fait F5) exécutait la fonction suivante getCustomerGlobalSyncActionAsync().
Cette fonction (obsolète) exécutait le datasource syncAction.customer.sql pour alimenter la variable de session syncAction.customer.Global avec l'ensemble des tiers accessibles par l'utilisateur connecté.
Note : syncAction.customer.sql est une copie adaptée de la requête de synchro de la table sw_data_customer (liste des tiers).
Avec un nombre important de tiers accessibles (>50 000) nous avions les problèmes suivants:

  • les requêtes filtrées avec la variable syncAction.customer.Globalqui intègrent la totalité du contenu de la variable, et donc elles deviennent très longues
  • le filtrage était effectué comme suit
    [..] AND c.customer_ID IN ({{syncAction.customer.Global}})

    et il est déconseillé d'utiliser l'opérateur IN avec un grand volume (pour des raisons de performance)

Comment ça fonctionne maintenant

Après une étroite collaboration avec l'équipe Divalto One, nous décidons d'appliquer une solution de filtrage en stockant la liste des tiers accessible par compte d'accès (device). Cette solution correspond à une fonctionnalité du Framework SFK qui devrait être disponible au prochain cycle (Winter'21).
Le Framework SFK va permettre en ajoutant de la sémantique aux requêtes de s'affranchir de jointure permettant le filtrage de données. Cette fonctionnalité va aussi permettre de gérer le multi-société avec le Framework.
Normalement prévue de la forme suivante

SELECT c.name FROM {{TABLE(customer, c)}}

Toutefois cette solution n'étant pas disponible pour notre correction, nous avons appliqué une correction similaire.
Au lieu d'avoir le code suivant :

INNER JOIN
  sw_data_customer AS c
  ON c.customer_ID = vr.customer_ID
INNER JOIN
  sw_data_customer AS customerFilter
  ON customerFilter.customer_ID = c.customer_ID
  AND customerFilter.customer_ID IN ({{syncAction.customer.Global}})

Nous avons maintenant le code suivant :

INNER JOIN
  sw_data_customer AS c
  INNER JOIN
    (
      SELECT
        customer_ID AS c_rowaccess_customer_ID
      FROM
        sw_data_customer
      INNER JOIN
        sw_sys_rowaccess
        ON sw_sys_rowaccess.device_ID = {{deviceID}}
        AND sw_sys_rowaccess.metatable_ID = {{customerMetatableID}}
        AND sw_sys_rowaccess.rowID = customer_ID
    ) AS c_rowaccess
    ON c_rowaccess.c_rowaccess_customer_ID = c.customer_ID
  ON c.customer_ID = vr.customer_ID
  • {{deviceID}} : variable system correspondant au device_ID de l'utilisateur connecté
  • {{customerMetatableID}} : variable de session initialisée dans le script Global.onLogin et contenant le metatable_ID correspondant à la table sw_data_customer

Comme dit précédemment nous disposons de la table sw_sys_rowaccess pour identifier les tiers accessibles par un compte.
Cette table est alimentée avec la fonction setByDatasourceAsync(datasourceName: string, forceUpdate: boolean) du service $rowAccess.

  • datasourceName : nom du datasource de type rowaccess qui va permettre de lister les éléments (dans notre cas des customer_ID)
  • forceUpdate : une option qui permet d'obliger le recalcul, sinon SFK a mis en place un mécanisme horodaté pour éviter de demander un recalcul trop fréquemment

On retrouve cette fonction dans :

  • Global.OnLogin avec forceUpdate à false (valeur par défaut) pour éviter que F5 lance systématiquement le recalcul des tiers accessibles
  • customer.new.crudComponent.AfterInsert avec forceUpdate à true pour demander un recalcul des tiers accessibles et intégrer le tiers qui vient d'être créé
    Attention : la position de la fonction est importante dans le script (il faut avoir l'ajout dans la table sw_data_customeruser avant et il faut mettre tous les SELECT avec un filtrage de tiers après)
  • customer.id.updateFromSirene.Click avec forceUpdate à true lorsque l'on veut mettre à jour les informations d'un tiers à partir de la base Sirene, il y a une création d'un tiers temporaire pour engager une fusion, c'est pour donner l'accès à ce tiers que l'on demande un recalcul

Je suis en version 5.3.0.0.X que dois-je faire

Il y a 4 cas possibles, veuillez identifier le cas qui concerne votre projet.

1. Global.OnLogin n'est pas surchargé et aucune surcharge contenant syncAction.customer.Global

Rien à faire, vous n'avez pas d'erreur liée au filtrage des tiers.

2. Global.OnLogin n'est pas surchargé et une ou plusieurs surcharge(s) contenant syncAction.customer.Global

Correction temporaire juste pour ne plus avoir d'erreur

La correction qui suit va vous permettre de faire cohabiter les 2 mécanismes, mais il ne s'agit que d'une correction temporaire.
Surcharger le fichier .\DeviceTypes\Default\#scriptLibrary\entity\onLoginOverload.
Il faut modifier le contenu de la fonction afterAsync comme suit :

async afterAsync()
{
  const helperCustomer = await $library.getLibraryAsync('customer');
  await helperCustomer.getCustomerGlobalSyncActionAsync();
  return;
}

Correction finale

Retirer la correction temporaire.
Il faut reprendre tous les fichiers surchargés contenant syncAction.customer.Global et utiliser la jointure avec la table sw_sys_rowaccess.

3. Global.OnLogin est surchargé et aucune surcharge contenant syncAction.customer.Global

Modifier le script Global.OnLogin.
Remplacer

const helperCustomer = await $library.getLibraryAsync('customer');
await helperCustomer.getCustomerGlobalSyncActionAsync();

Par

const helperSysMetaTable = await $library.getLibraryAsync('helper.sysMetaTable');
const customerMetatableID = await helperSysMetaTable.getMetatableBySchemaTableAsync('sw_data_customer');
await $session.setAsync('customerMetatableID', customerMetatableID);
await $rowAccess.setByDatasourceAsync('customer.rowaccess');

4. Global.OnLogin est surchargé et une ou plusieurs surcharge(s) contenant syncAction.customer.Global

Correction temporaire juste pour ne plus avoir d'erreur

La correction qui suit va vous permettre de faire cohabiter les 2 mécanismes, mais il ne s'agit que d'une correction temporaire.
Modifier le script Global.OnLogin.
Remplacer

const helperCustomer = await $library.getLibraryAsync('customer');
await helperCustomer.getCustomerGlobalSyncActionAsync();

Par

const helperCustomer = await $library.getLibraryAsync('customer');
await helperCustomer.getCustomerGlobalSyncActionAsync();
const helperSysMetaTable = await $library.getLibraryAsync('helper.sysMetaTable');
const customerMetatableID = await helperSysMetaTable.getMetatableBySchemaTableAsync('sw_data_customer');
await $session.setAsync('customerMetatableID', customerMetatableID);
await $rowAccess.setByDatasourceAsync('customer.rowaccess');

Correction finale

Modifier le script Global.OnLogin.
Retirer le code suivant

const helperCustomer = await $library.getLibraryAsync('customer');
await helperCustomer.getCustomerGlobalSyncActionAsync();

S'assurer que le script contient bien le code suivant à la place de celui supprimé précédemment

const helperSysMetaTable = await $library.getLibraryAsync('helper.sysMetaTable');
const customerMetatableID = await helperSysMetaTable.getMetatableBySchemaTableAsync('sw_data_customer');
await $session.setAsync('customerMetatableID', customerMetatableID);
await $rowAccess.setByDatasourceAsync('customer.rowaccess');

Il faut reprendre tous les fichiers surchargés contenant syncAction.customer.Global et utiliser la jointure avec la table sw_sys_rowaccess.