Activity coding in Dynamics CRM
At a previous job, we had used GoldMine (version 6.7 was where we parted ways) as our CRM system before moving to Dynamics CRM. At the time - maybe it's still done this way - GoldMine allowed you to code activities with a hierarchy of references, codes and results. We used reference to separate activities either by department or broad functional grouping (e.g. "service" was a separate reference although there was no separate "service" department). Codes were the specific kinds of activities or client touch points grouped under a reference, and the results indicated how the activity concluded.
The reference/code/result structure allowed us an incredible level of granularity for tracking all the times we either interacted with an external stakeholder (client, prospect, partner, external service provider) or did something on its behalf. We were able to compile detailed "production" reports for our call center reps, and the activities' structure gave them a framework for approaching their routine daily responsibilities.
Here's a simple reference/code/result example:
- Reference - Sales
- Code - 1PC//Prospecting call_ (In GoldMine everything before the // was treated as the picklist entry's primary key, so the 1PC would be stored in the database. We would then speak of 1PC calls as a shorthand.)_
- Result - 1LM//Left message
We tracked everything this way. Despite us still using GoldMine 6.7 in some parts of our business all the way into 2008, activity tracking was great except for two problems. First, although the codes existed in our processes as a logical hierarchy, GoldMine didn't understand that. If you picked the "sales" reference, you still had to weed through all the codes and results that corresponded to all the other references. Second, adding new reference/code/result entries in a supported manner took a lot of time.When we started considering the move to CRM 3 in 2005, I found the default activity entity structure to be something of a letdown, so I brought the reference/code/result framework over when we actually did move. Here's how I did it.
First, I created three separate picklist attributes in CRM on the six main activity entities we used - appointment, e-mail, fax, letter, phone call and task. Let's call them lucas_reference, lucas_code and lucas_result. I then populated them with the existing references, codes and results from GoldMine. The following are not the actual codes, but they'll give you a decent idea of how things were structured.
References (lucas_reference)
Sales
Service
Codes (lucas_code)
1CD//Contract discussion
1PC//Prospecting call
1PE//Prospecting e-mail
1FC//Follow-up call
1FE//Follow-up e-mail
1SC//Suspect call
2CI//Service call inbound
2EI//Service e-mail inbound
2FU//Service follow-up
Results (lucas_result)
1CF//Contract finalized
1UP//Understands product
2CS//Customer satisfied
2CU//Customer unsatisfied
2IE//Issue escalated
2IR//Issue resolved
CRS//Call rescheduled
CMP//Completed
EMI//E-mail in
EMO//E-mail out
PCI//Phone call in
PCO//Phone call out
LVM//Left message
In this example, codes and results starting with 1 are children of the "sales" reference. Codes and results starting with 2 are children of the "service" reference. Codes and results starting with letters are general options available to any reference.
At this point, allow me to point out a few things. First, we didn't use the existing CRM service activity, and we rarely (practically never) used the fax or letter entities. Why we didn't is beyond the scope of this blog post.Second, the reference/code/result picklist entries were duplicated across all six activity entities even if they didn't make sense. That is, you could have a task with a result of "PCO//Phone call out." Because we had several hundred options, it was easier to make sure every activity had everything than to weed out the ones that didn't fit. Also it made report writing simpler.Third, the general options were handy for when we didn't have a firm process that applied to the activity. For example, sometimes on the service side, all we could do was note that a call came in.Fourth, reference and code were business required. Result was not because you obviously wouldn't know a scheduled activity's result at the point in time when you created it.
Once I created and populated the picklist attributes, I added them to the entities' forms, which was simple enough, but you'd still have the GoldMine problem of every code and result being available for any reference. I took care of that with a bit of JavaScript.The following example is based on something I picked up from "Working with Microsoft Dynamics CRM 3.0" by Mike Snyder and Jim Steger.
var objRef = crmForm.all.lucas_reference;
var objCode = crmForm.all.lucas_code;
var objRes = crmForm.all.lucas_result;
//get the currently selected code and result values so we don't lose data if we're editing an activity instead of creating a new one
var curcode = objCode.DataValue;
var curresult = objRes.DataValue;
//set the lucas_code and lucas_result picklist options to empy arrays for now
var oTempArray = new Array();
objCode.Options = oTempArray;
var oTempArrayRes = new Array();
objRes.Options = oTempArrayRes;
//if a reference is selected
if(objRef.SelectedText!="")
{
//get prefix of codes we're going to enable
var codePrefix = "";
var resPrefix = "";
switch(objRef.SelectedText)
{
case "Sales":
codePrefix = "1";
resPrefix = "1";
break;
case "Service":
codePrefix = "2";
resPrefix = "2";
break;
}
//enable the correct code codes
if(codePrefix != "")
{
//loop through the original lucas_code options
for (var i = 1; i < objCode.originalPicklistOptions.length; i++)
{
//if the lucas_code option begins with the codePrefix, add it to the holding array
if(objCode.originalPicklistOptions[i].Text.indexOf(codePrefix)==0)
{
objCode.AddOption(objCode.originalPicklistOptions[i].Text, objCode.originalPicklistOptions[i].DataValue);
}
}
//make sure the picklist is enabled
objCode.Disabled = false;
//set the picklist's selected value to what we stored earlier
objCode.DataValue = curcode;
}
else
{
//disable the field
objCode.Disabled = true;
}
//enable the correct result codes
if(resPrefix != "")
{
//loop through the original lucas_result options
for (var j = 1; j < objRes.originalPicklistOptions.length; j++)
{
//if the lucas_result option begins with the resPrefix, add it to the holding array
if(objRes.originalPicklistOptions[j].Text.indexOf(resPrefix)==0)
{
objRes.AddOption(objRes.originalPicklistOptions[j].Text, objRes.originalPicklistOptions[j].DataValue);
}
//handle some general cases
if(objRes.originalPicklistOptions[j].Text.indexOf("CRS")==0)
{
objRes.AddOption(objRes.originalPicklistOptions[j].Text, objRes.originalPicklistOptions[j].DataValue);
}
if(objRes.originalPicklistOptions[j].Text.indexOf("CMP")==0)
{
objRes.AddOption(objRes.originalPicklistOptions[j].Text, objRes.originalPicklistOptions[j].DataValue);
}
}
//make sure the picklist is enabled
objRes.Disabled = false;
//set the picklist's selected value to what we stored earlier
objRes.DataValue = curresult;
}
else
{
//disable the field
objRes.Disabled = true;
}
}
else
{
//no selected reference, no displayed codes or results
objCode.Options = oTempArray();
objRes.Options = oTempArrayRes();
//disable the fields for good measure
objRes.Disabled = true;
objCode.Disabled = true;
)
Add that to the onLoad event for each activity form and the onChange event for the reference field on each activity form, and you're good to go.
Happy activity documenting!