Product Search Plugin
| Available in: All Salesforce CPQ Editions |
- 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.
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
- The Constructor method can be called first, but it’s not required for implementation.
- 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
- Salesforce CPQ calls isSearchCustom to determine whether you’re using Custom or Enhanced searching.
- 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.
- If isSearchCustom returned False, Salesforce CPQ calls getAdditionalSearchFilters. This method appends a WHERE clause to the existing SOQL query.
Method Samples
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 }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 }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 }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 }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 }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 }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
- The Constructor method can be called first, but it’s not required for implementation.
- 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:
- isInputHidden: Hides the search filter from the Quote Line Editor if the Quote status is set to approved.
- getInputDefaultValue: Sets the field value for the initial search.
- Salesforce CPQ calls isSearchCustom to determine whether you’re using Custom or Enhanced searching.
- 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.
- If isSearchCustom returned False, Salesforce CPQ calls getAdditionalSearchFilters. This method appends a WHERE clause to the existing SOQL query.
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 }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 }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 }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 }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 }