Sie befinden sich hier:
Anpassen Ihrer Verfahrenspläne mit Apex Hooks
Fügen Sie Ihren Preisgestaltungsverfahrensplänen benutzerdefinierte Apex-Logik hinzu, um individuelle Preisgestaltungsszenarien zu unterstützen. Sie können Apex Hooks verwenden, um benutzerdefinierte Geschäftslogik anzuwenden, die den Preiskontext ändert, nachdem ein Angebotsbelegposten konfiguriert wurde. Verwenden Sie einen Apex Prehook, um die Preise anhand von Produktattributen anzupassen, bevor sie bepreist werden, und einen Apex Posthook, um Preisänderungen für Gruppen und andere Angebotsobjektelemente nach der Preisgestaltung zu verarbeiten. Wenn ein Vertriebsmitarbeiter ein Produkt konfiguriert oder eine Gruppe von Angebotsbelegposten ändert, ändert der Preisgestaltungsplan den Preis entsprechend den Anweisungen in Apex.
Erforderliche Editionen
| Verfügbarkeit: Lightning Experience |
| Verfügbar in: Enterprise, Unlimited und Developer Edition von Revenue Cloud mit aktivierter Salesforce-Preisgestaltung |
| Erforderliche Benutzerberechtigungen | |
|---|---|
| Erstellen, Aktualisieren und Löschen von Preisgestaltungsverfahren und Verfahrensplänen: | Salesforce Pricing Design Time User or Procedure Plan Access (Zugriff auf Benutzer- oder Verfahrensplan für die Gestaltungszeit der Salesforce-Preisgestaltung) |
| Verwenden von Preisgestaltungsverfahren: | Salesforce-Preislaufzeitbenutzer |
| Definieren, Bearbeiten, Löschen, Festlegen der Sicherheit und der Versionseinstellungen für Apex-Klassen: | Autor-Apex |
- Bei Verwendung des Prozesstyps Revenue Cloud muss jede Apex Logik, die Sie hinzufügen, das erste oder letzte Element in der Ausführungssequenz des Verfahrensplans sein.
- Externe Callouts über einen Apex Hook werden nur unterstützt, wenn die Anforderung "Vertriebstransaktion platzieren" über die Salesforce-Benutzeroberfläche oder die API für Vertriebstransaktionen ausgelöst wird. Sie werden nicht unterstützt, wenn die Anforderung über Apex oder Flow ausgelöst wird.
- Externe Callouts in Apex Hooks werden nicht unterstützt, wenn der Modus "Doppelte Beibehaltung" aktiviert ist. Wenden Sie sich an den Salesforce-Kundensupport, um weitere Hilfestellung zu erhalten.
- Externe Callouts in Apex Hooks können die Leistung beeinträchtigen. Vorhersehbare Service Level Objectives (SLOs) können daher nicht garantiert werden.
-
Stellen Sie sicher, dass die Orchestrierung des Verfahrensplans für die Preisgestaltung aktiviert ist.
- Geben Sie unter "Setup" im Feld "Schnellsuche" den Text Umsatzeinstellungen ein und wählen Sie dann Umsatzeinstellungen aus.
- Suchen Sie die Einstellung Verfahrensplanorchestrierung für Preise und aktivieren Sie sie bei Bedarf.
- Lassen Sie die Einstellung "Standard- und Preisgestaltungsverfahren für Vertriebstransaktionen ausschließen" deaktiviert.
-
Definieren Sie Klassen für die Apex Hooks, die Ihren Preisgestaltungsverfahren hinzugefügt werden sollen.
- Geben Sie unter "Setup" im Feld "Schnellsuche" den Text Apex ein und wählen Sie dann Apex Klassen aus.
- Wählen Sie Neu aus, um eine neue Apex Klasse zu erstellen.
-
Geben Sie im Klasseneditor die Klassendefinition ein.
Dieser Prehook (ApexDmAttributePreHook) aktualisiert beispielsweise die Werte dynamischer Attribute mit dem Namen
Display_Sizeanhand ihrer Anzeigegröße. Weitere Beispielklassen werden am Ende dieser Schritte unter Beispielklassen für Apex Pricing Hooks angezeigt.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; } } - Speichern Sie die Klassendefinition.
- Geben Sie unter "Setup" im Feld "Schnellsuche" den Text Verfahrensplan ein und wählen Sie dann Verfahrensplandefinitionen aus.
- Wählen Sie in der Spalte "Definitionsnamen" eine Verfahrensplandefinition aus, die bearbeitet werden soll.
-
Wählen Sie in der Verfahrensplandefinition unter "Verfahrensplanabschnitte" die Option Hinzufügen aus, um einen neuen Abschnitt hinzuzufügen.
- Wählen Sie Standard aus.
- Benennen Sie den Abschnitt, beispielsweise PreApex für einen Apex-Prehook.
- Wählen Sie unter "Abschnittstyp" die Option Apex und dann Speichern aus.
- Erweitern Sie den neuen Abschnitt.
- Wählen Sie für "Phasen" die Option Preis und für "Lösungstyp" die Option Standard aus.
- Geben Sie im angezeigten Apex Auswahlfeld die oben definierte Apex Klasse ein, beispielsweise ApexDmlAttributePreHook, und wählen Sie dann Speichern aus.
- Fügen Sie nach Bedarf zusätzliche Prehooks und Posthooks hinzu.
- Wählen Sie Abschnitte verwalten aus, um Vor-Hooks über dem Abschnitt "Preisgestaltungsverfahren" und Nach-Hooks darunter neu anzuordnen.
- Speichern Sie Ihre Änderungen an der Verfahrensplandefinition.
Prehook: Aktualisieren Sie den Rabattprozentsatz auf 2 % für alle Belegposten im Kontext (z. B. Angebotsbelegposten).
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: Rufen Sie eine externe Ressource auf, um einen Mengenwert abzurufen.
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: Lösen Sie eine Aktualisierung des Preises eines Produkts aus, wenn sein Attributname Anzeige und sein Attributwert Integrierte Anzeige 1080p lautet oder wenn sein Attributname Drucker und sein Attributwert Laser lautet.
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: Wenden Sie einen zufälligen Rabattprozentsatz auf alle Belegposten im Kontext an.
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: Aktualisieren Sie einen dynamischen Attributwert mit einem zufälligen Wert aus einem Callout in eine externe Ressource.
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: Aktualisieren Sie die Beschreibung jeder Zeile im Kontext.
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;
}
}
