Loading
学习
目录
选择筛选器

          没有结果
          没有结果
          以下是一些搜索提示

          检查关键字的拼写。
          使用更普遍的搜索词。
          选择更少的筛选器,并扩大搜索范围。

          搜索所有 Salesforce 帮助
          使用 Apex 钩子自定义程序计划

          使用 Apex 钩子自定义程序计划

          为支持独特的定价场景,将自定义 Apex 逻辑添加到定价程序计划。您可以使用 Apex 挂钩来应用自定义业务逻辑,该逻辑在配置报价行后修改定价上下文。在定价前,使用 Apex 预挂钩根据产品属性调整定价,在定价后,使用 Apex 后挂钩处理小组和其他报价对象元素的定价更改。当销售代表配置产品或更改一组报价行项目时,定价程序计划会根据 Apex 中的说明更改定价。

          所需的 Edition

          适用于: Lightning Experience
          适用于启用了 Salesforce 定价的 Revenue Cloud 的 EnterpriseUnlimitedDeveloper Edition
          所需用户权限
          创建、更新和删除定价程序和程序计划: Salesforce 定价设计时间用户或程序计划访问权限
          要使用定价程序: Salesforce 定价运行时用户
          定义、编辑、删除和设置 Apex 类的版本设置: 作者 Apex
          重要
          重要
          • 在使用 Revenue Cloud 流程类型时,您添加的任何 Apex 逻辑必须是程序计划执行序列中的第一个或最后一个元素。
          • 只有当通过 Salesforce 用户界面或“放置销售交易”API 触发“放置销售交易”请求时,才支持来自 Apex 挂钩的外部标注。当从 Apex 或流触发请求时,它们不受支持。
          • 启用双重持久模式时,不支持 Apex 钩子中的外部标注。有关其他指导,请联系 Salesforce 客户支持。
          • Apex 挂钩中的外部标注会影响性能。因此,不能保证可预测的服务级别目标 (SLO)。
          1. 确保打开定价的程序计划编排。
            1. 从“设置”中,在快速查找框中输入收入设置,然后选择收入设置
            2. 查找并在必要时启用设置定价的程序计划编排。
            3. 将设置排除默认值和销售交易类型定价程序保持禁用状态。
          2. 为 Apex 挂钩定义类,以添加到定价程序。
            1. 从“设置”中,在快速查找框中输入Apex,然后选择Apex 类
            2. 选择新建以创建新的 Apex 类。
            3. 在类编辑器中,输入类定义。

              例如,此预挂钩 (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;
                  }
                  
              }
            4. 保存类定义。
          3. 从“设置”中,在快速查找框中输入程序计划,然后选择程序计划定义
          4. 在定义名称列中,选择要编辑的程序计划定义。
          5. 从手术计划定义中,在手术计划部分中,选择添加以添加新部分。
            1. 选择标准
            2. 命名部分,例如 Apex 预挂钩的 Pre Apex
            3. 在部分类型中,选择 Apex 然后保存
            4. 展开新部分。
            5. 对于阶段,选择定价,对于解决类型,选择默认
            6. 在出现的 Apex 选择框中,输入您上面定义的 Apex 类,例如 ApexDmlAttributePreHook,然后选择保存
            7. 根据需要添加其他预挂钩和后挂钩。
          6. 选择管理部分,以重新安排定价程序部分上方的预挂钩和下方的后挂钩。
          7. 保存对程序计划定义的更改。
          示例
          示例 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;
              }
              
          }
          
          

          预挂钩:对上下文中的所有行应用随机折扣百分比。

          
          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;
              }
          
          }

          预挂钩:使用从标注到外部资源的随机值更新动态属性值。

          
          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;
              }
          }
           
          正在加载
          Salesforce Help | Article