Custom Package Total Calculation

Use a quote calculator plugin to calculate the total price for all components in a quote line and then store that value in the custom field Component Custom Total.
Available in: Salesforce CPQ Winter ’16 and later

These are sample Apex and Javascript plugins for use in the JavaScript Quote Calculator. Each version exports all of the methods that the calculator looks for, and documents their parameters and return types.

Javascript

1export function onInit(lines) {
2    if (lines != null) {
3        lines.forEach(function (line) {
4            line.record["Component_Custom_Total__c"] = 0;
5        });
6    }
7};
8
9export function onAfterCalculate(quoteModel, quoteLines) {
10    if (quoteLines != null) {
11        quoteLines.forEach(function (line) {
12            var parent = line.parentItem;
13            if (parent != null) {
14                var pComponentCustomTotal = parent.record["Component_Custom_Total__c"] || 0;
15                var cListPrice = line.ProratedListPrice__c || 0;
16                var cQuantity = line.Quantity__c == null ? 1 : line.Quantity__c;
17                var cPriorQuantity = line.PriorQuantity__c || 0;
18                var cPricingMethod = line.PricingMethod__c == null ? "List" : line.PricingMethod__c;
19                var cDiscountScheduleType = line.DiscountScheduleType__c || '';
20                var cRenewal = line.Renewal__c || false;
21                var cExisting = line.Existing__c || false;
22                var cSubscriptionPricing = line.SubscriptionPricing__c || '';
23
24                var cTotalPrice = getTotal(cListPrice, cQuantity, cPriorQuantity, cPricingMethod, cDiscountScheduleType, cRenewal, cExisting, cSubscriptionPricing, cListPrice);
25                pComponentCustomTotal += cTotalPrice;
26
27                parent.record["Component_Custom_Total__c"] = pComponentCustomTotal;
28            }
29        });
30    }
31};
32
33function getTotal(price, qty, priorQty, pMethod, dsType, isRen, isExist, subPricing, listPrice) {
34    if ((isRen === true) && (isExist === false) && (priorQty == null)) {
35        // Personal note: In onAfterCalculate, we specifically make sure that priorQuantity can't be null.
36        // So isn't this loop pointless?
37        return 0;
38    } else {
39        return price * getEffectiveQuantity(qty, priorQty, pMethod, dsType, isRen, isExist, subPricing, listPrice);
40    }
41}
42
43function getEffectiveQuantity(qty, priorQty, pMethod, dsType, isRen, exists, subPricing, listPrice) {
44    var delta = qty - priorQty;
45
46    if (pMethod == 'Block' && delta == 0) {
47        return 0;
48    } else if (pMethod == 'Block') {
49        return 1;
50    } else if (dsType == 'Slab' && (delta == 0 || (qty == 0 && isRen == true))) {
51        return 0;
52    } else if (dsType == 'Slab') {
53        return 1;
54    } else if (exists == true && subPricing == '' && delta < 0) {
55        return 0;
56    } else if (exists == true && subPricing == 'Percent Of Total' && listPrice != 0 && delta >= 0) {
57        return qty;
58    } else if (exists == true) {
59        return delta;
60    } else {
61        return qty;
62    }
63}

Apex

1global class QCPWinter16Legacy implements SBQQ.QuoteCalculatorPlugin, SBQQ.QuoteCalculatorPlugin2 {
2
3    /* NOTE: the getReferencedFields method is no longer required if you use the ReferencedFields field set on
4 the Quote Line object. 
5             This field set must be created as it's not a managed one.
6       NOTE: if you need to access Quote fields, you can create the ReferencedFields field set on the Quote object as well.
7       NOTE: if you do not use the getReferencedFields method, you can remove SBQQ.QuoteCalculatorPlugin2 from the class declaration.
8    */
9    global Set<String> getReferencedFields() {
10        return new Set<String>{
11            /* Note: add fields using the following format - Only add fields referenced
12                               
13               by the plugin and not in the Line Editor field set on the Quote Line object
14            String.valueOf(SBQQ__QuoteLine__c.My_Field_API_Name__c)
15            */
16            String.valueOf(SBQQ__QuoteLine__c.Component_Custom_Total__c),
17            
18            String.valueOf(SBQQ__QuoteLine__c.SBQQ__ProratedListPrice__c),
19            String.valueOf(SBQQ__QuoteLine__c.SBQQ__PriorQuantity__c),
20            String.valueOf(SBQQ__QuoteLine__c.SBQQ__PricingMethod__c),
21            String.valueOf(SBQQ__QuoteLine__c.SBQQ__DiscountScheduleType__c),
22            String.valueOf(SBQQ__QuoteLine__c.SBQQ__Renewal__c),
23            String.valueOf(SBQQ__QuoteLine__c.SBQQ__Existing__c),
24            String.valueOf(SBQQ__QuoteLine__c.SBQQ__SubscriptionPricing__c)
25        };
26    }
27
28    global void onBeforePriceRules(SObject quote, SObject[] lines) {
29    }
30    
31    global void onAfterPriceRules(SObject quote, SObject[] lines) {
32    }
33    
34    global void onBeforeCalculate(SObject quote, SObject[] lines) {
35    }
36    
37    global void onAfterCalculate(SObject quote, SObject[] lines) {
38        for(SObject line : lines) {
39            SObject parent = line.getSObject(SBQQ__QuoteLine__c.SBQQ__RequiredBy__c.getDescribe().getRelationshipName());
40            if(parent != null) {
41              Decimal pComponentCustomTotal = (Decimal)parent.get(String.valueOf(SBQQ__QuoteLine__c.Component_Custom_Total__c));
42              
43              Decimal cListPrice = (Decimal)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__ProratedListPrice__c));
44              Decimal cQuantity = (Decimal)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__Quantity__c));
45              Decimal cPriorQuantity = (Decimal)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__PriorQuantity__c));
46              String cPricingMethod = (String)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__PricingMethod__c));
47              String cDiscountScheduleType = (String)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__DiscountScheduleType__c));
48              Boolean cRenewal = (Boolean)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__Renewal__c));
49              Boolean cExisting = (Boolean)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__Existing__c));
50              String cSubscriptionPricing = (String)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__SubscriptionPricing__c));
51              
52              pComponentCustomTotal = (pComponentCustomTotal == null) ? 0 : pComponentCustomTotal;
53              
54              cListPrice = (cListPrice == null) ? 0 : cListPrice;
55              cQuantity = (cQuantity == null) ? 1 : cQuantity;
56              cPriorQuantity = (cPriorQuantity == null) ? 0 : cPriorQuantity;
57              cPricingMethod = (cPricingMethod == null) ? 'List' : cPricingMethod;
58              cDiscountSCheduleType = (cDiscountSCheduleType == null) ? '' : cDiscountSCheduleType;
59              cRenewal = (cRenewal == null) ? false : cRenewal;
60              cExisting = (cExisting == null) ? false : cExisting;
61              cSubscriptionPricing = (cSubscriptionPricing == null) ? '' : cSubscriptionPricing;
62              
63              Decimal cTotalPrice = getTotal(cListPrice, cQuantity, cPriorQuantity, cPricingMethod, cDiscountScheduleType, cRenewal, cExisting, cSubscriptionPricing, cListPrice);
64              pComponentCustomTotal += cTotalPrice;
65              
66              parent.put(SBQQ__QuoteLine__c.Component_Custom_Total__c, pComponentCustomTotal);
67              
68            }
69        }
70    }
71    
72    global void onInit(SObject[] lines) {
73        for(SObject line : lines) {
74            line.put(SBQQ__QuoteLine__c.Component_Custom_Total__c, 0);
75        }
76    }
77    
78    private Decimal getTotal(Decimal price, Decimal quantity, Decimal priorQuantity, String pricingMethod, String discountScheduleType, Boolean renewal, Boolean existing, String subscriptionPricing, Decimal ListPrice) {
79        price = (price == null) ? 0 : price;
80        renewal = (renewal == null) ? false : renewal;
81        existing = (existing == null) ? false : existing;
82        
83        if(renewal == true && existing == false && priorQuantity == null) {
84            return 0;
85        } else {
86            return price * getEffectiveQuantity(quantity, priorQuantity, pricingMethod, discountScheduleType, renewal, existing, subscriptionPricing, listPrice);
87        }
88        
89    }
90    
91    private Decimal getEffectiveQuantity(Decimal quantity, Decimal priorQuantity, String pricingMethod, String discountScheduleType, Boolean renewal, Boolean existing, String subscriptionPricing, Decimal ListPrice) {
92        Decimal result = 0;
93        Decimal deltaQuantity = 0;
94        
95        quantity = (quantity == null) ? 0 : quantity;
96        priorQuantity = (priorQuantity == null) ? 0 : priorQuantity;
97        pricingMethod = (pricingMethod == null) ? '' : pricingMethod;
98        discountScheduleType = (discountScheduleType == null) ? '' : discountScheduleType;
99        subscriptionPricing = (subscriptionPricing == null) ? '' : subscriptionPricing;
100        renewal = (renewal == null) ? false : renewal;
101        existing = (existing == null) ? false : existing;
102        listPrice = (listPrice == null) ? 0 : listPrice;
103        
104        deltaQuantity = quantity - priorQuantity;
105        
106        if(pricingMethod == 'Block' && deltaQuantity == 0) {
107            result = 0;
108        } else {
109            if(pricingMethod == 'Block') {
110                result = 1;
111            } else {
112                if(discountScheduleType == 'Slab' && (deltaQuantity == 0 || (quantity == 0 && renewal == true))) {
113                    result = 0;
114                } else {
115                    if(discountScheduleType == 'Slab') {
116                        result = 1;
117                    } else {
118                        if(existing == true && subscriptionPricing == '' && deltaQuantity < 0) {
119                            result = 0;
120                        } else {
121                            if(existing == true && subscriptionPricing == 'Percent Of Total' && listPrice != 0 && deltaQuantity >= 0) {
122                                result = quantity;
123                            } else {
124                                if(existing == true) {
125                                    result = deltaQuantity;
126                                } else {
127                                    result = quantity;
128                                }
129                            }
130                        }
131                    }
132                }
133            }
134        }
135        
136        return result;
137    }
138    
139}