Annual Pricing Variable

A custom field with a defined pricing variable alone isn’t sufficient for annual prices. Rollups are needed for both the line items and the order header. The order total must incorporate the annual prices, and these prices must be capable of handling adjustments, discounts, and other modifications.

Annual pricing variable enables users to specify an annual price for products within the Enterprise Product Catalog (EPC). This pricing detail can then be utilized in both the Industries Configure, Price, Quote (CPQ) Cart and the Standard Digital Commerce API.

Initial Setup 

Create Custom Fields on Order Item 

Field LabelField NameData TypePurpose
Annual ChargeAnnualCharge__cCurrency(16, 2)Store base annual price from PLE or ABP
Annual Calculated PriceAnnualCalculatedPrice__cCurrency(16, 2)Net price, if discount is present
Annual Effective TotalAnnualEffectiveTotal__cCurrency(16, 2)Annual total for root items (Bundle Total)
Annual Manual DiscountAnnualManualDiscount__cPercent(2, 2)Discount percentage field
Annual TotalAnnualTotal__cCurrency(16, 2)Total for calculated price and rollup with quantity

Create Custom Fields on Order 

Field LabelField NameData TypeFormula
Total: Annual ChargesAnnualTotal__cRoll-Up Summary (Annual Total)-
Effective Total: AnnualAnnualEffectiveTotal__cRoll-Up Summary (Annual Effective Total)-
Order TotalOrderTotal2__cFormulavlocity_cmt__OneTimeTotal__c + vlocity_cmt__Tax__c +( vlocity_cmt__RecurringTotal__c * vlocity_cmt__NumberOfContractedMonths__c )+(AnnualTotal__C*(vlocity_cmt__NumberOfContractedMonths__c/12))
Effective Order TotalEffectiveOrderTotal2__cFormulavlocity_cmt__EffectiveOneTimeTotal__c + vlocity_cmt__Tax__c +( vlocity_cmt__EffectiveRecurringTotal__c * vlocity_cmt__NumberOfContractedMonths__c ) + (AnnualEffectiveTotal__c * (vlocity_cmt__NumberOfContractedMonths__c) / 12)

The total formulas use the vlocity_cmt__NumberOfContractedMonths__c field to prorate Annual totals. This can be adjusted or changed independently without affecting the rest of the configuration.

Note

Create Annual Pricing Variable 

Pricing Variable NameCodeAggregationCharge TypeFrequencyScopeApplies To VariablePricing Variable Binding NameObjectFieldTypeSub-TypeValueType
Annual Charge Adjustment AbsoluteANNUAL_CHARGE_ADJ_ABSUnitAdjustmentYearlyLineAnnual Std PricePriceStandardPricing Element
Annual Charge Adjustment PercentANNUAL_CHARGE_ADJ_PERCUnitAdjustmentYearlyLineAnnual Std PricePriceStandardPricing Element
Annual Std PriceANNUAL_CHARGEUnitRecurringYearlyLineAnnual Std Price - OrderItemOrderItemAnnualCharge__cPriceStandardPricing Element
Annual Std Price CalculatedANNUAL_STD_PRC_CALCUnitRecurringYearlyLineAnnual Std Price Calculated - OrderItemOrderItemAnnualCalculatedPrice__cPriceStandardCalculated
Annual Std Price Discount Pct ManualANNUAL_STD_PRC_DISC_PCT_MANUnitAdjustmentYearlyLineAnnual Std PricePriceStandardCalculated
Annual Std Price TotalANNUAL_STD_PRC_TOTALQuantityRecurringYearlyLineAnnual Std Price Total - OrderItemOrderItemAnnualTotal__cPriceStandardCalculated
Effective Annual Std Price TotalEFF_ANNUAL_STD_PRC_TOTALQuantityRecurringYearlyLineEffective Annual Std Price Total - OrderItemOrderItemAnnualEffectiveTotal__cPriceStandardCalculated
Group Rollup Annual Std Price TotalGROUP_ROLLUP_ANNUAL_STD_PRC_TOTALQuantityRecurringYearlyGroup RollupParent Rollup Annual Std Price TotalPriceStandardCalculated
Parent Rollup Annual Std Price TotalPARENT_ROLLUP_ANNUAL_STD_PRC_TOTALQuantityRecurringParent Rollup Annual Std Price Total - OrderOrderItemAnnualEffectiveTotal__cPriceStandardCalculated
Rollup Annual Std Price TotalROLLUP_ANNUAL_STD_PRC_TOTALQuantityRecurringRollupAnnual Std Price TotalPriceStandardCalculated

Create Custom Pricing Hook 

Here’s the sample code for creating a custom pricing hook.

1global with sharing class PricingVariableMapHookImplementation implements vlocity_cmt.VlocityOpenInterface
2{
3    global Boolean invokeMethod (String methodName, Map<String, Object> input,Map<String, Object> output, Map<String, Object> option) {
4        System.debug ('debug PricingVariableCalcHook: class called');
5        if(methodName == 'calculate.PostInvoke') {
6            calculatePostInvoke (input, output);
7        }
8        return true;
9    }
10
11    private void calculatePostInvoke(Map<String, Object> input,Map<String, Object> output) {
12
13    // New code from here
14        System.debug('?? initial output map: '+JSON.serialize(output));
15        Boolean isRoot = (Boolean)input.get('isRoot');
16        System.debug('?? isRoot: '+(Boolean)input.get('isRoot'));
17        Map<string, Object> calculatedPricingLogDataMap = (Map<string, Object>)output.get('calculatedPricingLogData');
18        Map<String, Object> pricingVariableCodeToValueMap = (Map<String,Object>)output.get('pricingVariableCodeValueMap');
19
20        // Below 2 checks for apply promo scenario for cart API as apply promo is still uses classic flow
21        // Should not be required for standard DC API
22        if(calculatedPricingLogDataMap == null ) {
23            calculatedPricingLogDataMap = (Map<String, Object>)output.get('pricingLogDataMap');
24        }
25
26        if(pricingVariableCodeToValueMap == null ) {
27            pricingVariableCodeToValueMap = (Map<String, Object>)output.get('pricingVariableMap');
28        }
29
30        Decimal lineQuantity = getValueFromPVC(pricingVariableCodeToValueMap, 'LINE_QUANTITY');
31
32
33        Decimal annualStdPrice = getValueFromPVC(pricingVariableCodeToValueMap,'ANNUAL_CHARGE');
34        System.debug('?? annualStdPrice: ' + String.valueOf(annualStdPrice));
35
36        Decimal annualStdPriceDiscPctManual = getValueFromPVC(pricingVariableCodeToValueMap,'ANNUAL_STD_PRC_DISC_PCT_MAN');
37
38        Decimal rollupAnnualStdPriceTotal = getValueFromPVC(pricingVariableCodeToValueMap,'ROLLUP_ANNUAL_STD_PRC_TOTAL');
39        System.debug('?? rollupAnnualStdPriceTotal: ' + String.valueOf(rollupAnnualStdPriceTotal));
40
41        //getValueFromPVC(pricingVarCodeValueMap, PricingVariableCode.EFF_REC_MONTH_STD_PRICE_TOTAL);
42
43        Decimal annualStdPriceCalc = annualStdPrice - (annualStdPrice * (annualStdPriceDiscPctManual / 100.0));
44
45        Double annualStdPriceTotal = lineQuantity * (annualStdPriceCalc.doubleValue() + rollupAnnualStdPriceTotal.doubleValue());
46
47        // Should consider rounding of value
48        pricingVariableCodeToValueMap.put('ANNUAL_STD_PRC_TOTAL',annualStdPriceTotal);
49
50        pricingVariableCodeToValueMap.put('ANNUAL_STD_PRC_CALC',annualStdPriceCalc);
51
52        calculatedPricingLogDataMap.put('ANNUAL_STD_PRC_TOTAL',
53                    new Map<String, Object>{
54                        'format'=> '[%s (%s) + Rollup %s (%s)] x %s (%s)',
55                        'data'=>new List<String>{'Annual Calculated Price',
56                                                  String.valueOf(annualStdPriceCalc),
57                                                  'Annual Total',
58                                                  String.valueOf(rollupAnnualStdPriceTotal),
59                                                 'LINE_QUANTITY',
60                                                 String.valueOf(lineQuantity)
61                                             }});
62
63        if (isRoot) {
64            pricingVariableCodeToValueMap.put('EFF_ANNUAL_STD_PRC_TOTAL',annualStdPriceTotal);
65            System.debug('?? annualStdPriceTotal: ' + String.valueOf(annualStdPriceTotal));
66        }24
67
68        System.debug('?? final output map'+JSON.serialize(output));
69        //logic for line item totals and rollups
70
71        //Set line item level custom fields
72        //Getting the cart document instance
73        vlocity_cmt.CpqCartDocument cart = (vlocity_cmt.CpqCartDocument)input.get('cartDocument');
74        String currentItemId = (String)input.get('itemId');
75        System.debug('debug currentItemId:'+currentItemId);
76        //method to get all the line items
77        Map<String, Object> outputMap = cart.call('getAllItems', new Map<String, Object>{'hierarchyLevel'=>-1});
78        Map<String, vlocity_cmt.CpqCartDocumentItem> allItemsMap = (Map<String, vlocity_cmt.CpqCartDocumentItem>) outputMap.get('result');
79
80        //Getting the cartDocumentItem having id as currentItemId
81        vlocity_cmt.CpqCartDocumentItem currentItem = allItemsMap.get(currentItemId);
82
83        Map<String,Object> itemArgs = new Map<String, Object>();
84        itemArgs.put('annualrecurringcharge', annualStdPrice);
85        Double total = 0.0;
86        itemArgs.put('annualtotal', annualStdPriceTotal);
87
88        currentItem.call('setItemFields', new Map<String,Object>{'fieldValueMap' => itemArgs});
89    }
90
91    private Decimal getValueFromPVC(Map<String, Object> pricingVarCodeValueMap, String pricingVariableCode) {
92
93        Object val = pricingVarCodeValueMap.get(pricingVariableCode);
94        if (val == null) {
95            pricingVarCodeValueMap.put(pricingVariableCode, 0.0);
96        } else {
97            pricingVarCodeValueMap.put(pricingVariableCode, val);
98        }
99        return Decimal.valueOf((Double) pricingVarCodeValueMap.get(pricingVariableCode));
100    }
101}

Create Interface Implementation Record 

First, create the interface. Then, associate the Active Implementation Class from the Interface Implementation Detail related list. The Active Implementation Class field will be automatically populated once the implementation record is created.

  • Interface Name: CpqPricingVariableCalcHook
  • Active Implementation Class: PricingVariableMapHookImplementation

Enable Standard Digital Commerce 

You need to enable standard digital commerce. See Enable Standard Digital Commerce.

Create Cart Mapping Entries for Annual Custom Fields 

NameDestination Field nameDestination SObject TypeSource Field NameSource SObject TypeSource Field Type
OrderItem>CPQCartDocumentItem101AnnualChargeCpqCartDocumentItemAnnualChargeOrderItemstring
OrderItem>CPQCartDocumentItem102AnnualCalculatedPriceCpqCartDocumentItemAnnualCalculatedPriceOrderItemstring
OrderItem>CPQCartDocumentItem103AnnualEffectiveTotalCpqCartDocumentItemAnnualEffectiveTotalOrderItemstring
OrderItem>CPQCartDocumentItem104AnnualManualDiscountCpqCartDocumentItemAnnualManualDiscountOrderItemstring
OrderItem>CPQCartDocumentItem105AnnualTotalCpqCartDocumentItemAnnualTotalOrderItemstring

Add Custom Fields to Basket 

You need to add fields to the basket. See Add Fields to the Basket.