Wednesday, November 26, 2008

Interactive Webcast: The Top 5 Ways to Save Money with CRM

I've been invited back to be a guest speaker for an interactive Webcast on December 4th at 10:00 AM Pacific, 1:00 PM Eastern, 6:00PM GMT. This is through CBS interactive and Tech Republic.

http://webcasts.techrepublic.com.com/abstract.aspx?docid=390610&promo=100202

Thursday, November 20, 2008

Queues in MS CRM

There are a lot of misconceptions about queues. They only apply to cases and activities.  While the terminology is the same, assigning a case or activity to a queue is not the same as assigning an entity to a CRM user or team. Programmatically what is being used is a RouteRequest rather than an AssignRequest.

To get a better idea how this works we'll look at database. There are two entities storing queue information, the Queue and the QueueItem. These are not customizable.

Queue – (table) contains all queues

  • There are two queues automatically created for each CRM user. 
    • Assigned
    • In Progress
  • Any user defined queues that you create are also stored here.
    • example:  My New Public Queue

QueueItem – (link table)  Contains an entry for each entity assigned to a queue.

  • An item can only belong to one Queue at a time,
  • The only entities that can below to a Queue are:
    • Activities ( email, tasks, etc. )
    • Cases

An activity or case retains its owner and is not modified in any way when it is assigned to a queue. It is just added to the QueueItem list.  An entity can ONLY belong to one queue at a time.

Normal Lifecycle

When created a case or activity is added to the Assigned queue of the owner and it will stay there until it is completed/closed/canceled.

Sometimes a workflow will assign an entity to a user defined queue. (Below is an example that just puts all created Cases into a Support queue.) You might want logic with timers to move Cases around, or you might have different queues by subject, or related account territory. The important thing is that the queue is used as a natural part of the customer's business process.

image

When someone selects an entity in the queue and clicks  Accept... that entity it is then moved into that person's "In Progress" queue.

image

Example code working with Queues

Creating a Queue

var newQueue = new queue
{
    name = "My New Public Queue",
    businessunitid = new Lookup { Value = BusinessUnitId, type = EntityName.businessunit.ToString() },
    primaryuserid = new Lookup { Value = UserId, type = EntityName.systemuser.ToString() },
    queuetypecode = new Picklist { name = "Public", Value = 1 }
};

service.Create(newQueue);

Assigning an incident to a Queue

// Target the incident var target = new TargetQueuedIncident { EntityId = incidentId }; // Create a RouteRequest ( you would need to query for your Queue Id's ) var route = new RouteRequest { Target = target, RouteType = RouteType.Queue,
SourceQueueId = currentQueueId.Value EndpointId = finalQueueId, }; // Execute the route var routed = (RouteResponse)service.Execute(route);

Tracking Queue Assignment in a Plugin

One of the related improvements to MS CRM 4.0 is the inclusion of the Route Message for Plug-ins.  For example, you can now trigger based on a Case being routed to a queue even though the case entity is not modified in any way.

Your code can inspect the SourceQueueId to see what queue it is coming from and the EndpointId to see the destination queue.

This link shows a complete list of the Plug-in Message Input Parameters. You can see that the Route Message has 3 input parameters, two which I mentioned (SourceQueueId and EndpointId) as well as RouteType which has three values ( Auto = 0 ( automatic route) , User = 1 (route to a user's private queue), Queue =  2 (route to a public queue) ) You could use the RouteType to filter out a chunk of queue routes that wish to ignore.

The fact that the dynamic entity of an incident contains no information about the queue it is assigned to means that you need to examine the context.InputParameters.Properties to find out which queue the case is coming from and which queue it will finally call home.

public void Execute(IPluginExecutionContext context)
   {
      DynamicEntity entity = null;

    if (context.InputParameters.Properties.Contains(ParameterName.Target) &&
       context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
    {
        entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
        
        if (entity.Name != EntityName.incident.ToString()) { return; }
        if (context.MessageName != MessageName.Route.ToString()) { return; }
        
        Guid SourceQueueId = ((Moniker)context.InputParameters.Properties["SourceQueueId"]).Id;
        Guid EndpointId = ((Moniker)context.InputParameters.Properties["EndpointId"]).Id;
    }

Wednesday, November 19, 2008

A Couple Data import issues

I thought these items might be of help to other people populating data in MS CRM systems. The first is documented, but for the second I found no information on web.

Issue 1: When importing email activities, like all other activities in the vast majority of instances you need to complete them so they show up in history and not in the current Activities of a user's Workplace or against the items to which they are regarded.

If you import an email message with a From email address that is not a CRM system user, you can create the activity without a problem, but will get a Soap Exception "The specified sender type is not supported." When you try to set the state of the email to "Received". The following hot fix will allow you to Complete those incoming emails and set them to "Received".

http://support.microsoft.com/kb/947860/en-us

Issue 2:  While importing account information after a customer of mine added additional customertypecode picklist items I ran into a Soap Exception.

<code>0x8004431a</code>
<description>A validation error occurred.  The value of 'customertypecode' on record of type 'account' is outside the valid range.</description>

The customertypecode picklist starts at 1 and progresses linearly with the default values out of the box, and you can always rename the existing items, but any new picklist items start with values of 200001 and progress linearly from there.

Workaround: Edit an existing account or add a dummy account and select a new pick list item with a higher ranged value and save that account record. Now the problem magically disappears.

Hypothesis: I'm thinking that the CRM webservices are using reflection like Excel does on a spreadsheet column but against the database and it initially ranges the pick list field as a short, but only ranges it up when a larger value exists.

If anyone has a better understanding of why this behavior occurs, I would love to hear about it.

Wednesday, November 12, 2008

Interactive Webcast: Using CRM to Drive Sales in a Slowing Economy

I've been invited to be the guest speaker for an interactive Webcast on November 13th at 1:00 PM Pacific, 4:00 PM Eastern, 9:00PM GMT. This is through CBS interactive and Tech Republic.

http://webcasts.techrepublic.com.com/abstract.aspx?docid=390611

Friday, November 7, 2008

Annotation Import web service error

During a recent import I was getting some exceptions while adding annotations to Accounts and Contacts. The exceptions being generated were not SoapExceptions, but HTTP 400 malformed http request errors.

This was a data driven problem and I assumed that it was an illegal character messing up the SOAP. The annotation.notetext attribute was the only one being set to a large chunk of unknown text. Debugging this showed some illegal characters in my source data. Initially I found an occasional "\0" which is easily removed, but there was another character that showed up as a box symbol.

So how do you filter that out?

First you need to know what you are looking at, so while debugging copy the offending character into the clipboard and paste it into a text file. In Visual Studio there is a binary editor.

File -> Open File
After selecting your file click notice the small pick list to the right of Open.
Select Open With...

evilcharacter

Viewing the text file revealed that the offending character was a hex 12. I had copied three of them into NotePad. For the curious among you a hex 12 = DC2 or Device Control 2.

hex_editor

Armed with that information the following statement filtered out the offending characters and the import completed without error.

newAnnotation.notetext = note.Replace('\0', ' ').Replace('\u0012', ' ');

Below is a code example using this filter to generate contact annotations.

var newAnnotation = new annotation
       {
          objectid = CrmTypes.CreateLookup(EntityName.contact.ToString(), contactId),
          objecttypecode = CrmTypes.CreateEntityNameReference(EntityName.contact.ToString()),
          isdocument = CrmTypes.CreateCrmBoolean(false),
          ownerid = newContact.ownerid,
          subject = "GoldMine notes",
          notetext = note.Replace('\0', ' ').Replace('\u0012', ' ')
       };

try
{
    service.Create(newAnnotation);
}

This could have been solved a number of other ways like a regex that only allows valid characters for a SOAP request, but I haven't run into this issue enough to create one.