使用程序计划框架
为了确保您的程序以正确的顺序运行,并且定价一致地应用于报价,请创建新的程序计划。
所需的 Edition
| 适用于:Lightning Experience |
| 适用于:启用了 Salesforce 定价的 Revenue Cloud 的 Enterprise、Performance、Unlimited 和 Developer Edition |
| 所需用户权限 | |
|---|---|
| 创建和更新手术计划定义: | 程序计划访问权限 与 Salesforce 定价设计时间用户 |
| 创建、更新和删除定价程序: | 程序计划访问权限 或者 Salesforce 定价设计时间用户 |
| 要使用定价程序: | Salesforce 定价运行时用户 |
| 定义、编辑、删除和设置 Apex 类的版本设置: | 作者 Apex |
想象一下,您在一家航空公司工作,为商务旅客提供帮助。现在,让我们预订航班!我们希望我们的定价反映实时市场状况,并以客户的当地货币显示价格。
在计算价格之前,我们会自动从外部系统获取动态基本票价,以确保定价具有竞争力。我们还将应用预订平台的折扣,并添加便利费。
在我们构建手术计划后,我们将创建报价。然后,根据用户的位置(例如,印度),我们将总价从美元转换为印度卢比。
在开始前,请确保:
- 已启用 Salesforce 定价。
- 拥有创建程序、程序计划、产品和报价的权限。
- 了解如何创建定价程序。要了解有关定价程序的更多信息,请参阅使用 Salesforce 定价构建定价程序。
- 了解并知道如何使用上下文定义。要了解有关上下文定义的更多信息,请参见上下文定义。
- 在您的定价程序和程序计划中使用相同的上下文定义。
程序计划定义可能很复杂,特别是当它们涉及 Apex 类和不同的定价程序时。按照这些步骤创建报价,并了解如何使定价动态准确,而不使过程本身复杂化。
创建商业产品
- 从应用程序启动程序中,查找并选择产品。
-
创建名为 Delhi - New York 的商业产品。
要了解如何创建产品,请查看创建简单产品,并确保产品记录类型设置为商业。
- 将产品添加到目录,并为其创建价格手册条目。
- 从“设置”中,在快速查找框中,搜索并选择 Salesforce 定价设置。
- 在“同步定价数据”部分,单击同步。
构建定价程序
-
创建定价程序,并将其命名为航班预订定价程序。
要创建定价程序,请按照配置定价程序中的前 5 步操作。
-
创建常量。这些常量将作为定价程序中固定值的占位符。
常量名称 数据类型 默认值 AdjType TEXT 百分比 AdjValue NUMBER 5 覆盖 TEXT 覆盖 ConvFeeAdjType TEXT 金额 ConvFeeAdjValue NUMBER -250 - 添加以下元素。
-
添加第一个手动折扣元素来计算航班的动态基本票价。映射这些变量。
- 输入变量
- 调整类型:覆盖
- 调整值:PartnerUnitPrice
- 数量:LineItemQuantity
- 输入单价:价目表价格
- 输出变量
- 净单价:NetUnitPrice
- 输入变量
-
添加第二个手动折扣元素来计算航班预订的平台折扣。映射这些变量。
- 输入变量
- 调整类型:AdjType
- 调整值:AdjValue
- 数量:LineItemQuantity
- 输入单价:PartnerUnitPrice
- 输出变量
- 净单价:NetUnitPrice
- 输入变量
-
添加最后一个手动折扣元素来计算向客户收取的航班预订便利费。
- 输入变量
- 调整类型:ConvFeeAdjType
- 调整值:ConvFeeAdjValue
- 数量:LineItemQuantity
- 输入单价:NetUnitPrice
- 输出变量
- 净单价:NetUnitPrice
- 输入变量
- 设置首选项,以查看定价信息、简档访问权限和排名信息。
- 保存您的程序。
- 激活您的程序。
您的程序应该如下所示。为清楚起见,我们重命名了每个手动折扣元素,以显示它将执行的定价计算。要重命名元素,单击
并输入所需的名称。

定义 Apex 钩子的类
- 从“设置”的“快速查找”框中,输入Apex,然后选择 Apex 类。
- 单击新建。
-
第一个 Apex 类是一个预挂钩,它从定价上下文中获取所有销售交易项目,并在从外部数据库执行定价之前,用 990 到 1200 之间的动态随机基本票价覆盖它们的 PartnerUnitPrice 标签。
public class DynamicFlightBasePriceApex implements RevSignaling.SignalingApexProcessor { public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) { System.debug('Executing PREHOOK'); String contextId = request.ctxInstanceId; Context.IndustriesContext industriesContext = new Context.IndustriesContext(); //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); // Generate random price between 990 and 1200 Decimal minPrice = 990; Decimal maxPrice = 1200; Decimal range = maxPrice - minPrice; // Use Crypto.getRandomInteger() to generate a secure random number Integer randomInt = Math.abs(Crypto.getRandomInteger()); Decimal randomPrice = minPrice + Math.mod(randomInt, range.intValue() + 1); System.debug('Generated Random Price: ' + randomPrice); // 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)); 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' => 'PartnerUnitPrice', 'attributeValue' => randomPrice } } }); } // 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); } // Return the response RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse(); response.status = RevSignaling.TransactionStatus.SUCCESS; response.message = 'Apex executed successfully with Random Price: ' + randomPrice; return response; } } -
创建其他 Apex 类。
该 Apex 类是一个挂钩后,它在定价后获取 NetUnitPrice 标签,并使用 85-86 之间的模拟动态兑换率将其转换为本地货币 INR(印度卢比)。然后,它使用新的 INR 票价更新销售交易项目描述,为印度客户提供本地化定价可见性。
public class ConvertFareToINRApex implements RevSignaling.SignalingApexProcessor { public RevSignaling.TransactionResponse execute(RevSignaling.TransactionRequest request) { System.debug('Executing POSTHOOK'); String contextId = request.ctxInstanceId; Context.IndustriesContext industriesContext = new Context.IndustriesContext(); //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); //Query NetUnitPrice nodes Map<String, Object> inputQueryItem2 = new Map<String, Object>{ 'contextId' => contextId, 'tags' => new List<String>{ 'NetUnitPrice' } }; Map<String, Object> itemQueryOutput2 = industriesContext.queryTags(inputQueryItem2); Map<String, Object> itemQueryResult2 = (Map<String, Object>)itemQueryOutput2.get('queryResult'); List<Object> itemData2 = (List<Object>) itemQueryResult2.get('NetUnitPrice'); System.debug('NetUnitPrice itemData=' + itemData2); Map<String, Object> netUnitTagData = (Map<String, Object>) itemData2.get(0); Decimal netUnitPrice = (Decimal) netUnitTagData.get('tagValue'); netUnitPrice = netUnitPrice.setScale(2); System.debug('NetUnitPrice Tag Value=' + netUnitPrice); // Generate random price between 85.00 and 86.00 with decimals Decimal minPrice = 85.00; Decimal maxPrice = 86.00; Decimal range = maxPrice - minPrice; Integer randomInt = Math.abs(Crypto.getRandomInteger()); Integer randomDecimalInt = Math.mod(randomInt, 100); Decimal randomFraction = Decimal.valueOf(randomDecimalInt) / 100; Decimal usdToInrRate = minPrice + randomFraction; if (usdToInrRate > maxPrice) { usdToInrRate = maxPrice; } // Calculate NetUnitPrice in INR Decimal fareInInr = (netUnitPrice * usdToInrRate).setScale(2); String fareMessage = 'The Flight fare in INR is: ' + fareInInr + ' (USD ' + netUnitPrice + ' at rate ' + usdToInrRate + ')'; System.debug(fareMessage); // 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)); 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' => fareMessage } } }); } // 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); } // Return the response RevSignaling.TransactionResponse response = new RevSignaling.TransactionResponse(); response.status = RevSignaling.TransactionStatus.SUCCESS; response.message = 'Apex executed successfully with Price in INR: ' + fareInInr; return response; } }
配置自定义程序计划定义
- 从“设置”中,在快速查找框中,搜索并选择程序计划定义。
- 单击新建。
-
指定这些详细信息。
-
标题: 航班预订程序计划。
按 Tab 键,以自动填充开发人员姓名。
- 进程类型: Revenue Cloud。
- 主对象:报价。
-
上下文定义:<上下文定义名称>。
您需要选择专为用例设计的上下文定义。但是,请确保这与您在构建定价程序时使用的上下文定义相同。
- 保存更改。
-
标题: 航班预订程序计划。
- 打开新创建的程序计划定义记录。
- 要在“程序计划”部分添加所需的程序,请选择添加。
-
添加第一部分,以获取航班的动态基本价格。指定这些详细信息。
- 类型:标准。
- 名称:DynamicBasePriceApex。
- 部分类型: Apex。
-
添加部分后,单击
并指定这些详细信息。
- 阶段:定价
- 分辨率类型:默认
- Apex: DynamicFlightBasePriceApex
它看起来应该是这样的:
-
同样,添加另一个部分来执行定价计算,并使用我们使用 Apex 预挂钩生成的价格覆盖基本价格。指定这些值。
- 类型:标准。
- 名称:FlightPriceCalculation。
- 部分类型:定价程序。
-
单击
并指定这些详细信息。
- 阶段:定价
- 分辨率类型:默认
- 程序:Flight_Booking_Pricing_Procedure
-
最后,通过为开单国家/地区设置为印度的用户添加此 Apex Posthook,添加将价格从美元转换为印度卢比的部分
- 类型:标准。
- 名称:ConvertFareToINR。
- 部分类型: Apex。
-
单击
并指定这些详细信息。
- 阶段:定价
- 分辨率类型:基于规则
- 条件要求:满足所有条件 (AND)
- 资源:开单地址
- 运算符:等于
- 输出值:印度
- Apex: ConvertFareToINRApex
- 保存,然后激活手术计划定义。
验证您的程序计划执行情况
为了验证手术计划是否按照我们设置的顺序执行,以及定价是否正确,我们需要创建报价。
-
创建报价。
在开单地址国家/地区字段中,输入印度。
- 保存更改。
- 单击浏览目录,并将德里 - 纽约产品添加到报价。
-
将鼠标悬停在净单价值上,以查看价格瀑布图详细信息。
您将看到程序计划的应用,以及您在定价程序中配置的折扣和附加。
-
要查看报价行项目的兑换率,单击报价行项目行
中的 ,然后选择查看。
-
行项目描述显示航班的本地化价格。
本文章是否解决您的问题?
请与我们共享您的想法,以便我们进行改进!

