Find Lookup Records

Use a quote calculator plugin to query records within the plugin and use fields from those records to set each quote line’s Description field.
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 will look for, and documents their parameters and return types.

Javascript

1/**
2 * Created by jfeingold on 9/27/16.
3 */
4export function onAfterCalculate(quote, lines, conn) {
5	if (lines.length > 0) {
6		var productCodes = [];
7		lines.forEach(function(line) {
8			if (line.record['SBQQ__ProductCode__c']) {
9				productCodes.push(line.record['SBQQ__ProductCode__c']);
10			}
11		});
12		if (productCodes.length) {
13			var codeList = "('" + productCodes.join("', '") + "')";
14			/*
15			 * conn.query() returns a Promise that resolves when the query completes.
16			 */
17			return conn.query('SELECT Id, SBQQ__Category__c, SBQQ__Value__c FROM SBQQ__LookupData__c WHERE SBQQ__Category__C IN ' + codeList)
18				.then(function(results) {
19					/*
20					 * conn.query()'s Promise resolves to an object with three attributes:
21					 * - totalSize: an integer indicating how many records were returned
22					 * - done: a boolean indicating whether the query has completed
23					 * - records: a list of all records returned
24					 */
25					if (results.totalSize) {
26						var valuesByCategory = {};
27						results.records.forEach(function(record) {
28							valuesByCategory[record.SBQQ__Category__c] = record.SBQQ__Value__c;
29						});
30						lines.forEach(function(line) {
31							if (line.record['SBQQ__ProductCode__c']) {
32								line.record['SBQQ__Description__c'] = valuesByCategory[line.record['SBQQ__ProductCode__c']] || '';
33							}
34						});
35					}
36				});
37		}
38	}
39	return Promise.resolve();
40}

Javascript - Method-Chaining

This plugin uses method-chaining style to construct the query, which is useful when you want to dynamically construct your queries.

1/**
2 * Created by jfeingold on 9/27/16.
3 */
4export function onAfterCalculate(quote, lines, conn) {
5	if (lines.length) {
6		var codes = [];
7		lines.forEach(function(line) {
8			var code = line.record['SBQQ__ProductCode__c'];
9			if (code) {
10				codes.push(code);
11			}
12		});
13		if (codes.length) {
14			var conditions = {
15				SBQQ__Category__c: {$in: codes}
16			};
17			var fields = ['Id', 'Name', 'SBQQ__Category__c', 'SBQQ__Value__c'];
18			/*
19			 * Queries can also be constructed in a method-chaining style.
20			 */
21			return conn.sobject('SBQQ__LookupData__c')
22				.find(conditions, fields)
23				.execute(function(err, records) {
24					if (err) {
25						return Promise.reject(err);
26					} else {
27						var valuesByCategory = {};
28						records.forEach(function(record) {
29							valuesByCategory[record.SBQQ__Category__c] = record.SBQQ__Value__c;
30						});
31						lines.forEach(function(line) {
32							if (line.record['SBQQ__ProductCode__c']) {
33								line.record['SBQQ__Description__c'] = valuesByCategory[line.record['SBQQ__ProductCode__c']] || '';
34							}
35						});
36					}
37				});
38		}
39	}
40	return Promise.resolve();
41}

Apex

1global class QCPForFindingLookupRecords implements SBQQ.QuoteCalculatorPlugin, SBQQ.QuoteCalculatorPlugin2 {
2 global set<String> getReferencedFields() {
3 return new Set<String> {
4 String.valueOf(SBQQ__QuoteLine__c.SBQQ__ProductCode__c),
5 String.valueOf(SBQQ__QuoteLine__c.SBQQ__Description__c)
6 };
7 }
8
9 global void onInit(SObject[] lines) {}
10
11 global void onBeforeCalculate(SObject quote, SObject[] lines) {}
12
13 global void onBeforePriceRules(SObject quote, SObject[] lines) {}
14
15 global void onAfterPriceRules(SObject quote, SObject[] lines) {}
16
17 global void onAfterCalculate(SObject quote, SObject[] lines) {
18 if (!lines.isEmpty()) {
19 String[] productCodes = new String[0];
20 for (SObject line : lines) {
21 String productCode = (String)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__ProductCode__c));
22 if (productCode != null && !productCode.isWhitespace()) {
23 productCodes.add(productCode);
24 }
25 }
26 SBQQ__LookupData__c[] ds = [SELECT Id, SBQQ__Category__c, SBQQ__Value__c FROM SBQQ__LookupData__c WHERE SBQQ__Category__c IN :productCodes];
27 if (!ds.isEmpty()) {
28 Map<String,String> valuesByCategory = new Map<String,String>();
29 for (SBQQ__LookupData__c d : ds) {
30 valuesByCategory.put(d.SBQQ__Category__c, d.SBQQ__Value__c);
31 }
32 for (SObject line : lines) {
33 String productCode = (String)line.get(String.valueOf(SBQQ__QuoteLine__c.SBQQ__ProductCode__c));
34 if (productCode != null && !productCode.isWhitespace()) {
35 line.put(String.valueOf(SBQQ__QuoteLine__c.SBQQ__Description__c), valuesByCategory.get(productCode));
36 }
37 }
38 }
39 }
40 }
41}