Troubleshooting Business Rules

Business Rules is composed of two components: a client side JavaScript and a server side workflow. You can use these queries to find out the details about this:

LinqPad – Query and result


FetchXml Builder – Query and result



One gotcha with the business rule is that its behavior will deviate, if a field that is required by the business rule is removed on the form. I will demonstrate this, with the business rule below.


This business rule sets the “Contracting Unit” to required, if Order Type is “Work Based”. Below are the screenshots of the form in two different scenarios:

With “Order Type” present on the form


Without “Order Type” present on the form

Without Order Type.png

As you can see, “Contracting Unit” is set to required, only if the “Order Type” field is present on the form, even if the value of “Order Type” is “Work based”. At present, there seems to no check in the form customisation area to prevent a field from being removed, if is required by a business rule. This is how the “Contracting Unit is required” business rule, gets translated into JavaScript.


When you debug this using Chrome Dev Tools, you can easily see why the “Contracting Unit” field is not being set to required.


To assist developers who are troubleshooting why a business rule is not working, I have developed this simple script to run in the DevTools console, that lists the fields that are required by the Business Rules, but are not present in the form. This has to be run in the context of ClientApiWrapper IFrame.

let formAttribs = Xrm.Page.getAttribute().map(a=>a.getName()); Object.keys(Mscrm.BusinessRulesScript.AttributesOnChangeHandlers).filter(x=>!formAttribs.includes(x))

Here is a sample output of this script.


It is saying that “Order Type” should be present in the form, as it is required by a Business Rule that is running on the form. It uses an unsupported internal method to identify this information, and so I recommend that it be used in devtools console only.

Further Reading:

Understanding Process Triggers and Business Rule internals

Cherry picking Xrm.Internal

EDIT (09/02/17): This post is a result of my exploration into the Xrm.Internal namespace as other internal CRM code in general. It is not a recommendation to use this in a Production environment, as it is UNSUPPORTED. Since there are already ways to run SDK messages from JavaScript using many open source frameworks, I have decided not to post part 2 (how to do this using Xrm.Internal).

Word of warning: Code heavy post and nothing shown here is supported, as it uses internal methods. All these methods have been tested only in Dynamics 365 Online.

Xrm.Internal seems to be a goldmine for cool new functionalities, that one day will eventually make into the Client API. I will document some of the some methods that are useful.

Filter Partylist entity types – Hide unwanted

Scenario: You have opened a new email form. But, you don’t want users to choose contact or lead.

Method Definition: Xrm.Internal.filterLookupTypes([ATTRIBUTE],[ENTITY ARRAY], [FILTER TYPES])


Xrm.Internal.filterLookupTypes(Xrm.Page.getAttribute(“to”), [‘contact’,’lead’], true)





Filter Partylist entity types – Show wanted

This is the reverse of the previous scenario. You now want to show only contact and lead. In this case you can use this code.

Xrm.Internal.filterLookupTypes(Xrm.Page.getAttribute(“to”), [‘contact’,’lead’], false)

The order in which the entities appear matter. You can control the resolution order of the entities with this. The first entity on the array is the default selected entity type.

With parameter [‘contact’,’lead’]contact-first

With parameter [‘lead’,’contact’]


In both these cases, only the lead and contact entities are shown.


Get entity code from schema name

Method Definition: Xrm.Internal.getEntityCode([ENTITY SCHEMA NAME])




Get entity display name from schema name

Method Definition: Xrm.Internal.getEntityDisplayName([ENTITY SCHEMA NAME])




Get entity schema name from objecttypecode

Method Definition: Xrm.Internal.getEntityName([ENTITY OBJECT TYPE CODE])




Get state code name from integer value

Method Definition: Xrm.Internal.getStateFromNumber([ENTITY SCHEMA NAME],[STATECODES ARRAY])

getStateFromNumber returns a promise, not a simple value.




Get all features enabled for the current instance

One thing that caught my eye in the feature named “ServeStaticResourcesFromAzureCDN”. I know that learning path assets can be served from Azure CDN. Could this be opened up for other webresources in the future or is this strictly learning path only? I am not sure. Could “ODataV4UI” be Swagger UI or some sort of API Playground? Intriguing.

console.table(Object.keys(Mscrm.FeatureNames).filter(x=>!x.startsWith(‘_’)).map(x=> ({Feature: x, Enabled: Xrm.Internal.isFeatureEnabled(Mscrm.FeatureNames[x])})))





Export to Excel using Dynamics 365 SDK

With the new Dynamics 365 release, a new message has been added that making exporting FetchXML results really simple. This message is not documented, hence is technically unsupported. With that word of warning, I will show you how to utilise this new message to export data from Dynamics 365 to an Excel file.

ExportToExcel message definition

Parameter Type
View EntityReference
FetchXml string
LayoutXml string
QueryApi string
QueryParameters InputArgumentCollection


using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.ServiceModel;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;

namespace Experiments
    class Program
        private static OrganizationService _orgService;
        static void Main(string[] args)
                CrmConnection connection = CrmConnection.Parse(

                using (_orgService = new OrganizationService(connection))
                    var exportToExcelRequest = new OrganizationRequest("ExportToExcel");
                    exportToExcelRequest.Parameters = new ParameterCollection();
                    //Has to be a savedquery aka "System View" or userquery aka "Saved View"
                    //The view has to exist, otherwise will error out
                    //Guid of the view has to be passed
                    exportToExcelRequest.Parameters.Add(new KeyValuePair<string, object> ("View",
                        new EntityReference("userquery", new Guid("{0B915102-24A7-E611-8101-1458D05B1178}"))));
                    exportToExcelRequest.Parameters.Add(new KeyValuePair<string, object>("FetchXml", @"
                    <fetch distinct='false' no-lock='false' mapping='logical' returntotalrecordcount='true'>
	                    <entity name='contact'>
		                    <attribute name='fullname' />
                    exportToExcelRequest.Parameters.Add(new KeyValuePair<string, object>("LayoutXml", @"
                    <grid name='resultset' object='2' jump='fullname' select='1' icon='1' preview='1'>
	                    <row name='result' id='contactid'>
		                    <cell name='fullname' width='300' />
                    //need these params to keep org service happy
                    exportToExcelRequest.Parameters.Add(new KeyValuePair<string, object>("QueryApi", ""));
                    exportToExcelRequest.Parameters.Add(new KeyValuePair<string, object>("QueryParameters",
                        new InputArgumentCollection()));
                    var exportToExcelResponse = _orgService.Execute(exportToExcelRequest);
                    if (exportToExcelResponse.Results.Any())
                        File.WriteAllBytes("Active Contacts.xlsx", exportToExcelResponse.Results["ExcelFile"] as byte[]);
            catch (FaultException<OrganizationServiceFault> ex)
                string message = ex.Message;

Closing Notes:

  1. “View” parameter can accept “userquery” or “savedquery”, but they have to exist i.e. you can’t pass empty Guid.
  2. The fetchxml and layoutxml can be different from what is in the “savedquery” or “userquery”. Hence, you can create a “Personal View” just so that you can use it in this message, but modify the fetchxml and layoutxml to whatever you want.
  3. The name of the tab in the Excel output file will be the name of the view specified in the “View” parameter
  4. This message can be executed from Javascript as well, but you will get a base64 string instead of a byte array in the response.

Please vote up my request on Connect (logged Feb 2015) -> so that this message can be made available as an unbound WebAPI action.

Understanding Process Triggers and Business Rule internals

One of the less utilised/understood feature of Business Rule is Process Triggers. In this post, I will explain what a process trigger is and how you can use this in the context of business rule.

The Basics

Business Rule is basically a workflow that has a different UI compared to the standard workflow editor. You can quickly find all the business rules in your CRM instance by running this query.

Business Rules

Internals – How does Business Rules work

You can basically skip this part, if you are not interested in understanding the internals on how a business rules work. When you create a business rules you basically have all these components that make the business rules run seamlessly:

  1. The client side code that runs on the form
  2. The server side workflow defined in xaml
  3. Process trigger -> This dictates when the business rule logic should execute

When you design a business rule, it is automatically translated into a workflow xaml that executes on the server side and JavaScript code that executes on the client side.

Now, let us take a simple example of a business rule that sets the “Salutation”, when “Gender” is changed. Here is the business rule

Business Rule Definition

When you save this business rule, this is automatically translated into JavaScript, code that can run on the client side. Below is the JavaScript code that is generated by CRM, for this business rule:

function pbl_109af564df34e51180eac4346bc576e8() {
    try {
        var v0 ='gendercode');
        var v1 ='salutation');
        if (((v0) == undefined || (v0) == null || (v0) === "") || ((v1) == undefined || (v1) == null || (v1) === "")) {
        var v2 = (v0) ? v0.getValue() : null ;
        if ((v2) === (1)) {
        } else if ((v2) === (2)) {
    } catch (e) {
        Mscrm.BusinessRules.ErrorHandlerFactory.getHandler(e, arguments.callee).handleError();

Below is the JavaScript code this calls the “pbl_109af564df34e51180eac4346bc576e8” function that contains the logic for the business rule.

Mscrm.BusinessRulesScript.Initialize = function() {
    Mscrm.BusinessRulesScript.AttributesOnChangeHandlers = {};
    Mscrm.BusinessRulesScript.ControlsOnClickHandlers = {};
    (function() {
        var onchangehandler = function() {
        Mscrm.BusinessRulesScript.AttributesOnChangeHandlers['gendercode'] = onchangehandler;
        var attributeObject ='gendercode');
        if (attributeObject != null && attributeObject != undefined) {

From the above, triggering code we can see that the business rule is going to run when the form is opened, as the function “pbl_109af564df34e51180eac4346bc576e8” is called when the business rule is initiated. The function also executes when “Gender” is changed.

If you want to know what the generated JavaScript code for the business rule is, just get the “ClientData” field in the “Workflow” entity. You cannot get this field from Advanced Find. You can either use FetchXML Builder (a XrmToolBox tool) or LINQPad. Below is the fetchxml query, I used.


If you also get the “xaml” field on the workflow entity, you can see the markup server side workflow logic that will execute.

Process Trigger

Process Trigger dictates the events that will trigger the execution of business rule. There are three events:

  1. Load
  2. Change
  3. Save

“Load” and “Change” are the standard triggers when the business rule is created through the UI. “Save” is a special handler. It can only be set using the SDK and not through the UI. It behaves little differently compared to “Load”. The JavaScript code that is generated for “Save” is little different compared to the code that is generated for “Load”. Here is the LINQ query I used for getting process triggers for this workflow.


Now let us update the “load” trigger to “save”, so that the generated JavaScript for the business rule will run only on “Form Save” event and not on “Form Load”. Here is the simple snippet I ran to do this, after I got the ids of the process trigger in the previous LINQ Query. These process triggers are for the “load” events associated to our business rule. You have to deactivate the business rule, before you update the process trigger for the business rule.

Process Trigger Update

Now comes the important bit: Activate the business rule from the Advanced Find results (first screenshot). Don’t activate the business rule from the standard business rule window. If you activate the business rule from the standard UI, your process trigger will reset back to “load”

ActivateDont Activate

Now that the trigger is set to “Save”, lets compare the generated JavaScript code.

Compare Load and Save

As we can see, in the case of “save” process trigger, the client side business rule code runs only on “Save”. I like this, because I don’t want the business rule to run on “load” and confuse the user with “Unsaved changes” message. I will demonstrate this with a scenario.

Scenario: User opens a contact record, which doesn’t have the “Salutation” field set. Gender contains a valid value. The process trigger is “load”

Form OnLoad

As you can see from the above screenshot, the business rule ran immediately on form load, and set the Salutation to “Mr”. Hence, you have a unsaved changes message, on the bottom right. I am not very happy with this result because, it is not obvious to the user what changed and what caused the change. I want more control, so I want this rule to run only after “Save”. Now look at the same form, when the trigger is “Save”

Form OnSave

As you can see, the “Salutation” field is not set immediately. It will be set only

  1. When the form is saved OR
  2. When the “Gender” field is changed

One more thing: Every time when you deactivate and reactivate a business rule, new process triggers records are created, and so you have to get the correct ids when you update the “event” attribute.

I hope you can now understand the internals of business rule and how to use process trigger to control its behaviour.


  1. TechNet: Create and edit business rules
  2. MSDN – Create or edit how business rules are initiated



Restricting the customer lookup

EDIT (07/03/2018)Updated the post for v9. In v9, you can use the supported approach of setEntityTypes and getEntityTypes. Refer

EDIT (04/07/2017): After testing this in 8.2.1, I have simplified the code as getLookupTypes and setLookupTypes can be used straight from the lookup itself, instead of going through getLookupDataAttribute.

First off, I have tested this only in CRMOnline on 8.1.0 and 8.2.1 so this might not work for you if you are are in older version. You can now easily restrict the default entity of the lookup, if it links to multiple types. e.g. customer lookup. The script below is for attribute of type “customer”, but you can follow a similar approach for “partylist” as well.

These new functions are undocumented, so they are technically unsupported (only 8.x. 9.x is documented and supported). So, use this at your own risk. In the script below, I am restricting a field with schemaname “customerid” and of type “customer” to “contact” entity only.

JavaScript (9.x) – Supported

var lookup = Xrm.Page.getControl('customerid');

//check if multiple type dropdowns enabled for this lookup
if (lookup.getEntityTypes().length > 1) {

JavaScript (8.x) – Unsupported

var lookup = Xrm.Page.getAttribute('customerid');

//check if multiple type dropdowns enabled for this lookup and it is not a partylist. For partylist we might want to select an account and a contact
if (lookup.getLookupTypes().length > 1
     && !lookup.getIsPartyList()) {


LookupLookup Search Customer

You can use this script during form load and easily restrict the lookup type.

CRM2015 Update 1 – Grid methods

CRM2015 Update 1 has finally introduced the capability to manipulate/access grids in a supported way. The API methods are documented in However not all methods are documented. These are the additional methods that you can use, but not documented in msdn. The assumption is the subgrid is displaying the contact entity records.

Object GridRow Collection
Method get
Sample Usage
Description Gets the row at the specified index in the grid
Object  GridRow Collection
Method  getByFilter
Sample Usage
return r.getData()
.getValue() === 'Max Power';
Description  Returns only the rows that have fullname attribute = Max Power
Object  GridRow Collection
Method  getFirst
Sample Usage
return r.getData()
.getValue() === 'Max Power';
Description  Returns only the 1st row that has fullname attribute = Max Power
Object  GridRow
Method  getKey
Sample Usage
Description  Return the primary key of the 1st row
Object  GridRow
Method  getId
Sample Usage
Description  Return the primary key of the 1st row
Object  Attribute
Method  getValue
Sample Usage
Description  Gets the value for the fullname attribute from the first row in the grid. If this attribute is not a grid column, this method will error as getByName will return null
Object  Grid
Method  openAssociatedGrid
Sample Usage
Description  Opens the associated view, if it is in the navigation pane

Executing QuickFind using CRM SDK

Global search in CRM for Tablets executes Quickfind view across the entities specified in the System Settings.

Internally the tablet client uses ExecuteQuickFindRequest to perform this quick find search. We can perform the same request using OrganizationRequest. Let’s look at the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Tooling.Connector;
using System.Net;

namespace ExecuteQuickFind
    class Program
        static void Main(string[] args)
            var crmConnection = new CrmConnectionHelper(new NetworkCredential("[username]","[password]","CRM"),AuthenticationType.AD, "crm1","80","Contoso");
                var executeQuickFindRequest = new OrganizationRequest("ExecuteQuickFind");
                executeQuickFindRequest.Parameters = new ParameterCollection();
                var entities = new List<string> { "contact", "lead","opportunity","systemuser","competitor","activitypointer", "incident" };
                //specify search term
                executeQuickFindRequest.Parameters.Add(new KeyValuePair<string object="">("SearchText","maria"));
                //will cause serialisation exception if we don't convert to array
                executeQuickFindRequest.Parameters.Add(new KeyValuePair<string object="">("EntityNames", entities.ToArray()));
                var executeQuickFindResponse = crmConnection.OrganizationServiceProxy.Execute(executeQuickFindRequest);
                var result = executeQuickFindResponse.Results.FirstOrDefault();
                if (executeQuickFindResponse.Results.Any())
                    var quickFindResults = result.Value as QuickFindResultCollection;

                    if (quickFindResults != null)
                        foreach (var quickFindResult in quickFindResults)
                            if (quickFindResult.ErrorCode != 0)
                                Console.WriteLine("Quickfind for {0} errored with code {1}",
                            Console.WriteLine("***Entity {0} returned {1} record(s)***", quickFindResult.Data.EntityName,
                            foreach (var entityRow in quickFindResult.Data.Entities)
                                foreach (
                                    var attribute in
                                            attribute => !entityRow.FormattedValues.Any(x => x.Key == attribute.Key)))
                                    Console.WriteLine("{0} = {1}", attribute.Key, ExtractValue(attribute.Value));
                                foreach (var formattedAttributes in entityRow.FormattedValues)
                                    Console.WriteLine("Formatted: {0} = {1}", formattedAttributes.Key,

        private static object ExtractValue(object attributeValue)
            var attributeType = attributeValue.GetType().Name;
            object returnValue = attributeValue;
            switch (attributeType)
                case "OptionSetValue":
                    returnValue = ((OptionSetValue) attributeValue).Value;
                case "EntityReference":
                    returnValue = ((EntityReference)attributeValue).Name;
                case "Money":
                    returnValue = ((Money)attributeValue).Value;
            return returnValue;

Here is the result after executing this code.

The maximum number of entities you can specify in the EntityNames parameter for this ExecuteQuickFindRequest is 10.

Changing the default view of an inline lookup in CRM 2013

Dynamics CRM 2013 has done away with the ribbon interface, with the exception of a few forms. There are also some changes in the subgrid behavior. These are

1.) When you select a record in a subgrid ribbon menu no longer appears on top of the page
2.) Clicking the “+” icon above the subgrid, may display an inline lookup

I say “may” because it depends on the relationship. An inline lookup is only displayed when the parent entity field is NOT mandatory on the child record you are trying to create. When the inline lookup is displayed on a subgrid, there is no way to access this inline lookup control using CRM Client API.

There is however, an unsupported way to set the default view on an inline lookup. I am using Ribbon Workbench, by Scott Durow, as it makes the process much easier.

First notedown the name of the subgrid. In this example, I am trying to customise the contact subgrid on account. Note this down, as it is required in the later steps.

In this case, the subgrid is called accountContactsGrid. Next we have to get the GUID of the view we want to default to. There are two ways to do this: looking into the database or using Developer Tools. Open an account record, and try to add a contact to the subgrid. When the lookup selection popup appears, use Developer Tools to get the GUID of the required view. In this case, we want the GUID of Contacts: No Campaign Activities in Last 3 Months view. Note this down, as it also required in the script we will add later.

Open the solution with the contact entity in Ribbon Workbench. In this case I am trying to change the behavior of the contact subgrid. Ribbon Workbench displays the following subgrid menu for the contact entity.

Since this is a 1:M relationship and account relationship field is not mandatory on contact record, the first Add Existing button has to be customised. Right click on the first Add Existing button and click Customise Command.

The default command behavior is as below

The Mscrm.GridRibbonActions.addExistingFromSubGridStandard function is called with the entity typecode and the grid control as parameters. This has to be changed to call our custom function in a JavaScript webresource.

Create, upload and publish a new Javascript webresource with the code below. Since the contact entitity can be a subgrid in other entities as well, we want to restrict this behavior only to account entity. We can further lock down this behavior to a specific form on the account entity, if we want, but we are not going to do this in this instance.

function () {
    var NYR = window["NYR"] || {};
    NYR.addExistingContactSubgrid = function (gridTypeCode, gridControl) {
        Mscrm.GridRibbonActions.addExistingFromSubGridStandard(gridTypeCode, gridControl);
        if ( === 'account' &&
            gridTypeCode === 2 &&
            typeof gridControl['get_viewTitle'] === 'function') {
                var inlineBehaviour = document.getElementById('lookup_accountContactsGrid_i').InlinePresenceLookupUIBehavior;
    this.NYR = NYR;

Notice that we are using the name of the subgrid and the GUID of the required view. After uploading this script as a javascript webresource, we have to change the behavior of the “Add Existing” button using Ribbon Workbench. Change it to call our NYR.addExistingContactSubgrid function. In this case my webresource name is nyr_contact_ribbon.js.

Finally, publish the changes using Ribbon Workbench. Now, when you try to add contact from the subgrid in the account entity, Contacts: No Campaign Activities in Last 3 Months will be the default view.

I have used this to control the inline lookup behavior on a 1:M relationship, but the same approach can be followed for a M:M relationship as well. Notice that I have also hardcoded the GUID of the view. You can use SOAP/OData to retrieve the viewid using the viewname, to improve this code. This is an unsupported customisation, so please use it at your own risk.