Sunday, 28 October 2012

CRM 2011 Update Rollup - Error after installation

After installing a CRM 2011 Rollup update, you may in some rare cases get the following error when trying to launch the CRM web application:
An error has occured.
Try this action again. If this problem continues, check the Microsoft Dynamics CRM Community for solutions or contact your organization's Microsoft Dynamics CRM Administrator. Finally, you can contact Microsoft Support.


Solution
You can resolve this very easily by performing the following steps:

1. iisreset - if this has not resolved the issue then try restarting the server

if the above steps have not fixed the problem, then In brief the issue is that certain assemblies have not successfully been updated in the GAC (Global Assembly Cache)

2. GACUTIL (.net framework 4) to install the following assemblies:
Microsoft.Xrm.Sdk.dll
Microsoft.Crm.Extensibility.dll
Using the command prompt either from Visual Studio or from the Windows SDK tools, perform the following commands from within the CrmWeb\bin folder.
gacutil /i Microsoft.Xrm.Sdk.dll
gacutil /i Microsoft.Crm.Extensibility.dll


3. iisreset

That's it, hopefully your CRM should be back to its normal self!


Friday, 19 October 2012

How to use JavaScript to Query the CRM oData Service

My previous post explained the oData features and how you could build an oData query to retrieve data via the CRM 2011 REST service, and as promised here is a nice example of how you can put this into practice. 

Background Requirements
When creating or updating the Parent Customer field on a Contact record, we want to retrieve the Address of the Parent Customer record and populate the address fields on the Contact record.

Note: The parent customer can be either an Account or Contact

Solution
1. First we need to create two JScript web references. To do this you need to go to Settings > Customizations > Customize the System || Solution
    1. JQuery - very useful in general, primarily we want to use the Ajax features
    2. Contact Parent Customer - this is where our custom JavaScript will live

2. Now we need to add these to the CRM Contact Form. To do this, you need to click on the customize form option for the Contact entity and then click on the Change Properties button in the ribbon. The below window will be displayed where you need to add both web references to the form libraries section:

3. Now we want to write our JScript code and store this in our Contact Parent Customer web reference. Go and edit the Contact Parent Customer web reference and paste the code below:

Code

function GetParentAddress()
{
//CRM REST Endpoint
var oDataEndpoint = Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc";
//Clear existing field values
Xrm.Page.getAttribute("address1_name").setValue("");
Xrm.Page.getAttribute("address1_postalcode").setValue("");
Xrm.Page.getAttribute("address1_line1").setValue("");
Xrm.Page.getAttribute("address1_line2").setValue("");
Xrm.Page.getAttribute("address1_line3").setValue("");
Xrm.Page.getAttribute("address1_city").setValue("");
Xrm.Page.getAttribute("address1_stateorprovince").setValue("");
Xrm.Page.getAttribute("address1_addresstypecode").setValue("");
Xrm.Page.getAttribute("address1_country").setValue("");
//Get Parent Customer Id & Parent Customer Type
var id = Xrm.Page.getAttribute("parentcustomerid").getValue()[0].id;
var parentType = Xrm.Page.getAttribute("parentcustomerid").getValue()[0].typename; 
//Build Query
var oDataQuery = oDataEndpoint + ((parentType=="account") ? "/AccountSet?$filter=AccountId " : "/ContactSet?$filter=ContactId ");
oDataQuery += "eq guid'" + id + "'";

$.ajax({
  type: "GET",
  contentType: "application/json; charset=utf-8",
  datatype: "json",
  url: oDataQuery,
  beforeSend: function (XMLHttpRequest) 
  { 
XMLHttpRequest.setRequestHeader("Accept", "application/json"); 
  },
  success: function (data, textStatus, XmlHttpRequest)
  {
//success - populate fields on Contact Screen
Xrm.Page.getAttribute("address1_name").setValue(data.d.results[0].Address1_Name);
Xrm.Page.getAttribute("address1_postalcode").setValue(data.d.results[0].Address1_PostalCode);
Xrm.Page.getAttribute("address1_line1").setValue(data.d.results[0].Address1_Line1);
Xrm.Page.getAttribute("address1_line2").setValue(data.d.results[0].Address1_Line2);
Xrm.Page.getAttribute("address1_line3").setValue(data.d.results[0].Address1_Line3);
Xrm.Page.getAttribute("address1_city").setValue(data.d.results[0].Address1_City);
Xrm.Page.getAttribute("address1_stateorprovince").setValue(data.d.results[0].Address1_StateOrProvince);
if (data.d.results[0].Address1_AddressTypeCode != null)
{
Xrm.Page.getAttribute("address1_addresstypecode").setValue(data.d.results[0].Address1_AddressTypeCode.Value);
}
Xrm.Page.getAttribute("address1_country").setValue(data.d.results[0].Address1_Country);
  },
  error: function (XmlHttpRequest, textStatus, errorThrown) 
  { 
//failed
alert('oData Query Error: ' + oDataQuery); 
  }
});
}

4. Now we need to set the Parent Customer field OnChange event on the Contact form to trigger the GetParentAddress() function every time the value changes. To do this, navigate back to the Customize Contact Form window:

5. Click on the Parent Customer field and then click on the Change Properties button in the ribbon.

6. On the Events tab select the Parent Customer field from the Control drop-down and Add a new OnChange event.

7. Select the Contact Parent Customer library from the drop-down, and set the function to GetParentAddress as displayed below. 

8. Save and then Publish All Changes. 

Testing our Solution
I have created a new Account and a new Contact record, both with different addresses. 

Now when we change the Parent Customer field on any Contact record, either when we create or update a record we should notice the Contact address fields should auto-populate to the address values of the parent customer as shown below:

Test 1 Parent Customer = Account

Test 2 Parent Customer = Contact



Monday, 15 October 2012

CRM 2011 - oData Explained

An exciting new feature of CRM 2011 is the introduction of a REST endpoint which you can use to perform oData based queries against. The location of your endpoint can be identified using the following format: 

http://<server>/<organizationame>/XRMServices/2011/OrganizationData.svc/

Each entity in CRM is represented by a CSDL (Conceptual schema definition language) collection, and each collection uses the EntityName Set naming convention. For example you would use the following to access the Accounts collection:

http://<server>/<organizationame>/XRMServices/2011/OrganizationData.svc/AccountSet 

To create your query, you will append your criteria to the end of the URI. The following list defines what options you can use:

  • $expand - if you want related records to be retrieved with the record or collection
  • $filter - expression or function that must evaluate to ‘true’ for a record to be returned
  • $orderby - what values are used to order the returned records
  • $select - used to limit which columns/fields you wanted returned, default returns all
  • $skip - skip a number of records before returning results
  • $top - the max number of records to return, similar to TOP in TSQL

$expand
An example of the $expand option could be where you want to return all Contacts that belong to a specific Account, along with the account record itself. To do this all you need to do is specify the relationship name that defines the relationship between the Account and Contact entities.

By default the Account entity has a 1:N relationship named contact_customer_accounts displayed below:

 


So you can use the following oData query which will return the Account record that matches the account guid and will return all contact records.

http://<server>/<organizationame>/XRMServices/2011/OrganizationData.svc/AccountSet?$filter=AccountId eq guid'BA91ACB8-B813-E211-AABC-000C2970FBB2'&$expand=contact_customer_accounts

$filter
The use of the $filter option can be seen above, and below lists additional options you can use in your expression:
  • Equal - /AccountSet?$filter=Address1_City eq 'London'
  • Not equal - /AccountSet?$filter=Address1_City ne null
  • Greater than - /AccountSet?$filter=Revenue/Value gt 1000
  • Greater than or equal - /AccountSet?&$filter=Revenue/Value ge 1000
  • Less than - /AccountSet?$filter=Revenue/Value lt 1000
  • Less than or equal - /AccountSet?$filter=Revenue/Value le 1000
  • Logical and - /AccountSet?$filter=Revenue/Value ge 1000 and Address1_City eq 'London'
  • Logical or - /AccountSet?$filter=AccountCategoryCode/Value eq or AccountRatingCode/Value eq 1
  • Logical Negation - /AccountSet?$filter=(AccountCategoryCode/Value ne null) and not (AccountCategoryCode/Value eq 1)
Option Sets - to filter against option set values you need to use the /Value e.g.
/AccountSet?$filter=AccountCategoryCode/Value eq 1

Entity Reference - to filter against entity references by id you need to use the /Id e.g.
/ContactSet?$filter=ParentCustomerId/Id eq (guid'BA91ACB8-B813-E211-AABC-000C2970FBB2') 

Functions
In combination with the filter option, you can use any of the following functions. For example purposes imagine that the field Address1_City on the Account entity was set to London
  • Starts With - /AccountSet?$filter=startswith(Address1_City, 'Lon')
  • Sub String Of - /AccountSet?$filter=substringof('Lond', Address1_City)
  • Ends With - /AccountSet?$filter=endswith(Address1_City, 'don')

$orderby
Typical order by functionality, you can order by named fields, and by ascending or descending, or both
  • Ascending Ordering - /AccountSet?$filter=Name eq 'MyAccount'&orderby=Addess1_City
  • Descending Ordering - /AccountSet?$filter=Name eq 'MyAccount'&orderby=Addess1_City desc
  • Both - /AccountSet?$filter=Name eq 'MyAccount'&orderby=Addess1_City, Address1_PostalCode desc

$select
Using the select option you can limit which fields you want returned and in which order.
e.g. /AccountSet?$select=Name, Address1_Line1, Address1_Line2, Address1_Line3, Address1_City

$skip
Use this option to skip the first N number of records e.g.
/AccountSet?$filter=Name eq 'Company'$skip=1 

$top
Use this option to select the first N number of records
/AccountSet?$filter=Name eq 'Company'$top=5 

Please note that because we are affectively just building a URL, you can test your queries from within the browser. 

I will shortly follow up this post with a practical example of how you can use the REST endpoint and oData directly from your CRM forms using JavaScript.

Tuesday, 9 October 2012

Common JavaScript examples for CRM 2011

MS CRM developers often have to write JavaScript in order to help manipulate the CRM form. Many libraries are now available which make client side scripting, DOM manipulation, validation and ajax much easier, however CRM 2011 now comes with some useful objects and features which now makes it easier than ever to do this.

In brief you will want to lookup the following:

Xrm.Page.data.entity.attributes – field access and manipulation on the form
Xrm.Page.ui.controls – working with ui controls on the form
Xrm.Page.ui.navigation.items – working with navigation items on the form
Xrm.Utility – a set of useful helper functions

Here are some examples of the most common I have come across:

How to get text field value:
var value = Xrm.Page.data.entity.attributes.get("fieldname").getValue(); 

How to get the ID of a lookup field:
var id = Xrm.Page.data.entity.attributes.get("fieldname").getValue()[0].id;

How to get the ID of the current record:
var id = Xrm.Page.data.entity.getId();

How to get the Text of a lookup field:
var text = Xrm.Page.data.entity.attributes.get("fieldname").getValue()[0].name; 

How to set the value of a lookup field:
function PopulateLookup(fieldname, id, name, type) {
        var lookupData = new Array();
        lookupData[0] = new Object();
        lookupData[0].id = id;
        lookupData[0].name = name;
        lookupData[0].entityType = type;
        Xrm.Page.getAttribute(fieldname).setValue(lookupData);
}

How to set value of a Text field:
Xrm.Page.data.entity.attributes.get("fieldname").setValue("my value goes here!"); 

Notes: as with all drop-downs, each option has a value and display text and you should also check for null
How to get option set value:
var opField = Xrm.Page.data.entity.attributes.get("fieldname");
var value = opField.getValue();

How to get option set text:
var opField = Xrm.Page.data.entity.attributes.get("fieldname");
var text = opField.getText();

How to select option in Option Set field:
Xrm.Page.data.entity.attributes.get("fieldname").setValue(2);  

How to get values from a Data field:
var dateField = Xrm.Page.data.entity.attributes.get("fieldname").getValue();
if (dateField != null) {
        var day = dateField.getDate();
        var month = (dateField.getMonth() + 1); // increment by 1 as Jan starts at 0
        var year = dateField.getFullYear();
}

How to set a Date field to Todays date:
Xrm.Page.data.entity.attributes.get("fieldname").setValue(new Date());

How to Hide and Show a field:
var field = Xrm.Page.ui.controls.get("fieldname"); 
field.setVisible(false);  // hide field
field.setVisible(true);   // show field

How to Disabled and Enabled a field:
var field = Xrm.Page.ui.controls.get("fieldname");  
field.setDisabled(true);   // disable field
field.setDisabled(false); // enable field

How to Hide and Show a Navigation Item:
var navitem = Xrm.Page.ui.navigation.items.get("navitemname"); 
navitem.setVisible(false);  // hide
navitem.setVisible(true);   // show

How to Hide and Show a Tab:
var tab = Xrm.Page.ui.tabs.get("tabname");
tab.setVisible(false);  // hide
tab.setVisible(true);   // show

How to Save, Save and Close, and Close a Form:
Xrm.Page.data.entity.save();                                 // Save
Xrm.Page.data.entity.save("saveandclose");      // Save and Close 
Xrm.Page.ui.close();                                               // Close

How to Identify Form Mode: 
var formMode = Xrm.Page.ui.getFormType();
switch(formMode)
{
case 1: // Create
break;
case 2: // Update
break;
case 3: // Read Only
break;
case 4: // Disabled
break;
}

How to get the ID of the current user:
var id = Xrm.Page.context.getUserId();

How to get all the security roles for the current user:
var roles = Xrm.Page.context.getUserRoles();

How to open an existing CRM record in a new window:
Xrm.Utility.openEntityForm("account", guid);
Note: Just replace the first parameter with the entity logical name and the second parameter with the record guid.

How to open a new CRM record window:

Xrm.Utility.openEntityForm("account");

How to refresh a Sub-Grid:
Xrm.Page.ui.controls.get("gridname").refresh();

How to set Requirement Level of field using JavaScript:
Xrm.Page.getAttribute("fieldname").setRequiredLevel("none"); // No Constraint
Xrm.Page.getAttribute("fieldname").setRequiredLevel("required"); // Business Required
Xrm.Page.getAttribute("fieldname").setRequiredLevel("recommended"); // Business Recommended

How to get field Requirement Level:
Xrm.Page.getAttribute("fieldname").getRequiredLevel();

How to add an OnChange event handler to a field:
Xrm.Page.getAttribute("fieldname").addOnChange(functionName);

How to identify if a field value has changed:
Xrm.Page.getAttribute("fieldname").getIsDirty();
Note: this returns a Boolean value true/false 


Action Microsoft.Crm.Setup.Common.Analyzer +CollectAction failed. Fatal error during installation

When installing the Srs Data Connection (Microsoft Dynamics CRM Reporting Extensions), you may have experienced the following error: ...