Previously, the default behavior for reassigning and adding an approver queries from the My Approvals page was as follows:

  • Reassigning Approver default behavior -  Selects all users
  • Add Approver default behavior - Selects all users, queues, and roles

From Summer 2018 release, as an Approvals admin, you can determine certain predefined subset of users, groups, queues or roles to be added as adhoc or additional users to the list of approvals for in-flight approval requests. Support has also been added for custom callback code to filter query for users, roles and queues for reassigning and adding an Approver on the My Approvals page.

In the Summer 2019 release, we have added the ability to reassign to queues and roles as well as users. When you select re-assignment to a Queue or a Role, a list of queues or roles is dynamically populated via a drop-down. The new default behavior available is:

  • Reassigning Approver default behavior - Selects all users, queues, or roles
  • Add Approver default behavior - Selects all users, queues, or roles

You can reassign an Approval Request to an User, Queue or Role. You can further customize the out-of-the-box behavior by overriding the Approval System Properties to filter list of users, queues, and roles from the reassign user page accessed by the Reassign link from the Approval Requests related list, and by overriding the DefaultQueryCallback class used to filter users, queues and roles from the Reassign and Add Approver icons on the My Approvals page.

If you want to change the default behavior from selecting all users, queues, and roles to only selecting a subset of them, you can create a custom apex callback class that contains custom code to query users, queues, and roles when reassigning users or adding adhoc approvers from the My Approvals page. Your code must implement the IQueryCallback and IQueryCallback2 interfaces and define the following six methods if you want to override the default behavior which is to select all available users, queues and roles.  Note that since there are individual methods to select users, queues, or roles for reassigning or adding new approvers, you can display a different set of records for reassign approvers than adding them if you wanted to. Or your code can call a common method to return the same set of records for both.

/**
	 *	Apttus Approvals Management
	 *	IQueryCallback
	 *	 
	 *	@2018-2019 Apttus Inc. All rights reserved.
	 */
	global interface IQueryCallback {
		
	    /**
	     * Callback invoked to perform a filtered query when reassigning user approvers
		 * @param request the approval request context object
		 * @param searchText the prefilter search text
	     * @return list of sobjects returned from query
	     */
	    List<sObject> doReassignUserQuery(Approval_Request__c request, String searchText);

	    /**
	     * Callback invoked to perform a filtered query when adding user approvers
		 * @param request the approval request context object
		 * @param searchText the prefilter search text
	     * @return list of sobjects returned from query
	     */
	    List<sObject> doAddUserQuery(Approval_Request__c request, String searchText);
	    
	    /**
	     * Callback invoked to perform a filtered query when adding queue approvers
		 * @param request the approval request context object
		 * @param searchText the prefilter search text
	     * @return list of sobjects returned from query
	     */
	    List<sObject> doAddQueueQuery(Approval_Request__c request, String searchText);

	    /**
	     * Callback invoked to perform a filtered query when adding role approvers
		 * @param request the approval request context object
		 * @param searchText the prefilter search text
	     * @return list of sobjects returned from query
	     */
	    List<sObject> doAddRoleQuery(Approval_Request__c request, String searchText);

	}

	/**
	 *	Apttus Approvals Management
	 *	IQueryCallback2
	 *	 
	 *	@2018-2019 Apttus Inc. All rights reserved.
	 */
	global interface IQueryCallback2 extends IQueryCallback {
		
	    /**
	     * Callback invoked to perform a filtered query when reassigning queue approvers
		 * @param request the approval request context object
		 * @param searchText the prefilter search text
	     * @return list of sobjects returned from query
	     */
	    List<sObject> doReassignQueueQuery(Approval_Request__c request, String searchText);

	    /**
	     * Callback invoked to perform a filtered query when reassigning role approvers
		 * @param request the approval request context object
		 * @param searchText the prefilter search text
	     * @return list of sobjects returned from query
	     */
	    List<sObject> doReassignRoleQuery(Approval_Request__c request, String searchText);

	}
CODE

Save the custom class name in the “Query Callback Class” custom field of the “Approvals Custom Classes” custom setting:

To override the default behavior

  • From the Approval System Properties, in the Reassign Filter Page custom setting specify the name of the visual force page that lets users select from a filtered list of users, queues, and roles.

To specify the custom reassign user search, do the following:

  1. Go to Setup > Develop > Custom Settings and click Manage for Approval System Properties.
  2. In Reassign Filter Page specify the name of the visual force page.
  3. Click Save.

To override the Default Query Callback class

  1. Go to Setup > Develop > Custom Settings and click Manage for Approvals Custom Classes.
  2. Click Edit for Custom Classes. 

    If there is no existing entry, you should create one. Click New and in Name, type Custom Classes.

  3. In Query Callback Class, type the name of your callback class. For example, using the custom class in the Use Case below, enter “GSMyApprovalsQueryCallback” as the query callback class name:



  4. Click Save.

Use Case

For example, here is an implementation of a custom callback that implements custom filters for reassigning and adding approver users, adding approver users, queues and roles. This is just a sample implementation. Your custom class can be simpler or much more complex. Each method takes two parameters: the approval request context object the user has selected to reassign or add approvers for and search text entered in the search box used to perform additional record filtering. Each of the methods in your callback class needs to return a list of Users, QueueSObjects, or UserRoles in each of the three methods.

You must define your class as global for the callback mechanism to work.

/**
 *  Apttus Approvals Management
 *  GSMyApprovalsQueryCallback
 *   
 *  @2018-2019 Apttus Inc. All rights reserved.
 */
global with sharing class GSMyApprovalsQueryCallback
    implements Apttus_Approval.CustomClass.IQueryCallback,
               Apttus_Approval.CustomClass.IQueryCallback2 {

    // limit rows
    public static final Integer LIMIT_ROWS = 1000;

    /**
     * Callback invoked to perform a filtered query when reassigning user approvers
     * @param request the approval request context object
     * @param searchText the prefilter search text
     * @return list of sobjects returned from query
     */
    public List<sObject> doReassignUserQuery(Apttus_Approval__Approval_Request__c request, String searchText) {
        try {
            String sq = '\'';
            String qryStr = 'SELECT Id, Name, Username, LastName, FirstName, Email, UserRoleId, UserRole.Name, IsActive, Profile.Name ';
            qryStr += ', (select Id, ';
            qryStr += 'Apttus_Approval__Approval_Level__c, ';
            qryStr += 'Apttus_Approval__Description__c';
            qryStr += ' from ' + 'Apttus_Approval__Approval_Matrices__r)';
            qryStr += ' FROM User ';
            qryStr += ' WHERE IsActive = true ';
            
            if (!nullOrEmpty(searchText)) {
                // build the search term
                String searchTerm = (searchText != null ? searchText : '').trim();

                // append wildcards to the search term
                searchTerm = (!searchTerm.startsWith('%') ? '%' + searchTerm : searchTerm);
                searchTerm = (!searchTerm.endsWith('%') ? searchTerm + '%' : searchTerm);
                
                qryStr += 'AND ';
                qryStr += 'Name LIKE ' + sq + String.escapeSingleQuotes(searchTerm) + sq;
            }   
            
            // EXAMPLE: filter out users with names not containing 'Borden' and system admin profiles
            qryStr += ' AND (NOT Name LIKE ' + sq + 'Borden%' + sq + ')';          
            //qryStr += ' AND Profile.Name = ' + sq + 'System Administrator' + sq;          
            qryStr += ' ORDER BY Name';
            
            // limit rows
            qryStr += ' LIMIT ' + LIMIT_ROWS;
                
            // execute the query
            return Database.query(qryStr);  
                
        } catch (Exception ex) {
            system.debug(LoggingLevel.ERROR, ex.getMessage());
            return new List<sObject>();
            
        }
        
    }

    /**
     * Callback invoked to perform a filtered query when reassigning queue approvers
     * @param request the approval request context object
     * @param searchText the prefilter search text
     * @return list of sobjects returned from query
     */
    public List<sObject> doReassignQueueQuery(Apttus_Approval__Approval_Request__c request, String searchText) {
        try {
            String sq = '\'';
            String qryStr = 'SELECT Id, QueueId, Queue.Id, Queue.Name, Queue.DeveloperName, Queue.Type, Queue.Email';
            qryStr += ' FROM QueueSobject ';
            qryStr += ' WHERE ';
            
            if (!nullOrEmpty(searchText)) {
                // build the search term
                String searchTerm = (searchText != null ? searchText : '').trim();

                // append wildcards to the search term
                searchTerm = (!searchTerm.startsWith('%') ? '%' + searchTerm : searchTerm);
                searchTerm = (!searchTerm.endsWith('%') ? searchTerm + '%' : searchTerm);
                
                qryStr += 'Queue.Name LIKE ' + sq + String.escapeSingleQuotes(searchTerm) + sq;
                qryStr += ' AND SObjectType = ' + sq + 'Apttus_Approval__Approval_Request__c' + sq; 
                qryStr += ' AND';
            }   
            
            // EXAMPLE: filter out queues with names containing 'Approvals BQA'
            qryStr += ' Queue.Name LIKE ' + sq + 'Approvals BQA%' + sq;
            qryStr += ' AND SObjectType = ' + sq + 'Apttus_Approval__Approval_Request__c' + sq; 
            qryStr += ' ORDER BY Queue.Name';
            
            // limit rows
            qryStr += ' LIMIT ' + LIMIT_ROWS;
                
            // execute the query
            return Database.query(qryStr);  
                
        } catch (Exception ex) {
            system.debug(LoggingLevel.ERROR, ex.getMessage());
            return new List<sObject>();
            
        }
        
    }

    /**
     * Callback invoked to perform a filtered query when reassigning role approvers
     * @param request the approval request context object
     * @param searchText the prefilter search text
     * @return list of sobjects returned from query
     */
    public List<sObject> doReassignRoleQuery(Apttus_Approval__Approval_Request__c request, String searchText) {
        try {
            String sq = '\'';
            String qryStr = 'SELECT Id, Name, DeveloperName, RollupDescription';
            qryStr += ' FROM UserRole ';
            qryStr += ' WHERE ';
            
            if (!nullOrEmpty(searchText)) {
                // build the search term
                String searchTerm = (searchText != null ? searchText : '').trim();

                // append wildcards to the search term
                searchTerm = (!searchTerm.startsWith('%') ? '%' + searchTerm : searchTerm);
                searchTerm = (!searchTerm.endsWith('%') ? searchTerm + '%' : searchTerm);
                
                qryStr += 'UserRole.Name LIKE ' + sq + String.escapeSingleQuotes(searchTerm) + sq;
                qryStr += ' AND';
            }   
            
            // EXAMPLE: filter out roles with names containing Sales  
            qryStr += ' UserRole.Name LIKE ' + sq + 'Sales%' + sq;       
            qryStr += ' ORDER BY UserRole.Name';
            
            // limit rows
            qryStr += ' LIMIT ' + LIMIT_ROWS;
                
            // execute the query
            return Database.query(qryStr);  
                
        } catch (Exception ex) {
            system.debug(LoggingLevel.ERROR, ex.getMessage());
            return new List<sObject>();
            
        }
        
    }

    /**
     * Callback invoked to perform a filtered query when adding user approvers
     * @param request the approval request context object
     * @param searchText the prefilter search text
     * @return list of sobjects returned from query
     */
    public List<sObject> doAddUserQuery(Apttus_Approval__Approval_Request__c request, String searchText) {
        try {
            String sq = '\'';
            String qryStr = 'SELECT Id, Name, Username, LastName, FirstName, Email, UserRoleId, UserRole.Name, IsActive, Profile.Name ';
            qryStr += ', (select Id, ';
            qryStr += 'Apttus_Approval__Approval_Level__c, ';
            qryStr += 'Apttus_Approval__Description__c';
            qryStr += ' from ' + 'Apttus_Approval__Approval_Matrices__r)';
            qryStr += ' FROM User ';
            qryStr += ' WHERE IsActive = true ';
            
            if (!nullOrEmpty(searchText)) {
                // build the search term
                String searchTerm = (searchText != null ? searchText : '').trim();

                // append wildcards to the search term
                searchTerm = (!searchTerm.startsWith('%') ? '%' + searchTerm : searchTerm);
                searchTerm = (!searchTerm.endsWith('%') ? searchTerm + '%' : searchTerm);
                
                qryStr += 'AND ';
                qryStr += 'Name LIKE ' + sq + String.escapeSingleQuotes(searchTerm) + sq;
            }   
            
            // EXAMPLE: filter out users with names not containing 'Borden' and system admin profiles
            qryStr += ' AND (NOT Name LIKE ' + sq + 'Borden%' + sq + ')';          
            //qryStr += ' AND Profile.Name = ' + sq + 'System Administrator' + sq;          
            qryStr += ' ORDER BY Name';
            
            // limit rows
            qryStr += ' LIMIT ' + LIMIT_ROWS;
                
            // execute the query
            return Database.query(qryStr);  
                
        } catch (Exception ex) {
            system.debug(LoggingLevel.ERROR, ex.getMessage());
            return new List<sObject>();
            
        }
        
    }
    
    /**
     * Callback invoked to perform a filtered query when adding queue approvers
     * @param request the approval request context object
     * @param searchText the prefilter search text
     * @return list of sobjects returned from query
     */
    public List<sObject> doAddQueueQuery(Apttus_Approval__Approval_Request__c request, String searchText) {
        try {
            String sq = '\'';
            String qryStr = 'SELECT Id, QueueId, Queue.Id, Queue.Name, Queue.DeveloperName, Queue.Type, Queue.Email';
            qryStr += ' FROM QueueSobject ';
            qryStr += ' WHERE ';
            
            if (!nullOrEmpty(searchText)) {
                // build the search term
                String searchTerm = (searchText != null ? searchText : '').trim();

                // append wildcards to the search term
                searchTerm = (!searchTerm.startsWith('%') ? '%' + searchTerm : searchTerm);
                searchTerm = (!searchTerm.endsWith('%') ? searchTerm + '%' : searchTerm);
                
                qryStr += 'Queue.Name LIKE ' + sq + String.escapeSingleQuotes(searchTerm) + sq;
                qryStr += ' AND SObjectType = ' + sq + 'Apttus_Approval__Approval_Request__c' + sq; 
                qryStr += ' AND';
            }   
            
            // EXAMPLE: filter out queues with names containing 'BO'
            qryStr += ' Queue.Name LIKE ' + sq + 'BO%' + sq;
            qryStr += ' AND SObjectType = ' + sq + 'Apttus_Approval__Approval_Request__c' + sq; 
            qryStr += ' ORDER BY Queue.Name';
            
            // limit rows
            qryStr += ' LIMIT ' + LIMIT_ROWS;
                
            // execute the query
            return Database.query(qryStr);  
                
        } catch (Exception ex) {
            system.debug(LoggingLevel.ERROR, ex.getMessage());
            return new List<sObject>();
            
        }
        
    }

    /**
     * Callback invoked to perform a filtered query when adding role approvers
     * @param request the approval request context object
     * @param searchText the prefilter search text
     * @return list of sobjects returned from query
     */
    public List<sObject> doAddRoleQuery(Apttus_Approval__Approval_Request__c request, String searchText) {
        try {
            String sq = '\'';
            String qryStr = 'SELECT Id, Name, DeveloperName, RollupDescription';
            qryStr += ' FROM UserRole ';
            qryStr += ' WHERE ';
            
            if (!nullOrEmpty(searchText)) {
                // build the search term
                String searchTerm = (searchText != null ? searchText : '').trim();

                // append wildcards to the search term
                searchTerm = (!searchTerm.startsWith('%') ? '%' + searchTerm : searchTerm);
                searchTerm = (!searchTerm.endsWith('%') ? searchTerm + '%' : searchTerm);
                
                qryStr += 'UserRole.Name LIKE ' + sq + String.escapeSingleQuotes(searchTerm) + sq;
                qryStr += ' AND';
            }   
            
            // EXAMPLE: filter out roles with names containing Role
            qryStr += ' UserRole.Name LIKE ' + sq + 'Role%' + sq;       
            qryStr += ' ORDER BY UserRole.Name';

            // limit rows
            qryStr += ' LIMIT ' + LIMIT_ROWS;
                
            // execute the query
            return Database.query(qryStr);  
                
        } catch (Exception ex) {
            system.debug(LoggingLevel.ERROR, ex.getMessage());
            return new List<sObject>();
            
        }
        
    }

    /**
     * Checks if the given string value is null or empty.
     * @param strValue the string to check
     * @return <code>true</code> if the string value is null or empty, <code>false</code> otherwise
     */
    public static Boolean nullOrEmpty(String strValue) {
        // check if null or zero length string
        return (strValue == null || strValue.trim().length() == 0);
        
    }
CODE