Calculating True End Date and Subscription Term
You can use a quote calculator plugin to calculate values and store maximum values
for the custom quote line fields True Effective End Date and True Effective
Term.
| 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.
Apex
1global class QCPWinter16Legacy2 implements SBQQ.QuoteCalculatorPlugin, SBQQ.QuoteCalculatorPlugin2 {
2
3/* This QCP examples calculates and stores the effective end date on each quote line, as well as the effective term.
4 It also stores the max(effective end date) and max(effective term) on the Quote object
5*/
6
7
8 /* NOTE: the getReferencedFields method is no longer required if you use the ReferencedFields field set on
9 the Quote Line object.
10 This field set must be created as it's not a managed one.
11 NOTE: if you need to access Quote fields, you can create the ReferencedFields field set on the Quote object as well.
12 NOTE: if you do not use the getReferencedFields method, you can remove SBQQ.QuoteCalculatorPlugin2 from the class declaration.
13 */
14 global Set<String> getReferencedFields() {
15 return new Set<String>{
16 /* Note: add fields using the following format - Only add fields referenced
17
18 by the plugin and not in the Line Editor field set on the Quote Line object
19 String.valueOf(SBQQ__QuoteLine__c.My_Field_API_Name__c)
20 */
21 String.valueOf(SBQQ__QuoteLine__c.True_Effective_End_Date__c),
22 String.valueOf(SBQQ__QuoteLine__c.True_Effective_Term__c),
23
24 String.valueOf(SBQQ__Quote__c.True_Effective_End_Date__c),
25 String.valueOf(SBQQ__Quote__c.True_Effective_Term__c),
26
27 String.valueOf(SBQQ__QuoteLine__c.SBQQ__EffectiveStartDate__c),
28 String.valueOf(SBQQ__QuoteLine__c.SBQQ__EffectiveEndDate__c),
29 String.valueOf(SBQQ__QuoteLine__c.SBQQ__SubscriptionTerm__c),
30 String.valueOf(SBQQ__QuoteLine__c.SBQQ__DefaultSubscriptionTerm__c),
31
32 String.valueOf(SBQQ__Quote__c.SBQQ__SubscriptionTerm__c)
33 };
34 }
35
36 global void onBeforePriceRules(SObject quote, SObject[] lines) {
37 }
38
39 global void onAfterPriceRules(SObject quote, SObject[] lines) {
40 }
41
42 global void onBeforeCalculate(SObject quote, SObject[] lines) {
43 }
44
45 global void onAfterCalculate(SObject quote, SObject[] lines) {
46 Date maxEffectiveEndDate = null;
47 Decimal maxEffectiveTerm = 0;
48 for(SObject line : lines) {
49 Date trueEndDate = calculateEndDate(quote, line);
50 Decimal trueTerm = getEffectiveSubscriptionTerm(quote, line);
51 if(maxEffectiveEndDate == null || maxEffectiveEndDate < trueEndDate) {
52 maxEffectiveEndDate = trueEndDate;
53 }
54 if(maxEffectiveTerm < trueTerm) {
55 maxEffectiveTerm = trueTerm;
56 }
57 line.put(SBQQ__QuoteLine__c.True_Effective_End_Date__c, trueEndDate);
58 line.put(SBQQ__QuoteLine__c.True_Effective_Term__c, trueTerm);
59 }
60 quote.put(SBQQ__Quote__c.True_Effective_End_Date__c, maxEffectiveEndDate);
61 quote.put(SBQQ__Quote__c.True_Effective_Term__c, maxEffectiveTerm);
62 }
63
64 global void onInit(SObject[] lines) {
65 }
66
67 private Date calculateEndDate(SObject quote, SObject line) {
68 Date startDate = (Date)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__EffectiveStartDate__c));
69 Date endDate = (Date)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__EffectiveEndDate__c));
70 if ((startDate != null) && (endDate == null)) {
71 /* Note: we are assuming that Subscription Term Unit is Month in the package settings */
72 endDate = startDate.addMonths(getEffectiveSubscriptionTerm(quote, line).intValue()).addDays(-1);
73 /* Note: we are assuming that Subscription Term Unit is Day in the package settings */
74// endDate = startDate.addDays(getEffectiveSubscriptionTerm(line).intValue() - 1);
75 }
76 return endDate;
77 }
78
79 private Decimal getEffectiveSubscriptionTerm(SObject quote, SObject line) {
80 Decimal lineTerm = null;
81 Date startDate = (Date)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__EffectiveStartDate__c));
82 Date endDate = (Date)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__EffectiveEndDate__c));
83 if ((startDate != null) && (endDate != null)) {
84 /* Note: we are assuming that Subscription Term Unit is Month in the package settings */
85 lineTerm = startDate.monthsBetween(endDate.addDays(1));
86 /* Note: we are assuming that Subscription Term Unit is Day in the package settings */
87// lineTerm = startDate.daysBetween(endDate.addDays(1));
88 } else {
89 lineTerm = (Decimal)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__SubscriptionTerm__c));
90 if (lineTerm == null) {
91 lineterm = (Decimal)quote.get(String.valueOf(SBQQ__Quote__c.SBQQ__SubscriptionTerm__c));
92 if (lineTerm == null) {
93 return (Decimal)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__DefaultSubscriptionTerm__c));
94 }
95 }
96 }
97 return lineTerm;
98 }
99
100}Javascript
1export function onAfterCalculate(quote, lineModels) {
2 var maxEffectiveEndDate = null;
3 var maxEffectiveTerm = 0;
4 if (lineModels != null) {
5 lineModels.forEach(function (line) {
6 var trueEndDate = calculateEndDate(quote, line);
7 var trueTerm = getEffectiveSubscriptionTerm(quote, line);
8 if (maxEffectiveEndDate == null || (maxEffectiveEndDate < trueEndDate)) {
9 maxEffectiveEndDate = trueEndDate;
10 }
11 if (maxEffectiveTerm < trueTerm) {
12 maxEffectiveTerm = trueTerm;
13 }
14 line.record["True_Effective_End_Date__c"] = toApexDate(trueEndDate);
15 line.record["True_Effective_Term__c"] = trueTerm;
16 });
17 quote.record["True_Effective_End_Date__c"] = toApexDate(maxEffectiveEndDate);
18 quote.record["True_Effective_Term__c"] = maxEffectiveTerm;
19 }
20}
21
22function calculateEndDate(quote, line) {
23 var sd = line.record["SBQQ__EffectiveStartDate__c"];
24 var ed = line.record["SBQQ__EffectiveEndDate__c"];
25 if (sd != null && ed == null) {
26 ed = sd;
27 ed.setUTCMonth(ed.getUTCMonth() + getEffectiveSubscriptionTerm(quote, line));
28 ed.setUTCDate(d.getUTCDate() - 1);
29 }
30 return ed;
31}
32
33function getEffectiveSubscriptionTerm(quote, line) {
34 var sd = line.record["SBQQ__EffectiveStartDate__c"];
35 var ed = line.record["SBQQ__EffectiveEndDate__c"];
36 if (sd != null && ed != null) {
37 ed.setUTCDate(ed.getUTCDate() + 1);
38 return monthsBetween(sd, ed);
39 } else if (line.SubscriptionTerm__c != null) {
40 return line.SubscriptionTerm__c;
41 } else if (quote.SubscriptionTerm__c != null) {
42 return quote.SubscriptionTerm__c;
43 } else {
44 return line.DefaultSubscriptionTerm__c;
45 }
46}
47
48/**
49 * Takes a JS Date object and turns it into a string of the type 'YYYY-MM-DD', which is what Apex is expecting.
50 * @param {Date} date The date to be stringified
51 * @returns {string}
52 */
53function toApexDate(/*Date*/ date) {
54 if (date == null) {
55 return null;
56 }
57 // Get the ISO formatted date string.
58 // This will be formatted: YYYY-MM-DDTHH:mm:ss.sssZ
59 var dateIso = date.toISOString();
60
61 // Replace everything after the T with an empty string
62 return dateIso.replace(new RegExp('[Tt].*'), "");
63}
64
65function monthsBetween(/*Date*/ startDate, /*Date*/ endDate) {
66 // If the start date is actually after the end date, reverse the arguments and multiply the result by -1
67 if (startDate > endDate) {
68 return -1 * this.monthsBetween(endDate, startDate);
69 }
70 var result = 0;
71 // Add the difference in years * 12
72 result += ((endDate.getUTCFullYear() - startDate.getUTCFullYear()) * 12);
73 // Add the difference in months. Note: If startDate was later in the year than endDate, this value will be
74 // subtracted.
75 result += (endDate.getUTCMonth() - startDate.getUTCMonth());
76 return result;
77}