Sunday, December 28, 2014

Microsoft Unified Service Desk (USD) Configuration 101 - Usage and Entities

With a new year right around the corner, I figured I could talk about one of the fairly new areas/products of Microsoft CRM: the Unified Service Desk (USD).  I've had the chance to work with early versions of this product and have been involved in a deployment to a call center, so I have first-hand knowledge and a production deployment under my belt with this offering from Microsoft. Hopefully I can pass some of that knowledge onto others.

From what I've seen online, there's a lot of documentation and articles out there about what USD is and at length about how to use pieces of it, but most of it isn't very user friendly or easy to read.  I'm hoping to change that here.  I won't go into gruesome details about CCA, CCD, UII, and all the technical components in this post; hence the 101 designation in the post title.  I believe that seeing USD in action is the best way to understand how it works, so if you can, I recommend getting a demo site of CRM set up, installing USD, and going back and forth between your installation and this post as you read about the different components.  Of course, you can just read this start to finish and you'll get a lot out of it (I hope), but you'll get more if you get your hands dirty.  Just to give a quick figure, there are literally hundreds of records that are created from this demo data being installed.  You can even use it as a base to install your actual USD implementation.

Before I get into explaining how USD is installed and configured, a quick word on what USD actually is.  USD is a client-hosted application that captures, groups, and displays the web-based Microsoft Dynamics CRM forms.  At the end of the day you're still connecting to your CRM website, but you're doing it through a desktop executable.  Why would you do this?  USD provides many benefits that the website cannot do on its own, listed below:

  • It is easily integrated with Microsoft Dynamics CRM out of the box.  No coding is required to have the desktop application display and interact with CRM.  This makes it a perfect starting point as opposed to making something on your own to integrate with CRM.
  • It can keep track of many sessions at the same time, so users can do work for many different clients at once and their data will stay separated on the screen.
  • It integrates and hosts other applications easily.
  • It provides an easy way to automate processes in both CRM and other systems, as well as transfer information between those systems.
USD does not need to be pigeon-holed into a call center only product, although it does lend itself very well to that use case.  If a CRM implementation also has to interact with many other systems at the same time (ERP, websites, phone software), USD is a great candidate to connect all those applications.  Even if your users are using multiple systems that aren't related to CRM it can be a benefit, as that's where automation comes into play.  For example, say you own a car repair shop.  A caller calls in and says they're still having an issue with their steering alignment.  With one button click in USD, the application can:

  • Bring up customer information
  • Show recent cases in CRM
  • Open a new case for the client
  • Display their open balance in an ERP system
  • Display the manual for that client's car to help a technician fix the problem.


Yes, unlike CRM and most products from the last 5 years, USD is not web-based.  The configuration records are on the web in CRM, but the interpretation of those records is done on the client.  If you don't have any custom integrations, all deployments/updates are done on the server.  If you do have custom components, they're compiled into DLLs and placed in the USD folder.

USD Installation

Installing USD is fairly simple if you follow the installation instructions provided by Microsoft.  I highly recommend installing the demo data that it comes with as it gives you a ton of records to play with.  The installation is in two parts, one for the server and one for the client.  And honestly, using the term 'installation' for both of these pieces is being pretty generous.  The server installation is just a solution install and data import.  The client installation is just copying a bunch of files to a folder of your choosing.  There aren't any files actually 'installed' anywhere else as we know it in terms of most Windows applications.

Configuring USD

Configuring USD is done through creating records within CRM.  When USD loads on a user's machine, after logging in the server will be contacted to retrieve records from the USD entities.  This configuration is made up of 15-20 different entities, many of them obvious but some of them a little more convoluted and harder to understand.  Here I'll go through some of the more basic ones.  Since a call center is touted as the main use of USD, I'll use that as a running theme.

First, what I consider the main four entities:
  • Hosted Controls - The Objects of USD.  If you know Object Oriented Programming, these are the objects.  Most of the 'things' you make in CCD are hosted controls.  This includes your tabs, the debugger, any phone integration visuals, custom components, and even the global manager of USD.  When you have a new USD installation, you need to at least have a Global Manager setup.  Again, though, that's beyond the surface of USD so I'll leave the details of that for another post.
  • Events - Simply, things that happen.  This can be a web page finishing loading, a user clicking a button, or a phone call coming in and being picked up by USD.
  • UII Actions - Reactions to events.  For example, if someone clicks on a button, what happens?  Does a form open?  Is data saved somewhere?  Does a page close?  All of that is handled through actions.
  • Actions - These are the links between Events and UII actions.  You have an Event of clicking a button and a UII Action of saving a record, the Action record would link those two things up so that when the button is clicked, the record is saved.  This way you can use the same action multiple times, maybe you want the record saved automatically if someone closes a form, a button is pressed, or agent scripting is used (more on agent scripting later).



So you have the items, what they do, and how they respond.  You can have multiple actions fire off of one event happening, such as saving a record and then closing a tab.  You can even have actions fire off of other actions.  So if you want to retrieve information and then use that information after it's retrieved, you can set that up.  If you fired both events in parallel there's a chance the data won't be available and the call would fail.

Tying this to the call center example, your hosted control would be a CRM form, say, a Contact entity.  Your Event could be clicking on an activities button to bring up activities related to that contact, the Action would be to show a grid of activities, which would call the UII Action to do the actual display.  Note that the Activities button I mentioned is a USD button, not the one within CRM.  This is  done through toolbars, explained below.

Visual Components:
  • Toolbars - These should look familiar, they're like any toolbar in windows applications.  They're used to hold toolbar buttons which are used to fire events.
  • Toolbar Buttons - As mentioned above, these are used to fire events in USD.  Some common uses are to open Advanced Find in CRM, link to external websites, or open the debugger.
  • Agent Scripting - Helps guide users though the use of USD and CRM.  It gives a script for users to say to users if they're communicating with someone, and they can also help drive business logic through options.  For example, if someone is calling in to order a product vs. asking for support, you would have totally different things happen in terms of what records are created or screens are shown.
  • Session Lines - A 'mini area' of USD, usually used to show often-used information about a customer in the current session (more on sessions later).  If you're in a financial implementation, you can place company name, market cap, stock ticker here.  If you're selling homes, you can place the price of the home and number of bedrooms/bathrooms that a client is interested in.



To keep with the theme of a healthcare call center, in the top left of your USD screen would be a client's name, coverage plan, maybe their deductibles.  That's the session line for this client.  Above your phone call form would be a toolbar and on that toolbar you can have multiple toolbar buttons:  Save, close, create email, open a URL to an external website...anything you'd like.  If on this phone call the user is to determine why the user is calling (maybe they're asking about eligibility, maybe they're asking about changing their primary care physician), you would use Agent Scripting.  The script could contain language telling the user to ask why the caller is calling and then give options.  If a user clicks on one of those options, say the caller is calling about an existing issue, USD can open up a grid showing all service requests that are related to that customer, and possibly open a help website or if there is only one open service request, open that record.

I'll pause here as that should be enough to digest for one post.  I mentioned this at the start but it's that important:  Install the data that comes with the USD download and use it.  Explore it.  Play with it.  Everyone that I've worked with that knows this product, myself included, has learned and gotten used to the product from using it and not just reading about it.

Next time I'll go through more of the entities and components of USD:  Options, Window Navigation rules, Scriptlets, the debuggger, what Replacement Parameters are, and some of the smaller entities involved in configuring USD.  I'll also have future posts on making a custom hosted control and integrating with other applications.

As I'm sure you can tell, this is just scratching the surface of USD. If you have any specific questions or topics you'd like me to go over, feel free to send me an email!

Sunday, November 2, 2014

CRM 2013 SP1 - Debugging Custom Workflows

The topic for today is creating and then debugging a custom workflow for CRM 2013 SP1.  Sounds simple enough, right?  If you have done this type of work in the past it should be straightforward:  You make your DLL though Visual Studio, you deploy your DLL, profile the workflow, make a debug-text file from the profiling, then use the plugin registration tool and debug.

There are a few problems though, given the current (6.1.1) SDK.

First, if you're adding plugins through the plugin registration tool don't use version 6.1.1.  If you do, the tool won't detect your workflows so you can't register them nor debug them (if you managed to get your steps in using Visual Studio).  Find a version of 6.1.0 online and use that instead.

Version 6.1:



Version 6.1.1:



Once you've gotten past that hurdle, you can add the step to your solution and start profiling.  You'll hit another snag later though:

Version 6.1.1:



The profiler won't/can't find which plugin/workflow step you're trying to debug.

Version 6.1.0:



And the details of the error:

"Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Could not load file or assembly 'Microsoft.Xrm.Sdk.Workflow, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified."


This error basically means that the plugin profiler can't find the Microsoft.Xrm.Sdk.Workflow file.  Personally, I've tried placing the file in the location of my DLL, of the plugin registration tool, even where the debug file is (and even putting everything in the plugin registration folder).  To get around this, add the file to your local GAC (global assembly cache).   In case you're not familiar with this, perform the following steps:

  • Open a command prompt as an administrator.
  • Browse to where gacutil.exe file is on your machine.
    • For Windows 7 on my machine, this is at C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools.
  • Register the Workflow DLL that was used to make your workflow step (which should also be the same DLL in the plugin registration folder.
    • Type gacutil.exe -i <location of the file>


And then you should be all set to debug!  Remember to use version 6.1.0 of the plugin registration tool from the SDK, or hopefully 6.1.2 / 6.2 when they come out.

Monday, September 29, 2014

Bulk Delete From Multiple Entities With 1 Button

Over the years I've had the need to delete all records from multiple entities, such as when testing out data migrations or working with the new Unified Service Desktop.  You can use advanced find to find/delete the records or go into the bulk deletion module but then you have to do each entity one at a time and it can quickly become a hassle.

I created a very simple JavaScript web resource to fix this issue which has saved me a lot of time over the last few months as there are a lot of entities that USD works with.  All you need to do is create a web resource that makes a bulk-delete call and call that function with a button in the ribbon (for CRM 2011 that is).

Here's what the button looks like and the popup that's shown, plus the resulting bulk deletion job that's created:




Here are screenshots of the Ribbon Editor tool with the pieces needed to add this customization.  The screenshots below show the setup of this button used to delete data.

First the position of the button and the text:


Here are the button properties:



The command pointing to a JavaScript web resource:




And the rules indicating that the button should only be on the customization and solution pages:

 



Here is the JavaScript called by the button.  You can update the JobName and alert message at the end to be whatever you'd like.

-------------

function getServerUrl()
{
    try
    {
        var ServicePath = "/XRMServices/2011/Organization.svc/web";
        var serverUrl = "";
        if (typeof GetGlobalContext == "function")
        {
            var context = GetGlobalContext();
            serverUrl = context.getClientUrl();
        }
        else
        {
            if (typeof Xrm.Page.context == "object")
            {
                serverUrl = Xrm.Page.context.getClientUrl();
            }
            else
            {
                throw new Error("Unable to access the server URL");
            }
        }
        if (serverUrl.match(/\/$/))
        {
            serverUrl = serverUrl.substring(0, serverUrl.length - 1);
        }
        return serverUrl + ServicePath;
    }
    catch (e)
    {
    }
}

function RemoveData()
{
    try
    {
        var requestMain = ""
        requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
        requestMain += "  <s:Body>";
        requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
        requestMain += "      <request i:type=\"b:BulkDeleteRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
        requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>QuerySet</c:key>";
        requestMain += "            <c:value i:type=\"a:ArrayOfQueryExpression\">";
        requestMain += "              <a:QueryExpression>";
        requestMain += "                <a:EntityName>[Entity1]</a:EntityName>";
        requestMain += "              </a:QueryExpression>";
        requestMain += "              <a:QueryExpression>";
        requestMain += "                <a:EntityName>[Entity2]</a:EntityName>";
        requestMain += "              </a:QueryExpression>";
...
...
        requestMain += "            </c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>JobName</c:key>";
        requestMain += "            <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">Data Bulk Delete</c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>SendEmailNotification</c:key>";
        requestMain += "            <c:value i:type=\"d:boolean\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">false</c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>ToRecipients</c:key>";
        requestMain += "            <c:value i:type=\"d:ArrayOfguid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>CCRecipients</c:key>";
        requestMain += "            <c:value i:type=\"d:ArrayOfguid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>RecurrencePattern</c:key>";
        requestMain += "            <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\" />";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>StartDateTime</c:key>";
        requestMain += "            <c:value i:type=\"d:dateTime\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">0001-01-01T00:00:00</c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "        </a:Parameters>";
        requestMain += "        <a:RequestId i:nil=\"true\" />";
        requestMain += "        <a:RequestName>BulkDelete</a:RequestName>";
        requestMain += "      </request>";
        requestMain += "    </Execute>";
        requestMain += "  </s:Body>";
        requestMain += "</s:Envelope>";
        var req = new XMLHttpRequest();
        req.open("POST", getServerUrl(), true)
        req.setRequestHeader("Accept", "application/xml, text/xml, */*");
        req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
        req.send(requestMain);
        alert("Delete command received.  Data has been queued to be deleted and should start processing shortly.");
    }
    catch (e)
    {
    }
}

----------

And finally, if you can't use the ribbon editor tool and have to rely on old-fashioned XML editing, see below:

Note: [Annotations are in bold] and should be removed/replaced in an actual implementation.
Note:  The org that I’m importing to uses a prefix of ‘hac’ so you see it many times below.


<?xml version="1.0" encoding="utf-8"?>
<RibbonDiffXml>
  <CustomActions>
    <CustomAction Id="mcs.hac.ApplicationRibbon.RemoveCCDData.Button.CustomAction" Location="Mscrm.BasicHomeTab.Tools.Controls._children" Sequence="6">
      <CommandUIDefinition>
        <Button Alt="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.Alt" Command="hac.ApplicationRibbon.RemoveCCDData.Command" Description="Main Text of the Button" Id="hac.ApplicationRibbon.RemoveCCDData.Button" Image32by32="$webresource:hac_/shared/images/recycleBin.png" [This is a custom image I added as a web resource] LabelText="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.LabelText" Sequence="6" TemplateAlias="o1" ToolTipTitle="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipTitle" ToolTipDescription="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipDescription" />
      </CommandUIDefinition>
    </CustomAction>
  </CustomActions>
  <Templates>
    <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
  </Templates>
  <CommandDefinitions>
    <CommandDefinition Id="hac.ApplicationRibbon.RemoveCCDData.Command">
      <EnableRules />
      <DisplayRules>
        <DisplayRule Id="hac.ApplicationRibbon.OnlyInSolutionArea.DisplayRule" /> [This is where I’m setting the button to be shown only when looking at solutions, see the DisplayRule nodes below]
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="RemoveCCDData" Library="$webresource:hac_/shared/RemoveCCDData.js" /> [This is the web resource with the code to execute]
      </Actions>
    </CommandDefinition>
  </CommandDefinitions>
  <RuleDefinitions>
    <TabDisplayRules />
    <DisplayRules> [If you want to have this button shown on other pages, indicate that here]
      <DisplayRule Id="hac.ApplicationRibbon.OnlyInSolutionArea.DisplayRule">
        <OrRule>
          <Or>
            <PageRule Address="/tools/Solution/home_solution.aspx" />
          </Or>
          <Or>
            <PageRule Address="/tools/systemcustomization/systemcustomization.aspx" />
          </Or>
        </OrRule>
      </DisplayRule>
    </DisplayRules>
    <EnableRules />
  </RuleDefinitions>
  <LocLabels>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.Alt">
      <Titles>
        <Title description="Bolded Title of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.LabelText">
      <Titles>
        <Title description="Sub Text of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipDescription">
      <Titles>
        <Title description="Tooltip Description of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipTitle">
      <Titles>
        <Title description="Tooltip Title of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
  </LocLabels>

</RibbonDiffXml>

-------------

Hope this helps save you some time!

Wednesday, April 2, 2014

CRM 2011 - Setting Privacy / Don't Send to Microsoft

When deploying a new CRM implementation, most of the time clients want the default error reporting turned off.  The error message that's displayed to the user is generic and not helpful, and usually the error has nothing to do with Microsoft so the 'Send to Microsoft' button isn't very helpful.

What I've seen done is either one of two things:

A - An Admin will go into the database and write some scripts to automatically set every user's 'choice' to 'Never send an error report to Microsoft.'  This requires direct database access so isn't an option at all for Online, plus it's not a supported modification.

B - Instruct users to go to their options and set this value.  This obviously isn't the best option; Users don't like having to do setup of a system, and some just won't do it.

But then there's the easy way to do this, which most people don't know about since it's tucked away in an area that most people never look:  Privacy Settings.




Once there, click on the 'Error Reporting' tab and you can instantly update the setting for all users (and any future users that are added).



I hope this saves some people deployment time and headaches.  Just remember to turn this off if you actually need to view error logs on a page!

Sunday, March 23, 2014

CRM 2011 Dialogs Without a Record + Some Win32API

If you ever have the need/desire to have a dialog run without having to be under the context of a record, you can do so by creating a button, putting it in a ribbon on the main page of a given (or all) entity, and having it fire the following JavaScript (leave out the < >'s):

http://servername/crm/cs/dialog/rundialog.aspx?DialogId=<GUIDofDialog>&EntityName=<EntityName>&ObjectId=00000000-0000-0000-0000-000000000000

----

On a totally unrelated note, I'm doing some screen-scraping work and sending commands using the Win32API, I figured I'd pass along a little tidbit I've picked up.  If you ever need a quick guide to hex codes for capturing / sending commands:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx

And to go with those, here are some controls (and functions, macros, etc.) that can be used in conjunction with those hex commands:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773169(v=vs.85).aspx

Tuesday, February 4, 2014

Debugging CRM 2011 - Debugging JavaScript Files Loaded Asynchronously

I still use Internet Explorer 8 at times to mimic client environments.  I ran into an issue the other day where I needed to debug some onLoad JavaScript that I wrote on a form.  Since Roll Up 12 changed the loading of JavaScript web resources on forms from synchronous to asynchronous, you can't just edit a file directly in the IE debugger.  First, your JavaScript files aren't in the list of files to debug [my screenshots are on roll-up 15]:


The closest that you can get is in the FormScript.js file, where you can still see the functions that will be fired onLoad:



But if you do try to step into that function on IE8...


Now if you move to Internet Explorer 9 or higher, you can debug.  Remember that every time the form loads it re-loads your JS files, so you can't insert breakpoints as they'll just disappear.  For onChange events you can put breakpoints in your code after the files are loaded and for onLoad you can use the FormScript.js file as shown above.  You just have to go to the 'Other' listing and find out which file your code is in since they're not named like in Roll Up 11 and earlier:


In my case, it's script block 15:


I hope this helps!