使用 Apex 控點自訂程序計畫
若要支援唯一的定價案例,請將自訂 Apex 邏輯新增至您的定價程序計畫。您可以使用 Apex 勾點,在設定報價條列後套用修改定價內容的自訂業務邏輯。使用 Apex 預先關聯來根據產品屬性調整定價,然後使用 Apex 後關聯來處理定價後群組和其他「報價」物件元素的定價變更。當銷售代表設定產品或變更報價條列項目群組時,定價程序計畫會根據 Apex 中的指示變更定價。
必要版本
| 適用於:Lightning Experience |
| 提供版本:已啟用 Salesforce 定價的 Revenue Cloud Enterprise、Unlimited 及 Developer Edition |
| 需要的使用者權限 | |
|---|---|
| 建立、更新和刪除定價程序和程序計畫: | Salesforce 定價設計時間使用者或程序計畫存取權 |
| 若要使用定價程序: | Salesforce 定價執行階段使用者 |
| 為 Apex 類別定義、編輯、刪除、設定安全性和設定版本設定: | Author Apex |
重要
- 使用 Revenue Cloud 流程類型時,您新增的任何 Apex 邏輯都必須是「程序計畫」執行序列中的第一個或最後一個元素。
- 只有在透過 Salesforce 使用者介面或 Place Sales Transaction API 觸發「銷售交易」要求時,才支援 Apex 綁架中的外部呼叫。從 Apex 或流程觸發要求時,不支援。
- 啟用「雙重保留」模式時,不支援 Apex 執行緒中的外部呼叫。如需其他指引,請連絡 Salesforce 客戶支援。
- Apex 勾點中的外部呼叫可能會影響效能。因此,無法保證可預測的服務層級目標 (SLO)。
-
確定「定價的程序計畫協調流程」已開啟。
- 進入「設定」,在「快速尋找」方塊中輸入收入設定,然後選取「收入設定」。
- 尋找並視需要啟用「定價的程序計畫協調流程」設定。
- 停用「排除預設值和銷售交易類型定價程序」設定。
-
定義 Apex 勾點的類別,以新增至您的定價程序。
- 進入「設定」,在「快速尋找」方塊中輸入 Apex,然後選取「Apex 類別」。
- 選取「新增」 以建立新的 Apex 類別。
-
在類別編輯器中,輸入類別定義。
例如,此預先關聯 (ApexDmAttributePreHook) 會根據其顯示大小,以名稱
Display_Size來更新動態屬性的值。在這些步驟結束時,會顯示更多 Apex 定價勾點的範例類別。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; } } - 儲存類別定義。
- 進入「設定」,在「快速尋找」方塊中輸入程序計畫,然後選取「程序計畫定義」。
- 在「定義名稱」欄中,選取要編輯的程序計畫定義。
-
進入程序計畫定義,在「程序計畫區段」中選取「新增」以新增區段。
- 選取「標準」。
- 為區段命名,例如 Apex 預先關聯的 Pre Apex。
- 在「區段類型」中,選取「Apex」,然後選取「儲存」。
- 展開新區段。
- 針對「階段」,選取「定價」,針對「解決類型」選取「預設值」。
- 在出現的 Apex 選取方塊中,輸入您上方定義的 Apex 類別,例如 ApexDmlAttributePreHook,然後選取「儲存」。
- 視需要新增其他預先和後置。
- 選取「管理區段」,以重新排列「定價程序」區段上方的預留位置,以及其下方的後置位置。
- 儲存對「程序計畫」定義進行的變更。
範例 Apex 定價勾點的範例類別
前置:將內容中所有行 (例如報價行) 的折扣百分比更新為 2%。
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:呼叫外部資源以提取數量值。
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;
}
}前置:如果產品的「屬性名稱」為「顯示」,且其「屬性值」為「1080p 內建顯示」,或其「屬性名稱」為「印表機」,且其「屬性值」為「雷射」,則會觸發產品價格的更新。
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:將隨機折扣百分比套用至內容中的所有行。
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:使用從呼叫到外部資源的隨機值更新動態屬性值。
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:更新內容中每一行的描述。
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;
}
}此文章是否解決您的問題?
請讓我們知道,以便我們改進!

