Loading
Aprender
Índice de materias
Seleccionar filtros

          No hay resultados
          No hay resultados
          Estas son algunas sugerencias de búsqueda

          Compruebe la ortografía de sus palabras clave.
          Utilice términos de búsqueda más generales.
          Seleccione menos filtros para ampliar su búsqueda.

          Buscar en toda la Ayuda de Salesforce
          Personalizar sus planes de procedimiento con Apex Hooks

          Personalizar sus planes de procedimiento con Apex Hooks

          Para admitir escenarios de precios exclusivos, agregue lógica Apex personalizada a sus planes de procedimientos de precios. Puede utilizar ganchos Apex para aplicar lógica comercial personalizada que modifica el contexto de precios después de configurar una partida de presupuesto. Utilice un preenganche Apex para ajustar los precios basándose en atributos de productos antes de que se calculen los precios, y un postenganche Apex para gestionar cambios de precios para grupos y otros elementos de objeto Presupuesto después de los precios. Cuando un representante de ventas configura un producto o cambia un grupo de partidas de presupuesto, el plan de procedimiento de precios cambia los precios basándose en las instrucciones en Apex.

          Ediciones necesarias

          Disponible en: Lightning Experience
          Disponible en: Ediciones Enterprise, Unlimited y Developer de Revenue Cloud donde Precios de Salesforce está activado
          Permisos de usuario necesarios
          Para crear, actualizar y eliminar procedimientos de precios y planes de procedimientos: Acceso de plan de procedimiento o usuario de tiempo de diseño de precios de Salesforce
          Para utilizar procedimientos de precios: Usuario de tiempo de ejecución de precios de Salesforce
          Para definir, modificar , eliminar, definir seguridad y establecer la configuración de la versión para clases de Apex: Apex de autor
          Importante
          Importante
          • Cuando utilice el tipo de proceso Revenue Cloud, cualquier lógica Apex que agregue debe ser el primer o el último elemento en la secuencia de ejecución del Plan de procedimiento.
          • Las llamadas externas desde un hook Apex solo se admiten cuando se desencadena la solicitud Realizar transacción de ventas a través de la interfaz de usuario de Salesforce o la API de realización de transacciones de ventas. No se admiten cuando la solicitud se desencadena desde Apex o Flow.
          • Las llamadas externas en ganchos Apex no son compatibles cuando el modo Persistencia doble está activado. Para obtener directrices adicionales, póngase en contacto con el Servicio de atención al cliente de Salesforce.
          • Las llamadas externas en hooks Apex pueden afectar al rendimiento. Por lo tanto, no se pueden garantizar objetivos de nivel de servicio (SLO) predecibles.
          1. Asegúrese de que Orquestación de planes de procedimientos para precios está activada.
            1. Desde Configuración, en el cuadro Búsqueda rápida, introduzca Configuración de ingresos y, a continuación, seleccione Configuración de ingresos.
            2. Busque y, si es necesario, active el parámetro Procedimiento Planificar orquestación para precios.
            3. Deje la configuración Excluir procedimientos de precios de tipo de transacción de ventas y predeterminados desactivada.
          2. Defina clases para los ganchos de Apex para agregar a sus procedimientos de precios.
            1. Desde Configuración, en el cuadro Búsqueda rápida, introduzca Apex y, a continuación, seleccione Clases Apex.
            2. Seleccione Nuevo para crear una nueva clase Apex.
            3. En el editor de clases, introduzca la definición de clase.

              Por ejemplo, este pregancho (ApexDmAttributePreHook) actualiza los valores de atributos dinámicos con el nombre Display_Size basándose en su tamaño de visualización. Más clases de muestra se muestran en Clases de muestra para ganchos de precios Apex al final de estos pasos.

              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. Guarde la definición de clase.
          3. Desde Configuración, en el cuadro Búsqueda rápida, introduzca Plan de procedimiento y, a continuación, seleccione Definiciones de plan de procedimiento.
          4. En la columna Nombres de definición, seleccione una definición de plan de procedimiento para modificar.
          5. Desde la definición del plan de procedimiento, en Secciones de plan de procedimiento, seleccione Agregar para agregar una nueva sección.
            1. Seleccione Estándar.
            2. Asigne un nombre a la sección, por ejemplo Pre Apex para un preenganche de Apex.
            3. En Tipo de sección, seleccione Apex y Guardar.
            4. Amplíe la nueva sección.
            5. Para Fases, seleccione Precios y para Tipo de resolución seleccione Predeterminado.
            6. En el cuadro de selección Apex que aparece, introduzca la clase Apex que definió anteriormente, por ejemplo ApexDmlAttributePreHook y, a continuación, seleccione Guardar.
            7. Agregue ganchos previos y ganchos posteriores adicionales según sea necesario.
          6. Seleccione Gestionar secciones para reorganizar los preganchos por encima de la sección Procedimiento de precios y los postganchos por debajo de ella.
          7. Guarde sus cambios en la definición Plan de procedimiento.
          Ejemplo
          Ejemplo Clases de muestra para ganchos de precios Apex

          Preenganche: Actualice el porcentaje de descuento al 2% en todas las partidas en el contexto (por ejemplo, partidas de presupuesto).

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

          Preenganche: Llame a un recurso externo para obtener un valor de cantidad.

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

          Preenganche: Desencadene una actualización del precio de un producto si su Nombre de atributo es Pantalla y su Valor de atributo es Pantalla integrada 1080p, o si su Nombre de atributo es Impresora y su Valor de atributo es Láser.

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

          Preenganche: Aplique un porcentaje de descuento aleatorio para todas las líneas en el contexto.

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

          Preenganche: Actualice un valor de atributo dinámico con un valor aleatorio desde una llamada a un recurso externo.

          
          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: Actualice la descripción de cada partida en el contexto.

          
          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;
              }
          }
           
          Cargando
          Salesforce Help | Article