CPQ Product Search Plugin

Use the Product Search plugin to modify or add specificity to product filter searches and guided selling searches.
Available in: All Salesforce CPQ Editions

You can choose from two modes that control how the plugin modifies base Salesforce CPQ product searches.
  • Enhanced: Add more parameters to the WHERE clause of the product search's existing SOQL query.
  • Custom: Completely replace the product search's query logic with your own.

Salesforce CPQ applies the modified search immediately upon entering the product search screen, so the initial group of searchable products is already filtered.

You can configure the Product Search plugin to filter a product search based on certain parameters when users enter their own search queries. For example, in Product Search, you could configure the plugin to return all search results in descending order from the most recent Last Ordered Date. When the user enters the Product Search, the products returned in the search results are shown from the most recent Last Ordered Date. Users can further filter through the Product Search filter panel, if necessary. We'll return to this example in the use case.

Plugin methods vary slightly between Product Search and Guided Selling. However, the different methods for each (isFilterHidden vs. inInputHidden & getFilterDefaultValue vs. getInputDefaultValue) effectively perform the same function.

Date fields are returned as strings in the yyyy-mm-dd format.

Note

Salesforce CPQ executes the implemented methods for Product Search in the following order.
1* 1.0 Constructor()
2 * 2.0 FOREACH(Search Input Field){
3 * 		2.1 isFilterHidden()
4 * 		2.1 getFilterDefaultValue()
5 * }
6 * 3.0 isSearchCustom (CUSTOM vs ENHANCED)
7 * IF(isCustom){
8 *		4.0 search()
9 * }
10 * ELSE{
11 * 		4.0 getAdditionalSearchFilters()
12 * }

Walkthrough

  1. The Constructor method can be called first, but it’s not required for implementation.
  2. You’ll start off by entering a search field value in the FOREACH method. Salesforce CPQ calls this method for each filter value it receives, then passes those values to the following methods:
    • isFilterHidden: Hides the search filter from the Quote Line Editor if the Quote status is set to approved
    • getFilterDefaultValue: Sets the field value for the initial search
  3. Salesforce CPQ calls isSearchCustom to determine whether you’re using Custom or Enhanced searching.
  4. If isSearchCustom returned True, Salesforce CPQ calls search(). This method gives you full control of the search query - you’ll build the Select Clause and Where Clause manually, then build and perform the query.
  5. If isSearchCustom returned False, Salesforce CPQ calls getAdditionalSearchFilters. This method appends a WHERE clause to the existing SOQL query.

Method Samples

Each method below contains an implementation example. In each example, the SOQL query is from the price book entry table.Constructor
1global class ExampleProductSearchPlugin implements SBQQ.ProductSearchPlugin{
2	/**
3	 * Constructor. Not required for implementation
4	 */
5	global ExampleProductSearchPlugin(){
6		System.debug('METHOD CALLED: ExampleProductSearchPlugin Constructor');
7	}
isFilterHidden

Description

Determines the visibility of a filter in the UI. Salesforce CPQ calls this implemented method for each input.

Parameters

Param Type Description
quote SBQQ__Quote__c Current quote object
fieldName String API Name of the Product2 Field

Return Values

Returns TRUE if the field should be hidden in the UI, FALSE otherwise.

Example

1global Boolean isFilterHidden(SObject quote, String fieldName){
2		/*
3		// This would hide Product Code filter if Quote Status is Approved
4		return fieldName == 'ProductCode' && quote.SBQQ__Status__c. == 'Approved';
5		*/
6		return false;
7	}
getFilterDefaultValue

Description

Determines the input value for the initial search. Salesforce CPQ calls this implemented method for each input field.

Parameters

Param Type Description
quote SBQQ__Quote__c Current quote object
fieldName String API Name of the Product2 field

Return Values

Value to pass for this field in the initial search. NULL if none.

Example

1global String getFilterDefaultValue(SObject quote, String fieldName){
2		System.debug('METHOD CALLED: getFilterDefaultValue');
3		/*
4		// This would set Product Family filter to Service if Quote Type is Quote
5		return (fieldName == 'Family' && quote.SBQQ__Type__c. == 'Quote') ? 'Service' : NULL;
6		*/
7		return NULL;
8	}
isSearchCustom

Description

Determines if the mode is CUSTOM or ENHANCED. CUSTOM = Full control of Query; ENHANCED = Append additional criteria to WHERE clause.

Parameters

Param Type Description
quote SBQQ__Quote__c Current quote object
fieldValuesMap Map<String,Object> Map of search criteria. Key is Product2 API Name. Value is Value.

Return Values

TRUE for CUSTOM mode. FALSE for ENHANCED mode.

Example

1global Boolean isSearchCustom(SObject quote, Map<String,Object> fieldValuesMap){
2		System.debug('METHOD CALLED: isSearchCustom');
3		/*
4		// This would use CUSTOM mode if a Search field for sorting was defined and used
5		return fieldValuesMap.get('Sort_By__c') != '';
6		*/
7		return true;
8	}
getAdditionalSearchFilters() [Called when isSearchCustom = FALSE]

Description

Appends an extra WHERE clause text when using ENHANCED mode. SOQL query is from the Price Book entry table.

Parameters

Param Type Description
Quote SBQQ__Quote__c Current Quote Object
fieldValuesMap Map<String,Object> Map of search criteria. Key is Product2 API Name. Value is Value.

Return Values

String to be appended to WHERE clause. NULL if none.

Example

1global String getAdditionalSearchFilters(SObject quote, Map<String,Object> fieldValuesMap){
2		System.debug('METHOD CALLED: getAdditionalSearchFilters');
3		/*
4		// This would add an extra inventory filter if the family is Hardware
5		String additionalFilter = NULL;
6
7		if(fieldValuesMap.get('Family') == 'Hardware'){
8			additionalFilter = 'AND Product2.Inventory_Level__c > 3';
9		}
10
11		return additionalFilter;
12		*/
13		return NULL;
14	}
search() [Called when isSearchCustom = TRUE]

Definition

Override the entire search when using CUSTOM mode. Product2 fields in the Search Results field set should be set.

Parameters

Param Type Description
quote SBQQ__Quote__c Current Quote Object
fieldValuesMap Map<String,Object> Map of search criteria. Key is Product2 API Name. Value is Value. Only contains Keys for non-NULL values

Return Values

List of Price Book Entries with Product2 external lookup field set

Example

1global List<Price BookEntry> search(SObject quote, Map<String,Object> fieldValuesMap){
2		System.debug('METHOD CALLED: search');
3		//GET ALL POSSIBLE FILTER FIELDS FROM THE SEARCH FILTER FIELD SET
4		List<Schema.FieldSetMember> searchFilterFieldSetFields = SObjectType.Product2.FieldSets.SBQQ__SearchFilters.getFields();
5
6		//GET ALL POSSIBLE FIELDS FROM THE SEARCH RESULTS FIELD SET
7		List<Schema.FieldSetMember> searchResultFieldSetFields = SObjectType.Product2.FieldSets.SBQQ__SearchResults.getFields();
8
9		//BUILD THE SELECT STRING
10		String selectClause = 'SELECT ';
11
12		for(Schema.FieldSetMember field : searchResultFieldSetFields){
13			selectClause += 'Product2.' + field.getFieldPath() + ', ';
14		}
15        selectClause += 'Id, UnitPrice, Price Book2Id, Product2Id, Product2.Id';
16
17        //BUILD THE WHERE CLAUSE
18        String whereClause = '';
19
20		for(Schema.FieldSetMember field : searchFilterFieldSetFields){
21			if(!fieldValuesMap.containsKey(field.getFieldPath())){
22				continue;
23			}
24
25			if(field.getType() == Schema.DisplayType.String || field.getType() == Schema.DisplayType.Picklist){
26				whereClause += 'Product2.' + field.getFieldPath() + ' LIKE \'%' + fieldValuesMap.get(field.getFieldPath()) + '%\' AND ';
27			}
28		}
29
30		whereClause += 'Price Book2Id = \'' + quote.get('SBQQ__Price Book__c') + '\'';
31
32		//BUILD THE QUERY
33		String query = selectClause + ' FROM Price BookEntry WHERE ' + whereClause;
34
35		//DO THE QUERY
36		List<Price BookEntry> pbes = new List<Price BookEntry>();
37		pbes = Database.query(query);
38
39		return pbes;
40	}
GUIDED SELLING

Salesforce CPQ executes the implemented methods for Guided Selling in the following order:

1* 1.0 Constructor()
2 * 2.0 FOREACH(Search Input Field){
3 * 		2.1 isInputHidden()
4 * 		2.1 getInputDefaultValue()
5 * }
6 * 3.0 isSearchCustom (CUSTOM vs ENHANCED)
7 * IF(isCustom){
8 *		4.0 search()
9 * }
10 * ELSE{
11 * 		4.0 getAdditionalSearchFilters()
12 * }

Walkthrough

  1. The Constructor method can be called first, but it’s not required for implementation.
  2. You’ll start off by entering a search field value in the FOREACH method. Salesforce CPQ calls this method for each filter value it receives, then passes those values to the following methods:
  3. isInputHidden: Hides the search filter from the Quote Line Editor if the Quote status is set to approved.
  4. getInputDefaultValue: Sets the field value for the initial search.
  5. Salesforce CPQ calls isSearchCustom to determine whether you’re using Custom or Enhanced searching.
  6. If isSearchCustom returned True, Salesforce CPQ calls search(). This method gives you full control of the search query - you’ll build the Select Clause and Where Clause manually, then build and perform the query.
  7. If isSearchCustom returned False, Salesforce CPQ calls getAdditionalSearchFilters. This method appends a WHERE clause to the existing SOQL query.
isInputHidden

Description

Determines the visibility of an Input in the UI when using Guided Selling. Salesforce CPQ calls this implemented method for each input.

Parameters

Param Type Description
quote SBQQ__Quote__c Current quote object
input String Name of the Quote Process Input

Return Values

Returns TRUE if the field should be hidden in the UI, FALSE otherwise

Example

1global Boolean isInputHidden(SObject quote, String input){
2		System.debug('METHOD CALLED: isInputHidden');
3		/*
4		// This would hide an Input called 'Urgent Shipment' on Fridays.
5		return input == 'Urgent Shipment' && Datetime.now().format('F') == 5;
6		*/
7		return false;
8	}
getInputDefaultValue

Description

Determines the input value for the initial search. Salesforce CPQ calls this implemented method for each Quote Process Input.

Parameters

Param Type Description
quote SBQQ__Quote__c Current quote object
input String Name of the Quote Process Input

Return Values

Returns a value to pass for this field in the initial search. NULL if none.

Example

1global String getInputDefaultValue(SObject quote, String input){
2		System.debug('METHOD CALLED: getInputDefaultValue');
3
4		return NULL;
5	}
isSearchCustom()

Description

Determines if the mode is CUSTOM or ENHANCED when using Guided Selling. CUSTOM = Full control of Query; ENHANCED = Append additional criteria to WHERE clause.

Parameters

Param Type Description
quote SBQQ__Quote__c Current quote object
inputValuesMap Map<String,Object> Map of search criteria. Key is Process Input Name. Value is Value. Only contains keys for non-Null values. Includes additional key "qpid", for the Quote Process ID

Return Values

TRUE for CUSTOM mode. FALSE for ENHANCED mode.

1/**
2	global Boolean isSuggestCustom(SObject quote, Map<String,Object> inputValuesMap){
3		return true;
4	}
getAdditionalSearchFilters() [Called when isSearchCustom = FALSE]

Description

Appends an extra WHERE clause text when using Guided Selling in ENHANCED mode. SOQL query is from the Price Book entry table.

Parameters

Param Type Description
Quote SBQQ__Quote__c Current Quote Object
fieldValuesMap Map<String,Object> Map of search criteria. Key is Product2 API Name. Value is Value

Return Values

String to be appended to WHERE clause. NULL if none.

Example

1global String getAdditionalSearchFilters(SObject quote, Map<String,Object> fieldValuesMap){
2		System.debug('METHOD CALLED: getAdditionalSearchFilters');
3		/*
4		// This would add an extra inventory filter if the family is Hardware
5		String additionalFilter = NULL;
6
7		if(fieldValuesMap.get('Family') == 'Hardware'){
8			additionalFilter = 'Product2.Inventory_Level__c > 3';
9		}
10
11		return additionalFilter;
12		*/
13		return NULL;
14	}
search() [Called when isSearchCustom = TRUE]

Definition

Override the entire search when using in CUSTOM mode. Product2 Fields in the Search Results field set should be set.

Parameters

Param Type Description
quote SBQQ__Quote__c Current Quote Object
fieldValuesMap Map<String,Object> Map of search criteria. Key is Product2 API Name. Value is Value. Only contains Keys for non-NULL values

Return Values

List of Price Book Entries with Product2 external lookup field set

Example

1global List<Price BookEntry> search(SObject quote, Map<String,Object> fieldValuesMap){
2		System.debug('METHOD CALLED: search');
3		//GET ALL POSSIBLE FILTER FIELDS FROM THE SEARCH FILTER FIELD SET
4		List<Schema.FieldSetMember> searchFilterFieldSetFields = SObjectType.Product2.FieldSets.SBQQ__SearchFilters.getFields();
5
6		//GET ALL POSSIBLE FIELDS FROM THE SEARCH RESULTS FIELD SET
7		List<Schema.FieldSetMember> searchResultFieldSetFields = SObjectType.Product2.FieldSets.SBQQ__SearchResults.getFields();
8
9		//BUILD THE SELECT STRING
10		String selectClause = 'SELECT ';
11
12		for(Schema.FieldSetMember field : searchResultFieldSetFields){
13			selectClause += 'Product2.' + field.getFieldPath() + ', ';
14		}
15        selectClause += 'Id, UnitPrice, Price Book2Id, Product2Id, Product2.Id';
16
17        //BUILD THE WHERE CLAUSE
18        String whereClause = '';
19
20		for(Schema.FieldSetMember field : searchFilterFieldSetFields){
21			if(!fieldValuesMap.containsKey(field.getFieldPath())){
22				continue;
23			}
24
25			if(field.getType() == Schema.DisplayType.String || field.getType() == Schema.DisplayType.Picklist){
26				whereClause += 'Product2.' + field.getFieldPath() + ' LIKE \'%' + fieldValuesMap.get(field.getFieldPath()) + '%\' AND ';
27			}
28		}
29
30		whereClause += 'Price Book2Id = \'' + quote.get('SBQQ__Price Book__c') + '\'';
31
32		//BUILD THE QUERY
33		String query = selectClause + ' FROM Price BookEntry WHERE ' + whereClause;
34
35		//DO THE QUERY
36		List<Price BookEntry> pbes = new List<Price BookEntry>();
37		pbes = Database.query(query);
38
39		return pbes;
40	}