Vous êtes ici :
Meilleures pratiques pour les hooks de tarification Apex
Suivez les meilleures pratiques ci-dessous lors de l'implémentation de hooks Apex dans vos plans de procédure de tarification afin d'optimiser les performances et d'éviter des résultats inattendus.
Apex Governor Limits
Les hooks Apex dans les plans de procédure sont exécutés dans la même transaction Apex. Par conséquent, le minuteur de l'unité centrale est cumulé dans toutes les opérations. Tenez compte des limites du gouverneur en écrivant une logique de hook, en particulier pour les transactions avec de nombreux éléments de ligne.
Pour les valeurs limites actuelles, voir Execution Governors and Limits in the Apex Developer Guide.
Interrogation uniquement des balises dont vous avez besoin
Évitez d'interroger les balises au niveau de l'entité générales telles que SalesTransactionItem, qui renvoient chaque attribut de chaque élément. À la place, demandez uniquement les balises spécifiques requises par votre logique de hook.
Non recommandé : Interrogation de balises larges
// BAD: Returns ALL attributes for every SalesTransactionItem
Map<String, Object> input = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> output = industriesContext.queryTags(input);
Recommandé : Interroger uniquement des balises spécifiques
// GOOD: Query only the specific tags (attributes) you need
Map<String, Object> input = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'LineItem', 'LineItemQuantity', 'ItemProductCode' }
};
Map<String, Object> output = industriesContext.queryTags(input);
Recommandé : Combiner des balises spécifiques avec leanerQueryTags
// BETTER: Combine specific tags with leanerQueryTags for maximum efficiency
Map<String, Object> input = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'LineItem', 'LineItem$DmlStatus',
'LineItemQuantity', 'ItemProductCode' }
};
Map<String, Object> output = industriesContext.leanerQueryTags(input);
Utiliser leanerQueryTags au lieu de queryTags
La classe Context.IndustriesContext fournit deux méthodes pour interroger les données de contexte. Utilisez leanerQueryTags dans la mesure du possible pour améliorer les performances.
queryTags (heavy) : renvoie des nœuds contextuels complets avec des cartes balise-valeur imbriquées et des tableaux dataPath. Chaque attribut est inclus dans une carte avec 6 champs (tagValue, dmlStatus, isDirty, isNodeLevelTag, tagPath, contextDataPathBuilder) par attribut et par élément. Tous les attributs de la balise sont renvoyés, quels que soient vos besoins.
leanerQueryTags (léger) : renvoie une structure compacte avec des valeurs de balise plates et des références d'ID d'enregistrement basées sur l'index. Les valeurs de balise contiennent seulement 3 champs (tagValue, recordIdIndexesForPath, isNodeLevelTag). Les ID d'enregistrement sont dédupliqués dans une liste de recordsInfo partagée. Les nœuds les référencent par index.
L'exemple suivant montre comment appeler leanerQueryTags et traiter sa sortie.
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
Map<String, Object> input = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'LineItem', 'ItemProductCode', 'LineItemQuantity' }
};
Map<String, Object> output = industriesContext.leanerQueryTags(input);
// Access the leaner result structure
Map<String, Object> queryResult = (Map<String, Object>)
output.get('leanerQueryTagResult');
List<Object> recordsInfo = (List<Object>) output.get('recordsInfo');
List<Object> lineItems = (List<Object>) queryResult.get('LineItem');
// Iterate over tag values — each tag is flat
List<Object> productCodes = (List<Object>) queryResult.get('ItemProductCode');
for (Object obj : productCodes) {
Map<String, Object> tagNode = (Map<String, Object>) obj;
String productCode = (String) tagNode.get('tagValue');
List<Integer> indexes = (List<Integer>) tagNode.get('recordIdIndexesForPath');
// recordsInfo entries are maps with 'recordId' and 'dmlStatusOrdinal'
String itemId = (String) ((Map<String, Object>)
recordsInfo.get(indexes[1])).get('recordId');
}
La structure de sortie leanerQueryTags se présente comme suit :
{
"isSuccess": true,
"contextId": "...",
"leanerQueryTagResult": {
"LineItem": [
{ "recordIdIndexesForPath": [0, 1], "tagValue": "0QLVW000001LxvN4AS", "isNodeLevelTag": false },
{ "recordIdIndexesForPath": [0, 2], "tagValue": "0QLVW000001Lxus4AC", "isNodeLevelTag": false }
],
"ItemProductCode": [
{ "recordIdIndexesForPath": [0, 1], "tagValue": "PROD-001", "isNodeLevelTag": false },
{ "recordIdIndexesForPath": [0, 2], "tagValue": "PROD-002", "isNodeLevelTag": false }
]
},
"recordsInfo": [
{ "recordId": "0Q0VW0000011qC50AI", "dmlStatusOrdinal": null },
{ "recordId": "0QLVW000001LxvN4AS", "dmlStatusOrdinal": null },
{ "recordId": "0QLVW000001Lxus4AC", "dmlStatusOrdinal": null }
]
}
Migrer depuis queryTags vers leanerQueryTags
Lors de la migration de hooks existants depuis queryTags vers leanerQueryTags, appliquez les transformations de clé ci-dessous.
Entrée : Utiliser des balises individuelles au lieu de balises larges
// Before (queryTags — broad tag):
Map<String, Object> input = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> output = industriesContext.queryTags(input);
// After (leanerQueryTags — specific tags):
Map<String, Object> input = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'LineItem', 'LineItem$DmlStatus',
'ItemProductCode', 'LineItemQuantity' }
};
Map<String, Object> output = industriesContext.leanerQueryTags(input);
Sortie : Clé de niveau supérieur différente
// Before:
Map<String, Object> queryResult = (Map<String, Object>) output.get('queryResult');
List<Object> items = (List<Object>) queryResult.get('SalesTransactionItem');
// After:
Map<String, Object> queryResult = (Map<String, Object>)
output.get('leanerQueryTagResult');
List<Object> recordsInfo = (List<Object>) output.get('recordsInfo');
List<Object> lineItems = (List<Object>) queryResult.get('LineItem');
Accès au nœud : Valeurs plates au lieu de cartes imbriquées
// Before (queryTags — nested map extraction):
for (Object itemObj : items) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
Map<String, Object> tagValueMap = (Map<String, Object>) itemNode.get('tagValue');
String productCode = (String) ((Map<String, Object>)
tagValueMap.get('ItemProductCode')).get('tagValue');
Decimal quantity = (Decimal) ((Map<String, Object>)
tagValueMap.get('LineItemQuantity')).get('tagValue');
}
// After (leanerQueryTags — flat value, iterated per tag):
List<Object> productCodes = (List<Object>) queryResult.get('ItemProductCode');
List<Object> recordsInfo = (List<Object>) output.get('recordsInfo');
for (Object obj : productCodes) {
Map<String, Object> tagNode = (Map<String, Object>) obj;
String productCode = (String) tagNode.get('tagValue');
List<Integer> indexes = (List<Integer>) tagNode.get('recordIdIndexesForPath');
String itemId = (String) ((Map<String, Object>)
recordsInfo.get(indexes[1])).get('recordId');
}
ID d'enregistrement : Indexer les références au lieu de dataPath
// Before (queryTags):
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
// dataPath = ['contextId', 'txnId', 'itemId'] — 3 elements, first is contextId
dataPath.remove(0);
String transactionId = (String) dataPath.get(0);
// After (leanerQueryTags):
List<Object> recordsInfo = (List<Object>) output.get('recordsInfo');
List<Integer> indexes = (List<Integer>) tagNode.get('recordIdIndexesForPath');
String transactionId = (String) ((Map<String, Object>)
recordsInfo.get(indexes[0])).get('recordId');
String itemId = (String) ((Map<String, Object>)
recordsInfo.get(indexes[1])).get('recordId');
Utiliser le modèle tagIdIndexMap
Lors du traitement de plusieurs balises dans une seule boucle, utilisez un tagIdIndexMap pour mapper chaque nom de balise avec sa position dans le tableau recordIdIndexesForPath. Cela permet d'extraire correctement les ID à chaque niveau hiérarchique :
Map<String, Integer> tagIdIndexMap = new Map<String, Integer>{
'LineItem' => 1, // item-level tag, ID at index 1
'LineItem$DmlStatus' => 1, // item-level tag, ID at index 1
'LineItemQuantity' => 1, // item-level tag, ID at index 1
'BusinessUnit__c' => 0, // transaction-level tag, ID at index 0
'Contract' => 0 // transaction-level tag, ID at index 0
};
- Index 0 = niveau transaction (le chemin contient un élément : [
txnId]) - Index 1 = niveau de l'élément (le chemin contient deux éléments : [
txnId,itemId]) - Index 2 = niveau de détail (le chemin contient trois éléments : [
txnId,itemId,detailId])
Filtrage des éléments supprimés
Ignorez les éléments supprimés au début de votre boucle d'itération pour éviter tout traitement inutile. Demandez LineItem$DmlStatus en tant que balise séparée dans votre requête :
private static final String DML_DELETED = 'DELETED';
for (Map<String, Object> item : salesTransactionInstance.salesTransactionItems) {
String dmlStatus = (String) item.get('LineItem$DmlStatus');
if (dmlStatus == DML_DELETED) {
continue; // Skip deleted items
}
// Process active items...
}
Lire $DmlStatus dans une balise séparée
Lors de l'interrogation de balises spécifiques (l'approche recommandée), le statut DML n'est pas incorporé aux métadonnées des balises. Vous devez demander LineItem$DmlStatus en tant que balise séparée. Cela s'applique aux queryTags et aux leanerQueryTags.
// Add LineItem$DmlStatus to your tags list
'tags' => new List<String>{ 'LineItem', 'LineItem$DmlStatus', 'ItemProductCode' }
// Access it like any other tag in the result
Map<String, Object> dmlNode = (Map<String, Object>) dmlStatusValues.get(i);
String dmlStatus = String.valueOf(dmlNode.get('tagValue'));
