Archive for February, 2012

CRM 2011: Custom Reports & the rsProcessingAborted Error

I created a custom report for CRM 2011 in BIDS months ago for a client, but was recently notified that it had stopped working after some significant changes were made to their environment. Out-of-the-box reports would display just fine, but the one I wrote displayed a vague “The report cannot be displayed. (rsProcessingAborted)” message and there was nothing useful in the Event Viewer to shed more light on the problem.

After confirming that the data source and credentials my report was using were still valid, I started poking around and discovered that other custom reports, even those created through the CRM 2011 Report Wizard, were failing with the same error. I tried a few solutions that I found online, but the one that fixed it is covered in When CRM 2011 Custom Reports Don’t Work.

Turns out all I needed to do was look up which account was being used for the CRM Application account by checking the CRMAppPool in IIS, then add an SPN for it with “setspn –a HTTP/NETBIOSNAME DOMAINSERVICEACCOUNT”. Such a quick and easy solution for something I was beginning to worry would end up being a real headache to fix! 

Tags: , , ,


Deactivating an Option Set Value in MS CRM

MS CRM has grown considerably from it’s early days. Customizing and developing against the older versions certainly had it’s share of pain and I really enjoy working with MS CRM 2011. For all the features they’ve added, though, there’s one still missing that I’m surprised isn’t baked into the product and that is the ability to retire an option set value. By retiring a value, I want a value in an option set to no longer be selectable, but, if any records have that existing value, I want it to keep that value. Currently, if you remove an option set value, any records that have that value will NOT be updated but the value that they had before will no longer link to an option set value.

As an example, open the Account entity and find the address1_shippingmethodcode field. If you look at the option set you’ll see it contains a list of values. For our example, we want to disable the value “Full Load” because it’s no longer a valid shipping option for new customers or customers that haven’t already chosen this shipping method. In this case it should be hidden. Any existing customers that have already selected this method may keep this shipping method, so if they have that value already, we want it to show up in the form when it’s opened. My coworker Rod McBride made a great suggestion as he was reviewing my post, but is NOT implemented below, and that is adding effective dates for an option set value. It wouldn’t be hard to add – simply add the start and date fields to the entity, add those fields to the oData query and then use those fields when determining to deactivate the picklist option. I’ll leave that as an exercise for you!

To do this:

  1. Create an account and on that account set the address1_shippingmethodcode to “Full Load” and save the account. You’ll need this to test later.
  2. Create a second account and on this account, set the address1_shippingmethodcode to a value other than “Full Load”. You’ll also need this to test later.
  3. Create a new entity (Disabled Picklist Items) with four attributes (in addition to the default name): new_entityname (Single Line of Text), new_entitytypecode (Whole Number), new_attributename (Single Line of Text), new_disabledattributevalue (Whole Number).
  4. Modify the form of the newly created entity (Disabled Picklist Items) and add the four fields to that form.
  5. Publish your changes. You now have a form that you can use to add records that indicate which option set value for a given entity/attribute combination that should be retired.
  6. Using the form you just created, add a new record of the type Disabled Picklist Items. Give it a name of “Account Shipping Method”. Enter the entity name as “account”, set the entitytypecode to 1 (1=account), set the Attribute Name as  “address1_shippingmethodcode” and set the Disabled Attribute Value of 6 (6 is the option set value for “Full Load”).
  7. Next, open the account form in design mode. On the form properties add two libraries: one for jQuery (I add jQuery often because it’s quite nifty; yes, I just used the word “nifty”. Deal with it. :)) and one that will contain the jscript that will do the actual work (client side) of removing the option set value that’s disabled if it’s not the selected value. As you can see, I have added new_jquery1.6.4.js (use whatever version you like) and a second called “new_DisablePicklistItem”. Set the form load to call a function named “DisablePicklistItems”.
  8. The code for the new_DisablePicklistItem script should look like the code below (see the bottom of the post). I put lots of comments in to explain what it’s doing. Most of the code is your typical MS CRM web service stuff. All that’s unique is the part that constructs the oData query and the part that actually processes the results.
  9. When you’re done, publish your changes.
  10. Open the account you created in the first bullet point. If you’ve gotten everything right, you should see your shipping method on address1 is “Full Load”.
  11. Now go to the account you created in the second bullet point. You should not see the value “Full Load” in the list. Same if you attempt to create a new account.

One thing I’ve noticed is that there is a bit of a delay the first time you open an account and the web service is accessed which is why I put a call to disable the control in question before the values are removed. Subsequent calls are much faster.

There you have it! You now have the ability to retire an option set value so that it will show up for those records that already have the value selected, but accounts that don’t (and new accounts) can’t select the retired option.

Here’s what the list looks like before:

Here’s what it looks like after – note that the option “Full Load” is missing:

function DisablePicklistItems(context)
{
 // Disable the control in question. In theory, you should read
 // what controls are disabled and dynamically disable them.
 // The Disable Picklist Items records you create in MS CRM
 // have the attributes you need to figure out what controls to disable.
 // Since I have a lot of work to do, I'm hard-coding to the attribute we're
 // discussing here.
 Xrm.Page.ui.controls.get("address1_shippingmethodcode").setDisabled(true);

 // The line below constructs and oData query that gets all the
 // DisabledPicklistItems records for the entity that we are on.
 var query = '/XRMServices/2011/organizationData.svc/new_disabledpicklistitemSet?$select=new_AttributeName,new_disabledattributevalue&$filter=new_EntityTypeCode eq ' + context.getContext().getQueryStringParameters().etc;

// Call a method that actually calls the web service.
 getEntityItems(query, "disabledItems");
}

// This function just calls the oData web service. It also
// specifies the callback function that gets called when
// the state of the request changes.
function getEntityItems(oDataQuery, queryName) {
 try {
 var serverUrl;
 serverUrl = Xrm.Page.context.getServerUrl() + oDataQuery;
 var request = new XMLHttpRequest();
 request.open("GET", serverUrl, true);
 request.setRequestHeader("Accept", "application/json");
 request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
 // When the request state changes, call the function "requestComplete".
 request.onreadystatechange = function () {
 requestComplete(request, queryName);
 }
 request.send();
 } catch (e) {
 // You probably want to do something other than an alert here.
 alert(e.Description);
 }
}

// The query name parameter is passed so that this function can be used by
// multiple functions that make CRM web service calls.
function requestComplete(request, queryName) {
 if (request.readyState == 4 && request.status == 200) {
 // Everything was OK, so get the request response and assign it to a variable.
 var json = $.parseJSON(request.responseText);
 if ( (json != undefined) && (json.d != undefined) && (json.d.results != undefined) && (json.d.results[0] != null) ) {
 // If you had more than one query, you would add additional else-ifs.
 // Might even make sense to use a switch statement. Might make even more
 // sense to have each if (or switch) call a completely separate function
 // instead having the logic inline. :)
 if (queryName == 'disabledItems') {
 // This is the heart of the process. We've gotten data
 // from the server so we need to loop through each value.
 // (The $ notation is jQuery which is beyond the scope of this post.)
 $.each(json, function(index, value) {
 // For each returned disabled attribute record from the web service,
 // get the attribute name and attribute value.
 var attribute = value.results[0].new_AttributeName;
 var attributeValue = value.results[0].new_disabledattributevalue;

 // Get the current value from the page of the attribute in question.
 var currentValue = Xrm.Page.data.entity.attributes.get(attribute).getValue();

 // If the current value on the page is not equal to the currently iterated
 // disabled value, then remove the option from the option set.
 if (currentValue != attributeValue)
 {
 Xrm.Page.getControl(attribute).removeOption(attributeValue);
 }

 });
 }
 }
 // Re-enable the control. Again, this is hard coded for speed but you should
 // probably do this dynamically.
 Xrm.Page.ui.controls.get("address1_shippingmethodcode").setDisabled(false);
 }
}

 

Tags: , , ,