Ti trovi qui:
Personalizzazione dei piani procedurali con gli hook Apex
Per supportare scenari di calcolo dei prezzi univoci, aggiungere una logica Apex personalizzata ai piani delle procedure di calcolo dei prezzi. È possibile utilizzare gli hook Apex per applicare una logica aziendale personalizzata che modifica il contesto dei prezzi dopo la configurazione di una voce preventivo. Utilizzare un prehook Apex per rettificare i prezzi in base agli attributi del prodotto prima che venga calcolato il prezzo e un posthook Apex per gestire le modifiche dei prezzi per i gruppi e altri elementi dell'oggetto Preventivo dopo la determinazione del prezzo. Quando un agente di vendita configura un prodotto o modifica un gruppo di voci preventivo, il piano della procedura di calcolo dei prezzi modifica i prezzi in base alle istruzioni in Apex.
Versioni (Edition) richieste
| Disponibile in: Lightning Experience |
| Disponibile in: Enterprise Edition, Unlimited Edition e Developer Edition di Revenue Cloud in cui è abilitato Salesforce Pricing |
| Autorizzazioni utente richieste | |
|---|---|
| Per creare, aggiornare ed eliminare procedure di calcolo dei prezzi e piani procedurali: | Accesso utente Salesforce Pricing Design Time o piano procedura |
| Per utilizzare le procedure di calcolo dei prezzi: | Utente Salesforce Pricing Run Time |
| Per definire, modificare, eliminare, impostare la protezione e definire le impostazioni di versione per le classi Apex: | Apex autore |
- Quando si utilizza il tipo di processo Revenue Cloud, qualsiasi logica Apex aggiunta deve essere il primo o l'ultimo elemento della sequenza di esecuzione del piano procedurale.
- Le chiamate esterne da un hook Apex sono supportate solo quando la richiesta Effettua transazione di vendita viene attivata tramite l'interfaccia utente Salesforce o l'API Effettua transazione di vendita. Non sono supportati quando la richiesta viene attivata da Apex o Flusso.
- Le chiamate esterne negli hook Apex non sono supportate quando è abilitata la modalità Double Persist. Per ulteriori informazioni, contattare l'Assistenza clienti Salesforce.
- Le chiamate esterne negli hook Apex possono influire sulle prestazioni. Pertanto, non è possibile garantire obiettivi del livello di servizio (SLO) prevedibili.
-
Assicurarsi che sia attivata l'orchestrazione del piano procedura per i prezzi.
- Da Imposta, nella casella Ricerca veloce, immettere Impostazioni reddito, quindi selezionare Impostazioni reddito.
- Individuare e, se necessario, abilitare l'impostazione Orchestrazione piano procedura per i prezzi.
- Lasciare disabilitata l'impostazione Escludi procedure predefinite e procedure prezzi tipo di transazione di vendita.
-
Definire le classi per gli hook Apex da aggiungere alle procedure di calcolo dei prezzi.
- Da Imposta, nella casella Ricerca veloce, immettere Apex, quindi selezionare Classi Apex.
- Selezionare Nuovo per creare una nuova classe Apex.
-
Immettere la definizione della classe nell'editor della classe.
Ad esempio, questo prehook (ApexDmAttributePreHook) aggiorna i valori degli attributi dinamici con il nome
Display_Sizein base alle loro dimensioni di visualizzazione. Altre classi di esempio sono visualizzate in Classi di esempio per gli hook prezzi Apex alla fine di questi passaggi.global class ApexDmlAttributePreHook implements RevSignaling.SignalingApexProcessor { public virtual class BaseException extends Exception {} public class OtherException extends BaseException {} public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) { System.debug('Executing PREHOOK'); String contextId = request.ctxInstanceId; Context.IndustriesContext industriesContext = new Context.IndustriesContext(); // STEP 1 - Query SalesTransactionItemAttribute and extract Display_Size values Map<String, Object> inputQueryItemAttr = new Map<String, Object>{ 'contextId' => contextId, 'tags' => new List<String>{ 'SalesTransactionItemAttribute' } }; Map<String, Object> itemAttrQueryOutput = industriesContext.queryTags(inputQueryItemAttr); Map<String, Object> itemAttrQueryResult = (Map<String, Object>) itemAttrQueryOutput.get('queryResult'); List<Object> itemAttrData = (List<Object>) itemAttrQueryResult.get('SalesTransactionItemAttribute'); Map<String, Decimal> parentCtxIdToDisplaySize = new Map<String, Decimal>(); for (Object attrObj : itemAttrData) { Map<String, Object> attrNode = (Map<String, Object>) attrObj; Map<String, Object> tagMap = (Map<String, Object>) attrNode.get('tagValue'); String attributeName = null; String attributeValueStr = null; String parentCtxId = null; if (tagMap.containsKey('Attribute')) { attributeName = (String)((Map<String, Object>) tagMap.get('Attribute')).get('tagValue'); } if (tagMap.containsKey('AttributeValue')) { attributeValueStr = (String)((Map<String, Object>) tagMap.get('AttributeValue')).get('tagValue'); } if (tagMap.containsKey('SalesTransactionItemAttrParent')) { parentCtxId = (String)((Map<String, Object>) tagMap.get('SalesTransactionItemAttrParent')).get('tagValue'); } if (attributeName == 'Display_Size' && attributeValueStr != null && parentCtxId != null) { Decimal sizeValue = Decimal.valueOf(attributeValueStr.split(' ')[0]); parentCtxIdToDisplaySize.put(parentCtxId, sizeValue); System.debug('DisplaySize=' + sizeValue); System.debug('Matched itemCtxId=' + parentCtxId); } } // STEP 2 - Query SalesTransactionItem nodes Map<String, Object> inputQueryItem = new Map<String, Object>{ 'contextId' => contextId, 'tags' => new List<String>{ 'SalesTransactionItem' } }; Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem); Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult'); List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem'); // STEP 3 - Build update list List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>(); for (Object itemObj : itemData) { Map<String, Object> itemNode = (Map<String, Object>) itemObj; List<Object> dataPath = (List<Object>) itemNode.get('dataPath'); System.debug('Full item dataPath: ' + JSON.serialize(dataPath)); Boolean matched = false; for (String ctxKey : parentCtxIdToDisplaySize.keySet()) { if (dataPath.contains(ctxKey)) { Decimal newPrice = parentCtxIdToDisplaySize.get(ctxKey); System.debug('DisplaySize match found for item ' + ctxKey); dataPath.remove(0); // Remove contextId itemNodeUpdates.add(new Map<String, Object>{ 'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath }, 'attributes' => new List<Object>{ new Map<String, Object>{ 'attributeName' => 'UnitPrice', 'attributeValue' => newPrice } } }); matched = true; break; } } if (!matched) { String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN'; System.debug('No DisplaySize match found for item ' + itemCtxId); } } // STEP 4 - Submit context update if (!itemNodeUpdates.isEmpty()) { Map<String, Object> updateInput = new Map<String, Object>{ 'contextId' => contextId, 'nodePathAndAttributes' => itemNodeUpdates }; System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---'); System.debug(JSON.serializePretty(updateInput)); industriesContext.updateContextAttributes(updateInput); } RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse(); response.status = RevSignaling.TransactionStatus.SUCCESS; //response.status = RevSignaling.TransactionStatus.FAILED; //response.message = 'An error occurred during the processing...'; return response; } } - Salvare la definizione della classe.
- Da Imposta, nella casella Ricerca veloce, immettere Piano procedura, quindi selezionare Definizioni piano procedura.
- Nella colonna Nomi definizione, selezionare una definizione del piano procedurale da modificare.
-
Dalla definizione del piano procedurale, in Sezioni del piano procedurale, selezionare Aggiungi per aggiungere una nuova sezione.
- Selezionare Standard.
- Assegnare un nome alla sezione, ad esempio PreApex per un prehook Apex.
- In Tipo di sezione, selezionare Apex e quindi Salva.
- Espandere la nuova sezione.
- Per Fasi, selezionare Prezzi e per Tipo di risoluzione selezionare Predefinito.
- Nella casella di selezione Apex visualizzata, immettere la classe Apex definita in precedenza, ad esempio ApexDmlAttributePreHook, quindi selezionare Salva.
- Aggiungere altri preganci e postganci in base alle esigenze.
- Selezionare Gestisci sezioni per riorganizzare i prehook sopra la sezione Procedura prezzi e i posthook sotto di essa.
- Salvare le modifiche alla definizione del piano procedurale.
Prehook: Aggiornare la percentuale di sconto al 2% su tutte le voci del contesto (ad esempio, le voci preventivo).
global class ApexDmlDiscountUpdatePreHook implements RevSignaling.SignalingApexProcessor {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) {
System.debug('Executing PREHOOK');
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
Integer randomDiscountPercentage = 2;
// STEP 2 - Query SalesTransactionItem nodes
Map<String, Object> inputQueryItem = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem);
Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult');
List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem');
System.debug('QLI itemData=' + itemData);
// STEP 3 - Build update list
List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>();
for (Object itemObj : itemData) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
System.debug('Full item dataPath: ' + JSON.serialize(dataPath));
Boolean matched = false;
dataPath.remove(0); // Remove contextId
itemNodeUpdates.add(new Map<String, Object>{
'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath },
'attributes' => new List<Object>{
new Map<String, Object>{
'attributeName' => 'Discount',
'attributeValue' => randomDiscountPercentage
}
}
});
matched = true;
if (!matched) {
String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN';
System.debug('No DisplaySize match found for item ' + itemCtxId);
}
}
// STEP 4 - Submit context update
if (!itemNodeUpdates.isEmpty()) {
Map<String, Object> updateInput = new Map<String, Object>{
'contextId' => contextId,
'nodePathAndAttributes' => itemNodeUpdates
};
System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---');
System.debug(JSON.serializePretty(updateInput));
industriesContext.updateContextAttributes(updateInput);
}
RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse();
response.status = RevSignaling.TransactionStatus.SUCCESS;
//response.status = RevSignaling.TransactionStatus.FAILED;
//response.message = 'An error occurred during the processing...';
return response;
}
}Prehook: Chiamare una risorsa esterna per recuperare un valore di quantità.
global class ApexDmlQuantityCalloutPreHook implements RevSignaling.SignalingApexProcessor {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) {
System.debug('Executing PREHOOK');
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
// STEP 1 - External Callout Test
Integer randomNumber = getRandomNumber();
System.debug(' Random Number from API: ' + randomNumber);
// STEP 2 - Query SalesTransactionItem nodes
Map<String, Object> inputQueryItem = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem);
Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult');
List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem');
System.debug('QLI itemData=' + itemData);
// STEP 3 - Build update list
List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>();
for (Object itemObj : itemData) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
System.debug('Full item dataPath: ' + JSON.serialize(dataPath));
Boolean matched = false;
dataPath.remove(0); // Remove contextId
itemNodeUpdates.add(new Map<String, Object>{
'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath },
'attributes' => new List<Object>{
new Map<String, Object>{
'attributeName' => 'Quantity',
'attributeValue' => randomNumber
}
}
});
matched = true;
if (!matched) {
String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN';
System.debug('No DisplaySize match found for item ' + itemCtxId);
}
}
// STEP 4 - Submit context update
if (!itemNodeUpdates.isEmpty()) {
Map<String, Object> updateInput = new Map<String, Object>{
'contextId' => contextId,
'nodePathAndAttributes' => itemNodeUpdates
};
System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---');
System.debug(JSON.serializePretty(updateInput));
industriesContext.updateContextAttributes(updateInput);
}
RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse();
response.status = RevSignaling.TransactionStatus.SUCCESS;
//response.status = RevSignaling.TransactionStatus.FAILED;
//response.message = 'An error occurred during the processing...';
return response;
}
// External callout
private Integer getRandomNumber() {
String endpoint = 'https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new';
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('GET');
req.setTimeout(5000);
try {
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
System.debug('Fetched prices from external service');
return Integer.valueOf(res.getBody().trim());
} else {
System.debug(' Callout failed: ' + res.getStatus());
}
} catch (Exception ex) {
System.debug(' Exception during callout: ' + ex.getMessage());
}
return 10;
}
}Prehook: Attivare un aggiornamento del prezzo di un prodotto se il Nome attributo è Espositore e il Valore attributo è Espositore incorporato 1080p o se il Nome attributo è Stampante e il Valore attributo è Laser.
global class ApexDmlMultiAttributePreHook implements RevSignaling.SignalingApexProcessor {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) {
System.debug('Executing PREHOOK');
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
// STEP 1 - Query SalesTransactionItemAttribute and extract Display_Size values
Map<String, Object> inputQueryItemAttr = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItemAttribute' }
};
Map<String, Object> itemAttrQueryOutput = industriesContext.queryTags(inputQueryItemAttr);
Map<String, Object> itemAttrQueryResult = (Map<String, Object>) itemAttrQueryOutput.get('queryResult');
List<Object> itemAttrData = (List<Object>) itemAttrQueryResult.get('SalesTransactionItemAttribute');
Map<String, Decimal> parentCtxIdToAttribute = new Map<String, Decimal>();
for (Object attrObj : itemAttrData) {
Map<String, Object> attrNode = (Map<String, Object>) attrObj;
Map<String, Object> tagMap = (Map<String, Object>) attrNode.get('tagValue');
String attributeName = null;
String attributeValueStr = null;
String parentCtxId = null;
System.debug('TagMap ' + tagMap);
if (tagMap.containsKey('Attribute')) {
attributeName = (String)((Map<String, Object>) tagMap.get('Attribute')).get('tagValue');
}
if (tagMap.containsKey('AttributeValue')) {
attributeValueStr = (String)((Map<String, Object>) tagMap.get('AttributeValue')).get('tagValue');
}
if (tagMap.containsKey('SalesTransactionItemAttrParent')) {
parentCtxId = (String)((Map<String, Object>) tagMap.get('SalesTransactionItemAttrParent')).get('tagValue');
}
if (attributeName == 'Display' && attributeValueStr == '1080p Built-in Display' && parentCtxId != null) {
Decimal defaultDisplayCost = 1000.00;
parentCtxIdToAttribute.put(parentCtxId, defaultDisplayCost);
System.debug('Display=' + defaultDisplayCost);
System.debug('Matched itemCtxId=' + parentCtxId);
}
if (attributeName == 'Printer' && attributeValueStr == 'Laser' && parentCtxId != null) {
Decimal defaultLaserPrinterCost = 500.00;
parentCtxIdToAttribute.put(parentCtxId, defaultLaserPrinterCost);
System.debug('Printer=' + defaultLaserPrinterCost);
System.debug('Matched itemCtxId=' + parentCtxId);
}
}
// STEP 2 - Query SalesTransactionItem nodes
Map<String, Object> inputQueryItem = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem);
Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult');
List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem');
// STEP 3 - Build update list
List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>();
for (Object itemObj : itemData) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
System.debug('Full item dataPath: ' + JSON.serialize(dataPath));
Boolean matched = false;
for (String ctxKey : parentCtxIdToAttribute.keySet()) {
if (dataPath.contains(ctxKey)) {
Decimal newPrice = parentCtxIdToAttribute.get(ctxKey);
System.debug('Attribute match found for item ' + ctxKey);
System.debug('Attribue with new price ' + newPrice);
dataPath.remove(0); // Remove contextId
itemNodeUpdates.add(new Map<String, Object>{
'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath },
'attributes' => new List<Object>{
new Map<String, Object>{
'attributeName' => 'UnitPrice',
'attributeValue' => newPrice
}
}
});
matched = true;
break;
}
}
if (!matched) {
String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN';
System.debug('No DisplaySize match found for item ' + itemCtxId);
}
}
// STEP 4 - Submit context update
if (!itemNodeUpdates.isEmpty()) {
Map<String, Object> updateInput = new Map<String, Object>{
'contextId' => contextId,
'nodePathAndAttributes' => itemNodeUpdates
};
System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---');
System.debug(JSON.serializePretty(updateInput));
industriesContext.updateContextAttributes(updateInput);
}
RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse();
response.status = RevSignaling.TransactionStatus.SUCCESS;
//response.status = RevSignaling.TransactionStatus.FAILED;
//response.message = 'An error occurred during the processing...';
return response;
}
}
Prehook: Applicare una percentuale di sconto casuale per tutte le righe del contesto.
global class ApexDmlRandomDiscountPreHook implements RevSignaling.SignalingApexProcessor {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) {
System.debug('Executing PREHOOK');
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
Integer randomDiscountPercentage = getRandomNumber();
// STEP 2 - Query SalesTransactionItem nodes
Map<String, Object> inputQueryItem = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem);
Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult');
List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem');
System.debug('QLI itemData=' + itemData);
// STEP 3 - Build update list
List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>();
for (Object itemObj : itemData) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
System.debug('Full item dataPath: ' + JSON.serialize(dataPath));
Boolean matched = false;
dataPath.remove(0); // Remove contextId
itemNodeUpdates.add(new Map<String, Object>{
'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath },
'attributes' => new List<Object>{
new Map<String, Object>{
'attributeName' => 'Discount',
'attributeValue' => randomDiscountPercentage
}
}
});
matched = true;
if (!matched) {
String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN';
System.debug('No DisplaySize match found for item ' + itemCtxId);
}
}
// STEP 4 - Submit context update
if (!itemNodeUpdates.isEmpty()) {
Map<String, Object> updateInput = new Map<String, Object>{
'contextId' => contextId,
'nodePathAndAttributes' => itemNodeUpdates
};
System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---');
System.debug(JSON.serializePretty(updateInput));
industriesContext.updateContextAttributes(updateInput);
}
RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse();
response.status = RevSignaling.TransactionStatus.SUCCESS;
//response.status = RevSignaling.TransactionStatus.FAILED;
//response.message = 'An error occurred during the processing...';
return response;
}
// External callout
public static Integer getRandomNumber() {
String endpoint = 'https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new';
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('GET');
req.setTimeout(5000);
try {
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
System.debug('Fetched prices from external service');
return Integer.valueOf(res.getBody().trim());
} else {
System.debug(' Callout failed: ' + res.getStatus());
}
} catch (Exception ex) {
System.debug(' Exception during callout: ' + ex.getMessage());
}
return 10;
}
}Prehook: Aggiornare un valore attributo dinamico con un valore casuale da una chiamata a una risorsa esterna.
global class ApexDmlAttributeExternalCalloutPreHook implements RevSignaling.SignalingApexProcessor {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) {
System.debug('Executing PREHOOK');
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
// STEP 1 - Query SalesTransactionItemAttribute and extract Display_Size values
Map<String, Object> inputQueryItemAttr = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItemAttribute' }
};
Map<String, Object> itemAttrQueryOutput = industriesContext.queryTags(inputQueryItemAttr);
Map<String, Object> itemAttrQueryResult = (Map<String, Object>) itemAttrQueryOutput.get('queryResult');
List<Object> itemAttrData = (List<Object>) itemAttrQueryResult.get('SalesTransactionItemAttribute');
Map<String, Decimal> parentCtxIdToDisplaySize = new Map<String, Decimal>();
for (Object attrObj : itemAttrData) {
Map<String, Object> attrNode = (Map<String, Object>) attrObj;
Map<String, Object> tagMap = (Map<String, Object>) attrNode.get('tagValue');
String attributeName = null;
String attributeValueStr = null;
String parentCtxId = null;
if (tagMap.containsKey('Attribute')) {
attributeName = (String)((Map<String, Object>) tagMap.get('Attribute')).get('tagValue');
}
if (tagMap.containsKey('AttributeValue')) {
attributeValueStr = (String)((Map<String, Object>) tagMap.get('AttributeValue')).get('tagValue');
}
if (tagMap.containsKey('SalesTransactionItemAttrParent')) {
parentCtxId = (String)((Map<String, Object>) tagMap.get('SalesTransactionItemAttrParent')).get('tagValue');
}
if (attributeName == 'Display_Size' && attributeValueStr != null && parentCtxId != null) {
Decimal sizeValue = getRandomNumber();
parentCtxIdToDisplaySize.put(parentCtxId, sizeValue);
System.debug('DisplaySize=' + sizeValue);
System.debug('Matched itemCtxId=' + parentCtxId);
}
}
// STEP 2 - Query SalesTransactionItem nodes
Map<String, Object> inputQueryItem = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem);
Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult');
List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem');
// STEP 3 - Build update list
List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>();
for (Object itemObj : itemData) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
System.debug('Full item dataPath: ' + JSON.serialize(dataPath));
Boolean matched = false;
for (String ctxKey : parentCtxIdToDisplaySize.keySet()) {
if (dataPath.contains(ctxKey)) {
Decimal newPrice = parentCtxIdToDisplaySize.get(ctxKey);
System.debug('DisplaySize match found for item ' + ctxKey);
dataPath.remove(0); // Remove contextId
itemNodeUpdates.add(new Map<String, Object>{
'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath },
'attributes' => new List<Object>{
new Map<String, Object>{
'attributeName' => 'UnitPrice',
'attributeValue' => newPrice
}
}
});
matched = true;
break;
}
}
if (!matched) {
String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN';
System.debug('No DisplaySize match found for item ' + itemCtxId);
}
}
// STEP 4 - Submit context update
if (!itemNodeUpdates.isEmpty()) {
Map<String, Object> updateInput = new Map<String, Object>{
'contextId' => contextId,
'nodePathAndAttributes' => itemNodeUpdates
};
System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---');
System.debug(JSON.serializePretty(updateInput));
industriesContext.updateContextAttributes(updateInput);
}
RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse();
response.status = RevSignaling.TransactionStatus.SUCCESS;
//response.status = RevSignaling.TransactionStatus.FAILED;
//response.message = 'An error occurred during the processing...';
return response;
}
// External callout
private Integer getRandomNumber() {
String endpoint = 'https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new';
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('GET');
req.setTimeout(5000);
try {
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
System.debug('Fetched prices from external service');
return Integer.valueOf(res.getBody().trim());
} else {
System.debug(' Callout failed: ' + res.getStatus());
}
} catch (Exception ex) {
System.debug(' Exception during callout: ' + ex.getMessage());
}
return 10;
}
}Posthook: Aggiornare la descrizione di ogni riga nel contesto.
global class ApexDmlDescriptionPostHook implements RevSignaling.SignalingApexProcessor {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) {
System.debug('Executing POSTHOOK');
String contextId = request.ctxInstanceId;
Context.IndustriesContext industriesContext = new Context.IndustriesContext();
String randomDescription = 'via Post Apex Pricing Hook';
// STEP 2 - Query SalesTransactionItem nodes
Map<String, Object> inputQueryItem = new Map<String, Object>{
'contextId' => contextId,
'tags' => new List<String>{ 'SalesTransactionItem' }
};
Map<String, Object> itemQueryOutput = industriesContext.queryTags(inputQueryItem);
Map<String, Object> itemQueryResult = (Map<String, Object>) itemQueryOutput.get('queryResult');
List<Object> itemData = (List<Object>) itemQueryResult.get('SalesTransactionItem');
System.debug('QLI itemData=' + itemData);
// STEP 3 - Build update list
List<Map<String, Object>> itemNodeUpdates = new List<Map<String, Object>>();
for (Object itemObj : itemData) {
Map<String, Object> itemNode = (Map<String, Object>) itemObj;
List<Object> dataPath = (List<Object>) itemNode.get('dataPath');
System.debug('Full item dataPath: ' + JSON.serialize(dataPath));
Boolean matched = false;
dataPath.remove(0); // Remove contextId
itemNodeUpdates.add(new Map<String, Object>{
'nodePath' => new Map<String, Object>{ 'dataPath' => dataPath },
'attributes' => new List<Object>{
new Map<String, Object>{
'attributeName' => 'SalesTrxnItemDescription',
'attributeValue' => randomDescription
}
}
});
matched = true;
if (!matched) {
String itemCtxId = dataPath.size() > 1 ? String.valueOf(dataPath[1]) : 'UNKNOWN';
System.debug('No DisplaySize match found for item ' + itemCtxId);
}
}
// STEP 4 - Submit context update
if (!itemNodeUpdates.isEmpty()) {
Map<String, Object> updateInput = new Map<String, Object>{
'contextId' => contextId,
'nodePathAndAttributes' => itemNodeUpdates
};
System.debug('--- PREHOOK: SUBMITTING CONTEXT UPDATE ---');
System.debug(JSON.serializePretty(updateInput));
industriesContext.updateContextAttributes(updateInput);
}
RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse();
response.status = RevSignaling.TransactionStatus.SUCCESS;
//response.status = RevSignaling.TransactionStatus.FAILED;
//response.message = 'An error occurred during the processing...';
return response;
}
}
