使用程序計畫架構
若要確保您的程序依正確順序執行,且定價一致套用至報價,請建立新的程序計畫。
必要版本
| 適用於:Lightning Experience |
| 適用於:啟用 Salesforce 定價的 Revenue Cloud Enterprise、Performance、Unlimited 及 Developer Edition |
| 需要的使用者權限 | |
|---|---|
| 建立和更新程序計畫定義: | 程序計畫存取權 和 Salesforce 定價設計時間使用者 |
| 建立、更新和刪除定價程序: | 程序計畫存取權 或 Salesforce 定價設計時間使用者 |
| 若要使用定價程序: | Salesforce 定價執行階段使用者 |
| 為 Apex 類別定義、編輯、刪除、設定安全性和設定版本設定: | Author Apex |
想像一下您在航空公司工作並協助商務旅行者。現在,讓我們預訂航班!我們想要定價反映即時市場條件,並以客戶的當地貨幣顯示價格。
在計算價格之前,我們會自動從外部系統中提取動態基本價格,以確保定價具有競爭力。我們也會套用預訂平台的折扣,並新增便利費。
建立程序計畫後,我們會建立報價。接著,根據使用者的位置 (例如,印度),我們會將總價從 USD 轉換為 INR。
開始之前,請確保您:
- 已啟用 Salesforce 定價。
- 擁有建立程序、程序計畫、產品和報價的權限。
- 瞭解如何建立定價程序。若要深入瞭解定價程序,請參閱使用 Salesforce 定價 建立您的定價程序。
- 瞭解並瞭解如何使用「內容定義」。若要深入瞭解內容定義,請參閱 內容定義。
- 在您的定價程序和程序計畫中使用相同的內容定義。
程序計畫定義可能很複雜,特別是在涉及 Apex 類別和不同的定價程序時。請遵循下列步驟來建立報價,並瞭解如何讓定價動態且準確,而無須複雜程序本身。
建立商業產品
- 進入 App Launcher,尋找並選取「產品」。
-
建立名為 Delhi - New York 的商業產品。
若要瞭解如何建立產品,請參閱 建立簡單產品並確保將「產品記錄類型」設定為「商業」。
- 將您的產品新增至目錄,並為其建立價目手冊項目。
- 進入「設定」,在「快速尋找」方塊中搜尋並選取「Salesforce 定價設定」。
- 在「同步化定價資料」區段中,按一下「同步化」。
建立您的定價程序
-
建立定價程序並將其命名為「航班預訂定價程序」。
若要建立定價程序,請遵循設定定價程序中的前 5 個步驟。
-
建立常數。這些常數會作為定價程序中固定值的預留位置。
常數名稱 資料類型 預設值 AdjType TEXT 百分比 AdjValue NUMBER 5 個 覆寫 TEXT 覆寫 ConvFeeAdjType TEXT Amount ConvFeeAdjValue NUMBER -250 - 新增下列元素。
-
系統會新增第一個「手動折扣」元素,以計算航班的動態基本價格。對應這些變數。
- 輸入變數
- 調整類型:覆寫
- 調整值:PartnerUnitPrice
- 數量:LineItemQuantity
- 輸入單價:ListPrice
- 輸出變數
- 淨單價: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 後接點,新增區段以將價格從 USD 轉換為 INR (美元轉換為印度盧比)
- 類型:標準。
- 名稱:ConvertFareToINR。
- 區段類型:Apex。
-
按一下
並指定這些詳細資料。
- 階段:定價
- 解決類型:以規則為基礎
- 條件需求:符合 所有條件 (AND)
- 資源:帳單對象國家
- 運算子:等於
- 輸出值:印度
- Apex:ConvertFareToINRApex
- 儲存,然後啟用您的手術計畫定義。
驗證您的程序計畫執行
若要確認程序計畫是否依照我們設定的順序執行,且定價正確,我們需要建立報價。
-
建立報價。
在「帳單對象」欄位中,輸入 印度。
- 請儲存您的變更。
- 按一下「瀏覽目錄 」,然後將「德里 - 紐約」產品新增至報價。
-
將滑鼠停留在「Net Unit Price」值上以查看價格瀑布圖詳細資料。
您會看見程序計畫的應用,以及您在定價程序中設定的折扣和新增項目。
-
若要檢視報價條列項目的已轉換匯率,請按一下報價條列項目列中的
,然後選取「檢視」。
-
「條列項目描述」會顯示您航班的本地化價格。
此文章是否解決您的問題?
請讓我們知道,以便我們改進!

