Tag Archives: REST

Working With REST Results in CRM 2011

A short post today.   When using Microsoft CRM 2011’s REST oData End Point to create a new record or to retrieve an existing record the result is provided to you in 2 different ways.  This can catch you out.

Retrieving Records

When querying for existing records the result provided by the oData query is an array of CRM records.  You can access that by referring to data.d.results[0] as illustrated below:

success: function (data, textStatus, XmlHttpRequest) {
	var Contact = data.d.results[0];
	if (Contact.Telephone1 != null) {
		Xrm.Page.data.entity.attributes.get("new_phonenumber2").setValue(Contact.Telephone1);
		alert("phone set");
	}

I’m placing the first CRM object in that array into a variable called Contact.  In the above example my query (which I haven’t included) was performing a top1 selection so I am only expecting one record and therefore only interested in the first record (array value 0). 

I then retrieve the Contact’s phone number by referring to Contact.Telephone1 where Contact is my object variable and Telephone1 is one of it’s attributes.  To know the attribute name and the correct case to use make sure you test your REST query in IE or using the oData Query Designer.

Creating Records

When creating a new record you will be returned the object that was created.  This will be just a single CRM object rather than an array of objects.  You can access that by referring to data.[“d”] as illustrated below:

success: function (data, textStatus, XmlHttpRequest) {
	var NewCRMRecordCreated = data["d"];
	alert(NewCRMRecordCreated.IncidentId);

Hope this helps.  Smile

Advertisements

Quick Create Contact from the Phone Call Form

Here’s a solution I’ve mocked up to speed up call handling times in a particular Call Centre scenario. 

Here we have Microsoft Dynamics CRM 2011 deployed into a Call Centre where our CSR’s are tasked with processing inbound calls.   The CRM system has been integrated to the telephony system such that when the caller can be matched to a CRM Contact record that Contact record pops for the user.  

But when the caller can’t be matched we want the CRM Phone Call form to pop for the user instead and we want to give the user a minimal-click solution that allows them to both record the Phone Call and the Contact.   Here’s what I came up with:

image

The telephony integration takes care of popping this form and provides us with the phone number of the caller.  It also populates the subject field for us as well.

If the CSR’s conversation with the caller indicates they should be in CRM (suggesting that the number they have called in from is new/different) then the CSR can use the out-of-the-box Sender field to search for the caller.

If the caller is new to us though the user can simply complete these steps to quickly create the Contact and link the Phone Call to the new Contact record:

1. The CSR clicks into the Title field in the Quick Create New Contact section of the form and populates:

image

Note: As soon as the CSR populates one of the fields in this section all the fields in the section become mandatory.

2. The CSR tabs out of the first field and then populates the second, and then the third:

image

3. The user tabs out of the Last Name field and custom code (jscript) is triggered. 

The code detects that all 3 mandatory fields have now been populated.  The jscript collects the field values from the form and then executes a REST call to create a new Contact based on those values.  On success, the code then updates the Sender field to reflect the Contact that was created and then disables the Quick Create fields:

image

For the user, the back end processing is invisible.  They simply followed an intuitive process. 

A little bit of jscript used in an supported (upgrade friendly) manner has allowed us to streamline the process by removing 6 clicks and one form load.   In a Call Centre environment this is gold.  

Here’s the jscript behind this.  The OnLoad function needs to be attached to the form’s OnLoad event.  The NewContact function needs to be attached to the OnChange event of the Title, First Name and Last Name fields.  The CreateContact function does the heavy lifting.

// Set lookup value of a field 
function SetLookupValue(fieldName, id, name, entityType) { 
    if (fieldName != null) { 
        var lookupValue = new Array(); 
        lookupValue[0] = new Object(); 
        lookupValue[0].id = id; 
        lookupValue[0].name = name; 
        lookupValue[0].entityType = entityType; 
        Xrm.Page.getAttribute(fieldName).setValue(lookupValue); 
    } 
} 

function DisableFields() { 
    Xrm.Page.ui.controls.get("new_title").setDisabled(true); 
    Xrm.Page.ui.controls.get("new_firstname").setDisabled(true); 
    Xrm.Page.ui.controls.get("new_lastname").setDisabled(true); 
    Xrm.Page.ui.controls.get("phonenumber").setDisabled(true); 
} 

function MakeFieldsMandatory() { 
    Xrm.Page.data.entity.attributes.get("new_title").setRequiredLevel("required"); 
    Xrm.Page.data.entity.attributes.get("new_firstname").setRequiredLevel("required"); 
    Xrm.Page.data.entity.attributes.get("new_lastname").setRequiredLevel("required"); 
    Xrm.Page.data.entity.attributes.get("phonenumber").setRequiredLevel("required"); 
} 

function MakeFieldsNonMandatory() { 
    Xrm.Page.data.entity.attributes.get("new_title").setRequiredLevel("none"); 
    Xrm.Page.data.entity.attributes.get("new_firstname").setRequiredLevel("none"); 
    Xrm.Page.data.entity.attributes.get("new_lastname").setRequiredLevel("none"); 
    Xrm.Page.data.entity.attributes.get("phonenumber").setRequiredLevel("none"); 
} 

function OnLoad() { 
    if (Xrm.Page.ui.getFormType() == 1) { 
        Xrm.Page.getAttribute("subject").setValue("Inbound phone call"); 
        Xrm.Page.getAttribute("phonenumber").setValue("+65 9784 5862"); 
    } 
    else if (Xrm.Page.ui.getFormType() != 1) { 
        DisableFields(); 
    } 
} 

function NewContact() { 
    if (Xrm.Page.getAttribute("new_title").getValue() == null && 
            Xrm.Page.getAttribute("new_firstname").getValue() == null && 
            Xrm.Page.getAttribute("new_lastname").getValue() == null && 
            Xrm.Page.getAttribute("phonenumber").getValue() == null) { 
        MakeFieldsNonMandatory(); 
    } 
    else if (Xrm.Page.getAttribute("new_title").getValue() != null && 
                Xrm.Page.getAttribute("new_firstname").getValue() != null && 
                Xrm.Page.getAttribute("new_lastname").getValue() != null && 
                Xrm.Page.getAttribute("phonenumber").getValue() != null && 
                Xrm.Page.data.entity.attributes.get("from").getValue() == null) { 
        CreateContact(); 
        DisableFields(); 
    } 
    else { 
        MakeFieldsMandatory(); 
    } 
} 

function CreateContact() { 
    // Get the CRM URL 
    var serverUrl = Xrm.Page.context.getServerUrl(); 

    // Cater for URL differences between on premise and online 
    if (serverUrl.match(/\/$/)) { 
        serverUrl = serverUrl.substring(0, serverUrl.length - 1); 
    } 

    // Specify the ODATA end point (this is the same for all CRM 2011 implementations) 
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc"; 

    // Specify the ODATA entity collection 
    var ODATA_EntityCollection = "/ContactSet"; 

    // Define an object for the CRM record you want created 
    var CRMObject = new Object(); 

    // Define attribute values for the CRM object 
    CRMObject.FirstName = Xrm.Page.getAttribute("new_firstname").getValue(); 
    CRMObject.LastName = Xrm.Page.getAttribute("new_lastname").getValue(); 
    CRMObject.Salutation = Xrm.Page.getAttribute("new_title").getValue(); 
    CRMObject.Telephone1 = Xrm.Page.getAttribute("phonenumber").getValue(); 

    //Parse the entity object into JSON 
    var jsonEntity = window.JSON.stringify(CRMObject); 

    //Asynchronous AJAX function to Create a CRM record using OData 
    $.ajax({ 
        type: "POST", 
        contentType: "application/json; charset=utf-8", 
        datatype: "json", 
        url: serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection, 
        data: jsonEntity, 
        beforeSend: function (XMLHttpRequest) { 
            //Specifying this header ensures that the results will be returned as JSON.      
            XMLHttpRequest.setRequestHeader("Accept", "application/json"); 
        }, 
        success: function (data, textStatus, XmlHttpRequest) { 
            //This function will trigger asynchronously if the Retrieve was successful 
            //alert("ajax call successful"); 
            var NewCRMRecordCreated = data["d"]; 
            var FullName = Xrm.Page.getAttribute("new_firstname").getValue() + " " + Xrm.Page.getAttribute("new_lastname").getValue(); 
            SetLookupValue("from", NewCRMRecordCreated.ContactId, FullName, "contact"); 
        }, 
        error: function (XmlHttpRequest, textStatus, errorThrown) { 
            //This function will trigger asynchronously if the Retrieve returned an error 
            alert("ajax call failed"); 
        } 
    }); 
}

You will need to upload the json and jquery libraries as web resources and reference in the On Load event along with the OnLoad function:

image

A CRM solution file containing these web resources and the changes to the Phone Call form are available for download here.

Smile

Defaulting a Lookup field via a REST query at Form Load

Here’s some sample code that demonstrates how to dynamically determine the GUID of a related record and use that to default a lookup field on a CRM form on form load.

In this example I have a dummy Contact record called “new contact” which I want to default onto the Phone Call form each time a new Phone Call record is created. 

i.e when I click New Phone Call on the ribbon…

image

…. I want to see this:

image

note: I have also re-configured the Phone Call entity’s Direction field so that “Incoming” is the default.  The idea being that the user is launching the Phone Call form to record an incoming call from an unknown caller.

Defaulting in a dummy Contact may some an unusual scenario but it is not uncommon to utilise a dummy record when dealing with anonymous callers or when wanting to defer the collection of contact details until later in the process.   In a Call Centre environment for example you will not achieve desired efficiencies if you except having to manually create new Contact records before being able to create Phone Calls, Cases, Opportunities, etc or before being able to convert Phone Calls to Cases or Opportunities (I expand on this Call Centre scenario in my next post).

Anyway, here’s how to make this work…

There are 2 parts to this.  Firstly, we need to find out the GUID of the Contact record that we want to default on to our Phone Call.   You could figure this out manually and then hardcode the GUID but then your code will not be transportable across environments.   Instead in this example I have hardcoded only the name of the contact (we can easily create this Contact in each environment, it’s just data).  My dummy Contact is called “new contact” and I use the REST oData endpoint to locate that record’s GUID by searching on that name (An alternative approach would be to  store the GUID in a configuration entity and then use REST to query for the GUID from there, however the same coding approach applies).   And  then secondly, once we know the GUID it’s a relatively simple matter of doing a SetValue to populate the field on the form.

Here’s the jscript:

// Set lookup value of a field 
function SetLookupValue(fieldName, id, name, entityType) { 
    if (fieldName != null) { 
        var lookupValue = new Array(); 
        lookupValue[0] = new Object(); 
        lookupValue[0].id = id; 
        lookupValue[0].name = name; 
        lookupValue[0].entityType = entityType; 
        Xrm.Page.getAttribute(fieldName).setValue(lookupValue); 
    } 
} 

function GetDefaultContactGUID() { 
    if (Xrm.Page.ui.getFormType() == 1) { 
        // Get the CRM URL 
        var serverUrl = Xrm.Page.context.getServerUrl(); 

        // Cater for URL differences between on premise and online 
        if (serverUrl.match(/\/$/)) { 
            serverUrl = serverUrl.substring(0, serverUrl.length - 1); 
        } 

        // Specify the ODATA end point (this is the same for all CRM 2011 implementations) 
        var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc"; 

        // Specify the ODATA entity collection (this needs to be specific to your entity) 
        var ODATA_EntityCollection = "/ContactSet"; 

        // Specify the ODATA filter 
        var ODATA_Query = "?$select=ContactId&$filter=FullName%20eq%20\'new%20contact\'&$top=1"; 

        // Build the URL 
        var ODATA_Final_url = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + ODATA_Query; 

        //Calls the REST endpoint 
        $.ajax({ 
            type: "GET", 
            contentType: "application/json; charset=utf-8", 
            datatype: "json", 
            url: ODATA_Final_url, 
            beforeSend: function (XMLHttpRequest) { 
                //Specifying this header ensures that the results will be returned as JSON.      
                XMLHttpRequest.setRequestHeader("Accept", "application/json"); 
            }, 
            success: function (data, textStatus, XmlHttpRequest) { 
                //This function will trigger asynchronously if the Retrieve was successful 
                SetLookupValue("from", data.d.results[0].ContactId, "new contact", "contact"); 
            }, 
            error: function (XmlHttpRequest, textStatus, errorThrown) { 
                //This function will trigger asynchronously if the Retrieve returned an error 
                alert("ajax call failed"); 
            } 
        }); 
    } 
}

The code snippet above contains 2 functions.  The “SetLookupValue” function is used to default the lookup field.  It is called by the 2nd function “GetDefaultContactGUID” which is where the REST query is constructed.

To deploy the above you need to load it into a jscript web resource and then reference that web resource on the Phone Call form and add a call in the Phone Call’s On Load event to the GetDefaultContactGUID function.   You will also need to upload json and jquery supporting  web resources and make sure they are specified in the order shown below:

image 

If you’re not sure what I’m talking about when I mention json and jquery web resources have a read of this earlier post.

That’s it, the code is well commented so have a read of that to understand the syntax. 

A solution file containing the above web resources and minor change to the Phone Call entity is available here for download.   Hope this helps someone.

G.

Filtered Lookup Approaches in CRM 2011

In this post I look at 3 different scenarios where lookup field filtering is required to illustrate the different approaches available in CRM 2011.  The out of the box options are demonstrated as well as more complex Jscript based approaches that can be adopted when the limitations of the standard functionality are hit…


Scenario 1:  You have 2 lookup fields on a form and one of those lookups should be filtered based on the other.

Example:  On the Contact form you add 2 new lookup fields, one for “Country” and one for “State”.   They map to 2 new entities: Country and State, where the Country entity has a 1:M relationship to State.  On the Contact form the user selects Country first and then when they select State the lookup should be filtered to only show the States that relate to the chosen Country.

Screen layout:

image

State lookup when Country = “Australia”:

image

State lookup when Country = “USA”:

image

Solution: Ok, this one is easy.  CRM 2011’s new Filtered Lookups functionality gives us this out of the box no code required.   The Filtered Lookup definition is set in the Properties of the State lookup on the CRM Form.  Here’s what you need to set:

image

The terminology employed here doesn’t read right to me, the fields feel like they’re placed upside down.   What we want is “Only show (State) records where the Country value of the State contains the Country value specified on this Contact record”. 

My suggestion is swap the fields in your head as you read the screen or don’t read the screen – instead just think of the first picklist as defining the input parameter and the second pick list as defining the field you want to filter on.

You can follow his approach works in those scenarios where your input parameter is a value on the form.   When your input parameter is on a related entity or needs to be hardcoded you need to go with another approach…


Scenario 2:  The same entity is represented twice on one form as 2 lookup fields.  Each lookup field should offer a different subset of records for the user to pick from.

Example:  The Account entity is used in CRM to store both Customers and Suppliers with the Relationship Type picklist on the Account form used to distinguish them.  The Contact form has 2 additional lookup fields to the Account entity, one labelled Customer, the other labelled Supplier.  The Customer lookup should only show Accounts of type “Customer” and the Supplier lookup should only show Accounts of type “Supplier”.

Screen layout:

image

Account Lookup for Customer field:

image

Account Lookup for Supplier field:

image

Solution:  There are 2 approaches to this, 1 simple, 1 complex.

The simple solution is to define 2 system views on the Account entity, 1 that only shows Customers and 1 that only shows Suppliers.  Then on the Contact form you assign the relevant view to each lookup.  Easy.

Create 2 system views:

image

Definition of the Customer lookup:

image

Definition of the Supplier lookup:

image

So this approach works fine in most simple scenarios.  The downside is that the 2 system views created to support this are visible throughout CRM.  In this scenario that’s probably not a big deal, the 2 views may be useful to end users, but once the volume of views increases things start to get messy. 

The workaround to this is to dynamically define new lookup views in jscript on form load and instruct the lookup fields to use these new views instead.  Here’s some sample jscript code that demonstrates how to do this.  Place this in a web resource and call the 2 functions provided from the Contact form’s OnLoad event:

function ChangeLookupView_Customer() { 
    //Note: in the form designer make sure the lookup field is set to "Show all views" in its "View Selector" property  

    //Set parameters values needed for the creation of a new lookup view... 
    var viewId = Xrm.Page.data.entity.getId();      // Your new lookup views needs a unique id.  It must be a GUID.  Here I use the GUID of the current record. 
    var entityName = "account";                     // The entity your new lookup view relates to 
    var viewDisplayName = "Account - Customers";    // A name for new lookup view 
    var viewIsDefault = true;                       // Whether your new lookup view should be the default view displayed in the lookup or not 

    //Define the Fetch XML query your new lookup view will use.  You can create this via Advanced Find.  You'll need to massage the syntax a little though 
    var fetchXml =  
              "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" + 
              "<entity name='account'>" + 
              "<attribute name='name' />" + 
              "<attribute name='accountid' />" + 
              "<attribute name='customertypecode' />" + 
              "<filter type='and'>" + 
              "<condition attribute='customertypecode' operator='eq' value='3' />" + 
              "</filter>" + 
              "<order attribute='name' decending='false' />" + 
              "</entity>" + 
              "</fetch>"; 

    //Define the appearance of your new lookup view 
    var layoutXml =  
                    "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" +   
                    "<row name='result' id='accountid'>" +  // id = the GUID field from your view that the lookup should return to the CRM form 
                    "<cell name='name' width='200' />" +   
                    "<cell name='customertypecode' width='200' />" + 
                    "</row>" + 
                    "</grid>"; 
 
    //Add your new view to the Lookup's set of availabe views and make it the default view 
    Xrm.Page.getControl("new_customer").addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, viewIsDefault); 
} 


function ChangeLookupView_Supplier() { 

    //Set parameters values needed for the creation of a new lookup view... 
    var viewId = Xrm.Page.data.entity.getId();  // Using the same GUID, the viewId only has to be unique within each lookup's set of views 
    var entityName = "account"; 
    var viewDisplayName = "Account - Suppliers"; 
    var viewIsDefault = true; 

    //Define the Fetch XML query your new lookup view will use   
    var fetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" + 
          "<entity name='account'>" + 
          "<attribute name='name' />" + 
          "<attribute name='accountid' />" + 
          "<attribute name='customertypecode' />" + 
          "<filter type='and'>" + 
          "<condition attribute='customertypecode' operator='eq' value='10' />" +   // A different value provided here to produce a different result set 
          "</filter>" + 
          "<order attribute='name' decending='false' />" + 
          "</entity>" + 
          "</fetch>"; 

    //Define the appearance of your new lookup view 
    //No changes required here compared to the above, the lookups should have the same appearance 
    var layoutXml = "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" +   
             "<row name='result' id='accountid'>" + 
              "<cell name='name' width='200' />" + 
              "<cell name='customertypecode' width='200' />" + 
             "</row>" + 
            "</grid>"; 

    //Add your new view to the Lookup's set of availabe views and make it the default view 
    //The supplier field is specified here, we want to add this view to that lookup field 
    Xrm.Page.getControl("new_supplier").addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, viewIsDefault); 
}

 

Rather than talk you through the code in this post I’ve put a lot of comments into the code so have a read through that.   2 tricks that I stumbled on are:

1) The view ID that you assign to your new view must be a GUID value.

2) In the form designer make sure your lookup field has "Show all views" set in its "View Selector" property.


Scenario 3:  A lookup field needs to be filtered based on a lookup field that resides on a related record.

Example:  The Account form has a Country field, the Contact form a State field.  When selecting the State on the Contact only those States related to the Country specified on the Contact’s parent Account should be offered to the user.

Screen layout – Account screen:

image

Screen layout – Contact screen:

image

State lookup on Contact form when parent Account’s Country = “USA”:

image

Solution:

This is the trickiest scenario to address because the filtering needs to be based on a piece of data that is not available to us on the form.  We can get to that data though and my preferred approach at the moment for this is to make an ODATA query to CRM’s REST end point.   So, our approach is as follows:

– Read the Parent Account GUID value from the Contact form

– Execute an ODATA query along the lines of: “Select the Country field from the Account entity where the Account’s GUID = this Contact’s Parent Account GUID”.

– Once we have that Country value we can then dynamically create a new view and assign it to the State lookup field on the Contact form just like we did in the last scenario.

Here’s the jscript that achieves this:

function GetCountryFromAccount() { 
    //Get the GUIDvalue of this Contact's Parent Account 
    var ParentAccount = Xrm.Page.data.entity.attributes.get("parentcustomerid"); 
    if (ParentAccount.getValue() != null) { 
        var ParentAccountGUID = ParentAccount.getValue()[0].id; 
    } 

    // Read the CRM Context to determine the CRM URL 
    var serverUrl = Xrm.Page.context.getServerUrl() 

    // Specify the ODATA End Point and Entity Collection 
    var ODATA_ENDPOINT = "XRMServices/2011/OrganizationData.svc"; 
    var ODATA_EntityCollection = "/AccountSet"; 

    // Specify the ODATA Query 
    var ODATA_Query = "(guid\'" + ParentAccountGUID + "\')?$select=new_Country"; 

    // Combine into the final URL 
    var ODATA_Final_url = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + ODATA_Query; 

    //Calls the REST endpoint to retrieve the Country value of the Parent Account 
    $.ajax({ 
        type: "GET", 
        contentType: "application/json; charset=utf-8", 
        datatype: "json", 
        url: ODATA_Final_url, 
        beforeSend: function (XMLHttpRequest) { 
            XMLHttpRequest.setRequestHeader("Accept", "application/json"); 
        }, 
        success: function (data, textStatus, XmlHttpRequest) { 
            //This function will trigger asynchronously if the Retrieve was successful 
            var CountryGUID = data.d.new_Country.Id; 
            if (CountryGUID == null) { 
                // country on acct is null, we won't bother filtering the view 
                return; 
            } 
            else { 
                // call function to add custom view 
                ChangeLookupView_State(CountryGUID); 
            } 
        }, 
        error: function (XmlHttpRequest, textStatus, errorThrown) { 
            //This function will trigger asynchronously if the Retrieve returned an error 
            //Error encountered, we won't bother filtering the view 
            return; 
        } 
    }); 
} 

function ChangeLookupView_State(CountryGUID) { 
    //Set parameters values needed for the creation of a new lookup view... 
    var viewId = Xrm.Page.data.entity.getId();              // Your new lookup views needs a unique id.  It must be a GUID.  Here I use the GUID of the current record. 
    var entityName = "new_state";                           // The entity your new lookup view relates to 
    var viewDisplayName = "States for Account Country";     // A name for new lookup view 
    var viewIsDefault = true;                               // Whether your new lookup view should be the default view displayed in the lookup or not 

    //Define the Fetch XML query your new lookup view will use.  You can create this via Advanced Find.  You'll need to massage the syntax a little though 
    var fetchXml = 
              "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" + 
              "<entity name='new_state'>" + 
              "<attribute name='new_stateid' />" + 
              "<attribute name='new_name' />" + 
              "<attribute name='new_countryid' />" + 
              "<order attribute='new_name' descending='false' />" + 
              "<filter type='and'>" + 
              "<condition attribute='new_countryid' operator='eq' value='" + CountryGUID + "' />" + 
              "</filter>" + 
              "</entity>" + 
              "</fetch>"; 

    //Define the appearance of your new lookup view 
    var layoutXml = 
                    "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" + 
                    "<row name='result' id='new_stateid'>" +  // id = the GUID field from your view that the lookup should return to the CRM form 
                    "<cell name='new_name' width='200' />" + 
                    "</row>" + 
                    "</grid>"; 

    //Add your new view to the Lookup's set of availabe views and make it the default view 
    Xrm.Page.getControl("new_statelookupid").addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, viewIsDefault); 
}

Note: Because I’m following an oDATA approach this jscript needs the jquery and json2 libraries loaded into your CRM instance as web resources and needs those web resources referenced on the CRM form.  I talk more about these prerequisite steps in an earlier post.

 

Footnote:

The more I play with filtered lookups the more evident it becomes that all you are really able to do is improve the user experience, you are not able to effectively implement any sort of validation control, there are too many loopholes that will allow users to pick values that disobey the default filtering behaviour.   So, in addition to configuring your filtered lookups you really need to build out validation rules, either via jscript or Plug-in.

Adding Selection Criteria to REST Queries in CRM 2011

When querying the REST end point you are able to:

  • specify a filter to limit which records are returned,
  • request which fields should be returned,
  • specify ordering, and
  • specify joins

For help writing these scripts there’s an awesome tool available on Codeplex:  OData Query Designer, which has now been bundled into the Dynamics XRM Tools solution on Codeplex.

I recommend working out your REST query using the above tool and testing in Internet Explorer before you attempt to write your REST query in jscript. 

I find when I form my URL in jscript I need to replace blank spaces in the query section of the URL with “%20”, e.g.:

    // Define ODATA query
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc"; 
    var ODATA_EntityCollection = "/PriceLevelSet"; 
    var PriceListName = 'Wholesale Price List';
    var QUERY = "?$select=PriceLevelId&$filter=Name%20eq%20'" + PriceListName + "'&$top=1";
    var URL = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + QUERY;

When testing in Internet Explorer you need to lose the %20 and just have the space.  The OData Query Designer tool outputs URLs that will work in Internet Explorer.

Here’s a full example of a working REST query that selects a single field, uses a filter and performs a top n.  In this example (intended for the Opportunity form’s Onload event) I execute a REST query to retrieve the GUID of the Price List named “Wholesale Price List”.  As this uses REST your CRM form will need json2 and jquery libraries registered on the CRM form (I have these libraries in a solution file I import when needed):

function RetrieveGUID() { 
    // Get CRM Context
    var context = Xrm.Page.context; 
    var serverUrl = context.getServerUrl(); 
    // Cater for URL differences between on-premise and online 
    if (serverUrl.match(/\/$/)) { 
        serverUrl = serverUrl.substring(0, serverUrl.length - 1); 
    }
    // Define ODATA query
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc"; 
    var ODATA_EntityCollection = "/PriceLevelSet"; 
    var PriceListName = 'Wholesale Price List';
    var QUERY = "?$select=PriceLevelId&$filter=Name%20eq%20'" + PriceListName + "'&$top=1";
    var URL = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + QUERY;
    //Asynchronous AJAX call 
    $.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: URL,
        beforeSend: function (XMLHttpRequest) {
            //Specifying this header ensures that the results will be returned as JSON.
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
        },
        success: function (data, textStatus, XmlHttpRequest) {
            //This function will trigger asynchronously if the Retrieve was successful
            var GUID_Retrieved = data.d.results[0].PriceLevelId;
            DefaultPriceList(GUID_Retrieved, PriceListName); 
        },
        error: function (XmlHttpRequest, textStatus, errorThrown) {
            //This function will trigger asynchronously if the Retrieve returned an error
            alert("ajax call failed");
        }
    });
}

function DefaultPriceList(GUID, NAME){
        var lookupValue = new Array(); 
        lookupValue[0] = new Object(); 
        lookupValue[0].id = GUID; 
        lookupValue[0].name = NAME; 
        lookupValue[0].entityType = "pricelevel"; 
        Xrm.Page.getAttribute("pricelevelid").setValue(lookupValue); 
}

 

Here are some examples REST queries that demonstrate the type of URL you will need to construct in jscript:

SELECT

Fields URL
Name https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$select=Name

Name,

StateCode

https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$select=Name,StateCode

Note: by default all fields are selected

 

FILTER

Fields URL
Name https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$filter=Name%20eq%20‘Script X’

Name,

StateCode

https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$filter=Name%20eq%20‘Script X’%20and%20StateCode/Value%20eq%200

Note: The State Code field has multiple attributes so we need to use the syntax “StateCode/Value” to specifically refer to the Value of the StateCode field.

 

SELECT and FILTER together

Fields URL
Name https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$select=Name

&$filter=Name%20eq%20‘Script X’

 

ORDER BY

Fields URL
Name https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$orderby=Name

Name,

StateCode

https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$orderby=Name,StateCode

 

ORDER BY AND SELECT TOP N

Fields URL
Name https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$top=1

&$orderby=Name

 

Retrieve the ID of the 1st Active record with Name = ‘X’

Fields URL
Name,

WorkflowID
https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/WorkflowSet

?$select=WorkflowId

&$filter=Name%20eq%20‘Script X’%20and%20StateCode/Value%20eq%200

&$top=1

 

JOINS

Entities URL
Account,

Related Phone Calls
https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc

/AccountSet

?$expand=Account_Phonecalls

Note: The expand parameter requires a relationship name.  The default REST query response includes a list of all available relationships so make use of that:

image

 

STRING COMPARISONS

image

 

REFERING TO THE CURRENT USER

/AccountSet?$filter=OwnerId/Id%20eq%20(guid'" + SystemUserId + "')

 

OPERATORS

image

 

Warning:   Look out, it’s all case sensitive!  Keep referring back to an Internet Explorer to check how case is used, it tends to be different from the schema names used in CRM:

image

Launching a Dialog from a Ribbon Button in CRM 2011

Here’s some sample code that demonstrates how to execute a Dialog from a Ribbon button in CRM 2011.  The solution sample is provided in managed and unmanaged formats:

Managed Solution (updated: 25 Feb 2011)

Unmanaged Solution (updated: 25 Feb 2011)

note: this has only been tested successfully in CRM Online 

I have built out a Dialog based solution for a call centre, further extending the solution I documented in one of my earlier blogs.  The scenario is as follows:

A call centre agent takes both sales and service inbound calls.  They are tasked with:

  • searching for the caller in CRM and adding them as a new Contact if not found
  • determining whether the call is sales or service related
  • logging the phone call in CRM
  • recording a new opportunity or case
  • informing the caller they will be called back within 24 hours

I wanted a Dialog based solution that would guide the call centre agent through the process with the minimal number of clicks.  I didn’t want the user to have to create the initial CRM record required for the Dialog, I wanted the user to simply click a button.   My solution design makes use of the REST End Point to retrieve the GUID of the Dialog that should be launched and also to create the initial Phone Call record.   Dialog’s are URL addressable so launching the dialog is not difficult once you have the context record and the Dialog GUID.  In the Dialog itself I make use of Dialog Variables and Database Queries to query CRM to see if the Caller exists and to present a list of possible matches back to the user.   Pretty powerful stuff.

 

The end result looks like this…

The user has an incoming call, they click the Incoming Call button. 

 

image

The Button executes a jscript function that does a window.open, launching an HTML page I’ve loaded into CRM as a web resource.

 

An HTML page pops indicating a Phone Call is being created and the Call Script is being loaded:

image

This HTML page is used to reference some jscript libraries I need to extract the CRM context and for the execution of the REST call.  The HTML page provides the progress indicators to the user and then executes a jscript function that does the heavy lifting.

The jscript function:

  • queries the REST end point for Processes to retrieve the GUID of the “Inbound Call Script” Dialog Process.
  • uses the REST end point for Phone Calls to create a Phone Call record.
  • formulates a URL using the CRM context, Dialog GUID and the Phone Call GUID
  • does a window.open to launch that URL

 

And then the Dialog appears:

image

 

The Call Centre Agent greets the caller, notes there reason for calling and collects their name (as prompted by the Dialog script):

image

 

The script then prompts the agent to find out whether the caller has an existing account:

image

 

If the caller indicates they are an existing customer a Contact search page is presented:

image

 

A search is executed against CRM using the search strings entered (doing a “like” search).  And the search results are returned to the agent for review:

image

 

The user can go back and modify their search, or they can select one of the search results, or they can indicate the caller was not found in the database:

image

 

Finally, the agent selects the nature of the enquiry so that an Opportunity or Case record can be created in CRM:

image

 

The final page of the Dialog provides the call wrap up script:

image

 

Behind the scenes a Phone Call record has been created and linked to either an existing Contact or to a new Contact.  A Case or an Opportunity has been created and linked to the Contact.  And the Phone Call has been marked as completed.

 

I was pretty impressed by the extensibility offered by the CRM platform here.  The REST end point performed a little slowly for me but I haven’t tried to performance tune anything here.  The Dialog functionality largely bent the way I needed it to.  The area it was weakest was the display of the Query Results, where a pick list is not really suitable.  

 

To see this in action:

– Download the solution, install, publish and refresh your browser

(Known Issue: Check the Dialog is Active.  The Dialog definition contains references to the Australian currency which prevent it from publishing in instances missing that currency.  Edit those mappings as required)

– Navigate to Workplace –> Contacts

– The “Incoming Call” button should appear on the Ribbon (it appears on the “Main” tab for the core data entities, i.e. on the Account and Contact ribbons)

– Click the button

– Navigate the dialog

– At conclusion you should see:

   – A new completed Phone Call record

   – Attached to either a new Case or a new Opportunity

   – And both records related to either an existing Contact or a new Contact

Creating records from Jscript in Microsoft CRM using REST

With CRM 2011 and the REST End Point we now have an easy to use syntax for executing creating, updating and retrieving CRM records from jscript.  In this blog I’m going to demonstrate the approach for creating records.

I want to demonstrate a very simple scenario to give you a gentle introduction to REST, so let’s just create a new Account record with the name "TEST".  We will need our code to be triggered in some manner so lets just register it to run on the OnLoad event of the Letter form (an entity you probably haven’t customised in your development environment yet).  If you can get this working you will be able to add similar code to a custom Ribbon button or to a more applicable form event.

First thing you need to do is load into your CRM a couple of generic jscript libraries which contain ‘stuff we need’.  So, download the latest json2 and jquery libraries and load them into your CRM as jscript web resources.  Here’s the links:

http://www.json.org/json2.js.

http://jquery.com/

I downloaded these 2:

image

And loaded them into my CRM:

image

Whist you are there create a 3rd jscript web resource and paste this code into it:

// JScript source code 
function LetterOnLoad() { 
    var context = Xrm.Page.context; 
    var serverUrl = context.getServerUrl(); 
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc"; 
    var CRMObject = new Object(); 
    ///////////////////////////////////////////////////////////// 
    // Specify the ODATA entity collection 
    var ODATA_EntityCollection = "/AccountSet"; 
    ///////////////////////////////////////////////////////////// 
    // Define attribute values for the CRM object you want created 
    CRMObject.Name = "TEST"; 
    CRMObject.Telephone1 = "123"; 
    CRMObject.Fax = "456"; 
    //Parse the entity object into JSON 
    var jsonEntity = window.JSON.stringify(CRMObject); 
    //Asynchronous AJAX function to Create a CRM record using OData 
    $.ajax({ type: "POST", 
        contentType: "application/json; charset=utf-8", 
        datatype: "json", 
        url: serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection, 
        data: jsonEntity, 
        beforeSend: function (XMLHttpRequest) { 
            //Specifying this header ensures that the results will be returned as JSON. 
            XMLHttpRequest.setRequestHeader("Accept", "application/json"); 
        }, 
        success: function (data, textStatus, XmlHttpRequest) { 
            alert("success"); 
            var NewCRMRecordCreated = data["d"]; 
            alert("CRM GUID created: " + NewCRMRecordCreated.AccountId); 
        }, 
        error: function (XMLHttpRequest, textStatus, errorThrown) { 
            alert("failure"); 
        } 
    }); 
}

Ok, now we need to go and add this to the Letter entity.  So get into the onload event of the Letter’s main form and add these 3 jscript libraries:

image

I think the ordering is important so make sure the 3rd library you created to hold the code above is listed last.  It refers to functions that the other 2 libraries provide.

In the lower half of that Form Properties window we need to specify the function we want to execute at the form load event.   Add the LetterOnLoad function there:

image

Ok, that should work.  You didn’t need to localise anything, the necessary references are derived.  Simply, save, publish all, and then pop the Letter form.

The end result should be:

A success alert…

image

… followed by a second message providing the GUID of the Account record created…

image

… and the proof that something actually happened, a shiny new Account record in your CRM system:

image

So that should be enough to get you started.  The code snippet I have provided is not tied to the Letter entity or to the onload event so you should be able to relocate it to another event or put it behind a ribbon button.   If you want it to run outside of CRM you need to handle the context using GetGlobalContext.

Here’s some more comments on what the code is doing to help you understand how to change it to match your requirements…

First do a bunch of connectivity stuff and define a new variable for our Account record called “CRMObject”.  You don’t need to touch this:

var context = Xrm.Page.context; 
var serverUrl = context.getServerUrl(); 
var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc"; 
var CRMObject = new Object();

Next we name the CRM entity that we want to create.   Enter your entity name as I have done for Account – i.e. prepend a “/” and append “Set” – e.g. for Contact you will enter “/ContactSet”:

///////////////////////////////////////////////////////////// 
// Specify the ODATA entity collection 
var ODATA_EntityCollection = "/AccountSet";

Now we need to define the field values of our record that we want created.  We already declared our CRMObject variable, now we give it attribute values:

///////////////////////////////////////////////////////////// 
// Define attribute values for the CRM object you want created 
CRMObject.Name = "TEST"; 
CRMObject.Telephone1 = "123"; 
CRMObject.Fax = "456";

So my example was pretty simple I was just provided hardcoded values but of course you could have a bunch of logic here to come up with the values you want.

I hit some snags here as things are case sensitive and I wasn’t sure what the right case was.  To figure that out I suggest you browse the REST End Point using the relevant URL.  Here’s my URL for the Account entity for my online instance.  If you’re running CRM 2011 online just swap out the org name in yellow and specify whatever entity you are interested in where I have Account in green:

https://avanade10.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc/AccountSet

Simply navigating to this URL has the effect of submitting the following query:  “Select the first 50 records from the Account entity”. 

You’ll either see the data returned in a sort of feed format or as raw XML.   I saw the data in feed format (which is pretty, but not too useful)…

image_thumb3

… until I followed these instructions:

To view the data in Internet Explorer , you must first make sure that Internet Explorer is not enabled to format RSS feed. In the Content tab of Internet Options. select Settings in the Feeds and Web Slices group. Then, click to clear the Turn on feed reading view option. Close and re-open Internet Explorer .

Now I see it like this:

image_thumb1

Each <entry> node is an individual Account record.  Under each Account you will see first all the link references to each related entity and then below that under the <m:properties> node you will see each property of the Account record:

image_thumb5

Here you can check the case of the field name.

Moving on, the rest of the code executes the creation request and deals with the asynchronous response.  In the success function I create an object variable “NewCRMRecordCreated ” to hold the record created by the request.  I can then extract field values of that object and do more stuff. 

In my example I am dealing with an Account record and want to display the Account ID so you will see that mentioned and you will need to change that if you change my example to work with a different CRM entity:

success: function (data, textStatus, XmlHttpRequest) { 
    alert("success"); 
    var NewCRMRecordCreated = data["d"]; 
    alert("CRM GUID created: " + NewCRMRecordCreated.AccountId); 
},

I hope this of help to someone out there.   Smile