A macros system for Dynamics CRM - part three
In part one of this series, I explained the basic idea behind a macros system for Dynamics CRM and a situation in which you'd want to use it instead of a workflow for process automation. I explained the structure of the macros system and showed the CRM entity form customizations that are required to implement it in part two. In this final part I'll show the ASP.Net code that does the heavy lifting for our basic policy renewal example.
The macros page
As I mentioned earlier, we use an ASP.Net Web Forms application to run the macros system, so we need to create a .aspx page for the policy macros. Because we believe in separation of business logic and presentation, our .aspx page requires two separate files, the .aspx page itself and its code-behind.Our .aspx page has two sections. First, there is the list of links that trigger the various macros. It looks something like this:
<asp:LinkButton ID="renewLinkButton" CssClass="text" runat="server" OnCommand="RenewPolicy" CommandArgument="Renew">
Renew policy
</asp:LinkButton>
<br />
<asp:LinkButton ID="anotherLinkButton" CssClass="text" runat="server" OnCommand="SomethingElse" CommandArgument="Renew">
Another macro
</asp:LinkButton>
We make extensive use of macros, which means we've got a lot more links, so we group them into different sections based on the category of business process we're automating. Our macros pages are also styled to look similar to the rest of the CRM interface in terms of colors and fonts, so the end users have no idea that macros are not part of the main application.
The second section is an ASP.Net literal control we use to display a message back to the end user as required. It looks like this:
<asp:Literal ID="reloadLiteral" runat="server" Visible="false">
<script type="text/javascript" language="javascript">
self.parent.location.reload(true);
</script>
</asp:Literal>
This hidden piece of JavaScript is the key to making the macros system work as intended. Once the server-side code executes correctly, the literal content is made visible and causes the user's browser to reload the CRM entity form to show any updates. This is why it's important to make sure your macros iframe is allowed to execute cross-frame scripting.Of course we're not limited to "showing" the reload script. If there's an error, we show a message and don't force the form to reload. We can also use JavaScript to redirect the user to a different page, which is exactly what we do when we renew a policy so the user sees the new policy record without having to go looking for it.
The code-behind
Our macros system doesn't actually do any actual CRM data processing in the code-behind layer. We keep all of that in a separate library (or try to, anyway) for ease of maintenance and testing. This next bit of code presumes you will be encapsulating your own CRM functionality in a similar fashion, but you could just as easily write the CRM code inline. (Disclaimer: this isn't the actual code we use. I've simplified it somewhat for illustrative purposes.)In case you've forgotten from part one, we want the code-behind method to:
- Update the current policy record to show that it was renewed.
- Create a new policy record that is a clone of the previous one with its renewal date pushed ahead one year and some blank fields for the user to supply new commission and premium data.
- Take the user from the old policy form to the new policy form.
protected void RenewPolicy(object s, CommandEventArgs e)
{
//instantiate a new library policy object
CRMLibrary.Policy mypolicy = new CRMLibrary.Policy();
//get the policy's ID from the iframe URL's querystring
Guid policyid = new Guid(Request.QueryString["id"]);
//call the policy renew method
//updates policy record and creates a new one
//returns "failure" followed by a detailed error message on error or the newly created policy ID as a string on success
string message = mypolicy.Renew(policyid); //output is a string because we use strings for plain "success" messages in the majority of the macros system
//if we failed, show the error message to the end user
if (message.StartsWith("failure"))
{
//set the literal value to the error message
reloadLiteral.Text = message;
}
//if it worked, let's redirect to the newly created record
else
{
//build the url to the newly created policy
//note we'll need to know the CRM numeric custom entity code to use in the new URL
string etc = "10010"; //yes, magic numbers are bad; a config file would work nicely.
string newPolicyUrl = "'http://YOUR_SERVER_AND_PORT/userdefined/edit.aspx?id={" + message + "}&etc=" + etc + "'";
//build new javascript to replace the plain reload script with a redirect instead
string redirectScript = "<script language="\"javascript\"" type="\"text/javascript\"">
";
redirectScript += "self.parent.location.href = " + newPolicyUrl + ";";
redirectScript += "
</script>";
//set the literal value to the script text
reloadLiteral.Text = redirectScript;
}
//make the literal visible
reloadLiteral.Visible = true;
}
As you can see, the code here is extraordinarily simple. Once you have the macros structure in place, you can add all sorts of enhanced functionality to your Dynamics CRM deployment.