How to Filter Lookups in a Dynamics 365 Business Process Flow

Sean Roque, 03 July 2019

When designing a business process flow in Dynamics 365, we might have a data step in one stage, which is a lookup to another step in the next stage. In cases like this we ideally want to filter the search options to only show related records.

This is not available out of the box but it can be done using JavaScript as an unsupported customization.

For example, a BPF for Case, where after selecting an Account lookup, we only want to filter the Contact lookup according to those records related to the selected Account.

image

To do this, we need to first check if the Control for that particular field exists, then add a filter to its Pre-Search event using JavaScript.

function filterBpfLookup = function(formContext){
	if (formContext.getControl("header_process_mag_contactlookupid") != null){
		formContext.getControl("header_process_mag_contactlookupid").addPreSearch(filterDefinition);
	}
}

Where ‘mag_contactlookupid’ is the name for the lookup between Case and Contact.

Now we have to define the filter called filterDefinition by first retrieving the value entered in the Account Control:

function filterDefinition = function(formContext) {
     var accountId = null;
     var accountLookup;
     var filterBpf;
     
     if (formContext.getControl("header_process_mag_contactlookupid") != null &&
     formContext.getControl("header_process_mag_contactlookupid").getAttribute().getValue() !=  null){
         accountLookup = formContext.getControl("header_process_mag_accountlookupid").getAttribute().getValue();
         accountId = accountLookup[0].id;
         
         if (accountId != null || accountId != undefined){
             filterBpf = "<filter type='and'>" + 
             "<condition attribute='mag_accountlookupid' operator='eq' value='" + accountId + "' />" +
             "</filter>";
             
             formContext.getControl("header_process_mag_contactlookupid").addCustomFilter(filterBpf);
         }
     }
 }

Then creating a query only showing Contact records related to the chosen Account and using addCustomFilter to add it to the respective control.

function filterDefinition = function(formContext) {
     var accountId = null;
     var accountLookup;
     var filterBpf;
     
     if (formContext.getControl("header_process_mag_contactlookupid") != null &&
     formContext.getControl("header_process_mag_contactlookupid").getAttribute().getValue() !=  null){
         accountLookup = formContext.getControl("header_process_mag_accountlookupid").getAttribute().getValue();
         accountId = accountLookup[0].id;
         
         if (accountId != null || accountId != undefined){
             filterBpf = "<filter type='and'>" + 
             "<condition attribute='mag_accountlookupid' operator='eq' value='" + accountId + "' />" +
             "</filter>";
             
             formContext.getControl("header_process_mag_contactlookupid").addCustomFilter(filterBpf);
         }
     }
 }

The problem with this is if the business process flow branches, it uses a different control. This means that while you can reference the first control as “header_process_mag_contactlookupid”, the contactlookup control in the next BPF branch will have a different name i.e. “header_process_mag_contactlookupid_1”.

The best approach is to loop through every control for that particular field and apply the filter, using controls.forEach and addPresearch

function filterBpfLookup = function(formContext) {
     var contactLookupId = formContext.getAttribute("mag_contactlookupid");
     
     if (contactLookupId != null){
         contactLookupId.controls.forEach(function(a, i)){
             a.addPreSearch(
                 function(){
                     filterDefinition(formContext);
                 }
             )
         }
     }
 }

And the same for the filterDefinition function:

function filterDefinition = function(formContext){
     var accountId = null;
     var accountLookup;
     var filterBpf;
     
     if (formContext.getControl("header_process_mag_contactlookupid") != null &&
     formContext.getControl("header_process_mag_contactlookupid").getAttribute().getValue() !=  null){
         accountLookup = formContext.getControl("header_process_mag_accountlookupid").getAttribute().getValue();
         accountId = accountLookup[0].id;
         
         if (accountId != null || accountId != undefined){
             filterBpf = "<filter type='and'>" + 
             "<condition attribute='mag_accountlookupid' operator='eq' value='" + accountId + "' />" +
             "</filter>";
             
             if(formContext.getControl("mag_contactlookupid") != null){
                 formContext.getControl("mag_contactlookupid").controls.forEach(function (a,i)){
                     a.addCustomFilter(filterBpf);
                 }
             }
         }
     }
 }