XRM
February 06, 2012
XRM 2011 JavaScript: Another 101 Lesson in Microsoft Dynamics CRM 2011
When you’re learning JavaScript, you’ll often come across the need to do something to every field on the form. When you do, it’s important to have efficient code to make such widespread changes so the user doesn’t have to wait for the JavaScript to finish. This lesson consists of several parts, some of which you’ll probably already know but hopefully there are some things in here for everyone.
Setting up our Environment
First thing first, open your CRM 2011 development environment and browse to an account form. Once the account form is open, hit F12 on your keyboard. A window should popup that looks like this:
If instead, you see the window in the bottom of your browser page. Click the little “Unpin” button shown here:
This is IE Developer Tools and if you haven’t used this in the past, then I hope you enjoy not having to save/publish/refresh/test nearly as often. To start, we’re going to see how many fields we have on the form. When inside of the CRM form, we’d use something like:
Xrm.Page.data.entity.attributes.getLength()
But when we’re inside Developer Tools, we actually have a different context. So we need to click on the “Console” tab and execute:
frames[0].Xrm.Page.data.entity.attributes.getLength()
Anything we want to execute on the form, we can do by simply prepending “frames[0].” in Developer Tools to get the proper context. This allows us to write and test code instantaneously, which is a huge timesaver.
Iterating Fields
Now that we are playing in our Developer Tools sandbox, let’s iterate through some fields to see which fields are required. To use multiple lines, click the double up arrow in the bottom right corner.
Here’s some slightly modified code from the SDK that we can just copy and paste into our Console:
var message = "The following fields are required:\n";
frames[0].Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
if (attribute.getRequiredLevel() == "required") {
message += " \u2219 " + attribute.getName() + "\n";
}
});
alert(message);
Note: I can’t stress enough how awesome the SDK is. Everyone I’ve met who partakes in the SDK creation, maintenance, etc. have been top notch people. If you aren’t using the SDK, you are missing out in tons of awesome tips, features and customizations.
You’ll notice that the above code quickly goes through each field and checks to see if the field is required. We could also check if the field is dirty (meaning the user changed the value), if the field is enabled, or whatever. It’s just a simple way to make mass changes quickly.
About “forEach”
For the experienced developers, you may be thinking did IE get ForEach? No, but the CRM developers were gracious enough to add this into the Xrm variable. For those of you unfamiliar, this was an old Mozilla thing that made life a lot easier when dealing with arrays. Expanding further, the Xrm forEach approach is faster than most arrays people would generate using for loops. You can write some JScript that is faster and I’ll get into that one day, but at this point you are better off using the forEach approach.
I’ve said it several times, but I really like the Xrm object due to its attention to performance. In fact the method here is about a hundred times faster than iterating the crmForm.all object. I’ll back that up with some pretty graphs and such in a later post.
Checking for Changed (Dirty) Fields
var message = "The following fields are dirty:\n";
frames[0].Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
if (attribute.getIsDirty()) {
message += " \u2219 " + attribute.getName() + "\n";
}
});
alert(message);
Notice how easy it is to change what we are checking for. Whether we are looking for a specific value, dirty fields, required fields, or whatever, doing so inside the forEach function makes it fast and easy.
Back to the Web Resource
To go from the Developer Tools back to the Web Resources, we need to replace all of the “frames[0].” references with blanks. IE Developer Tools is a great way to create and test our code without affecting anyone else and there is a lot more to developer tools than the console. I hope you enjoy!
Posted by Paul Way on February 06, 2012 at 08:25 AM in CRM Best Practices, CRM Development, CRM Javascript, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (1) | TrackBack (0)
January 13, 2012
XRM 2011 - Microsoft Dynamics CRM 2011 Style Buttons
Adding a button to a Form is a great way to add additional functionality right where the user is already focused. Today we are looking at a few different approaches to adding a button. First, we’re looking at handling the upgrade for those of you who have already used the 4.0 button inside of 2011 or if you had a 4.0 environment and are upgrading. If you have a clean 2011 environment, then feel free to skip down to the “From Scratch” section.
There is already some code floating around to create a 4.0 style button inside of CRM 2011. The button we’re creating will instead create a CRM 2011 style button (i.e. “Example Button”).
The CRM 2011 button starts gray and then when the user hovers over it, the button will look like:
Creating our Web Resource
First,name your Web Resource whatever you’d like. In my case, I’m planning to re-use it in various ways, so I’m throwing it in our JS root directory.
1: // CRM 2011 Style Button
2: // Creates a button from a form field
3: // Paul Way - 1/3/2012
4: function ConvertToButton(fldName, btnLabel, btnWidth, evt){
5: var btn = '<button id="btn_' + fldName + '" ' +
6: ' style="width:' + btnWidth + '" ' +
7: ' class="ms-crm-Button" ' +
8: ' onmouseover="Mscrm.ButtonUtils.hoverOn(this);" ' +
9: ' onmouseout="Mscrm.ButtonUtils.hoverOff(this);" ' +
10: '>' + btnLabel + '</button>';
11:
12: var ctrl = Xrm.Page.ui.controls.get(fldName)._control;
13:
14: // Add the new button
15: ctrl.get_element().innerHTML += btn;
16:
17: // Hide the textbox
18: ctrl.get_element().firstChild.style.display = 'none';
19:
20: // Hide the label (optional)
21: Xrm.Page.ui.controls.get('pager').setLabel('');
22:
23: // Add Event to the newly created button
24: ctrl.get_element().childNodes[1].attachEvent('onclick', evt);
25:
26: }
Modifying our Form
To use this new function, you’ll first need to add the newly created web resource to the form. You’ll also want to have a separate JS web resource to call the ConvertToButton function. In my case, I already have a web resource for contact specific JavaScript. If I didn’t, I would need to create a new web resource and then place my contact specific code there. Here’s an example screenshot:
By having two web resources for this, you’ll have only one version of your ConvertToButton function throughout your CRM 2011 environment. Let’s say down the road you’ll need to update the function to change the label to “Button”, then you only need to modify one file. In CRM 4.0, you probably had a lot of duplicate code. With CRM 2011, your browser can cache the JavaScript for better performance and it is easier to maintain when organized appropriately.
In our other web resource, the one we setup with the OnLoad, we need the following code.
1: function contactsOnLoad(){
2: convertToButton('pager', 'Example Button', '150px', function(){alert("test")});
3: }
Why is this so different then the 4.0 code?
If you’ve used the CRM 4.0 code, you’ll notice the code here is a lot shorter. It’s actually pretty different as well.
- For one, instead of modifying the input element we are actually creating a button HTML element. This just means that we can now use about any kind of field instead of just pure textboxes. Not a huge deal, but opens up the email address 3 and other attributes.
- Both are really unsupported but the other uses the deprecated crmForm.all.
- Finally, the button style was meant for 4.0 whereas now we have the 2011 look-n-feel.
From Scratch
So far we’ve mainly focused on if you already were using the 4.0 code and were upgrading. But what if you don’t have the existing code structure? I’d argue against creating a new attribute just to have a button. When adding a new attribute, you are also adding the attribute to the underlying SQL tables and views. Using an existing field isn’t really a great option either because there is always the chance you will need the field or it will overlap with an application from the MS marketplace.
Instead, I’d recommend creating a button place holder web resource and then embedding the web resource on the page. The web resource is just a JPG image like this:
We then place the image wherever we want on the form. Make sure to set the formatting to one column and one row.
Finally, we need a little bit of code added to the /js/formButton.js web resource:
1: // CRM 2011 Style Button
2: // Creates a button from a form field
3: // Paul Way - 1/3/2012
4:
5: function convertWebResourceToButton(fldName, btnLabel, btnWidth, leftMargin, evt){
6: var btn = '<button id="btn_' + fldName + '" ' +
7: ' style="margin-left:' + leftMargin + ';width:' + btnWidth + ';" ' +
8: ' class="ms-crm-Button" ' +
9: ' onmouseover="Mscrm.ButtonUtils.hoverOn(this);" ' +
10: ' onmouseout="Mscrm.ButtonUtils.hoverOff(this);" ' +
11: '>' + btnLabel + '</button>';
12:
13: var ctrl = Xrm.Page.ui.controls.get(fldName)._control.get_element().childNodes[1];
14:
15: // Replace image with buttom
16: ctrl.innerHTML = btn;
17:
18: // Add Event to the newly created button
19: ctrl.firstChild.attachEvent('onclick', evt);
20:
21: }
And as for our OnLoad function, we’ll need to use this instead:
1: function contactsOnLoad(){
2: convertWebResourceToButton('WebResource_btnProfInfo', 'Another Approach', '150px', '119px', function(){alert("test2")});
3: }
Notice that this will require an additional parameter for the left margin.
Summary
As you can see, there are several different approaches to adding a button onto the CRM form for 2011. With 2011, I’d opt for using the button place holder image method. No matter which approach you take, the end goal is enhancing the user’s experience. I hope you enjoy!
Posted by Paul Way on January 13, 2012 at 08:53 AM in CRM Best Practices, CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
January 12, 2012
CRM 2011 - iFrames & Saving
After spending time on several of the CRM forums, I noticed a few people having trouble with iFrames and saving data inside of the iFrame. While a forum post is a little difficult to write a full response, I wanted to share some insight on capturing the form save event to then trigger a save event inside of your iFrame.
From a business case, this is a fantastic way to tie multiple systems together. If all you are doing is syncing data, then look first at a plugin or scribe. However, if you are looking for the user to interact with multiple systems simultaneously, then an iFrame is a wonderful way to integrated with an existing system. A good example might be where CRM is only storing the summary information and the iFrame contains the details. The user may update the details which should save both the CRM record and the details of the web site.
Prerequisites
First, let’s assume you have an iFrame inside an entity.
Secondly, we need to uncheck the “Restrict cross-frame scripting” check box on the iFrame properties.
Lastly, it’s a good idea to make sure our iFrame is using HTTPS to prevent mixed mode security warnings.
Talking Directly
If you try to talk directly to the iFrame via something like:
Xrm.Page.ui.controls.get('IFRAME_opp').getObject().contentWindow.document
Unless you are On-Premise, you may encounter the “Access is Denied” warning. This will show itself when dealing with URLs on a different domain (or subdomain). Cross domain scripting is a security feature within the browser. Now an easy fix is to disable the browser settings, but this isn’t a good solution.
Messaging
Instead of talking directly, we need send a messages to our iFrame. To send a message, we’d do something like:
Xrm.Page.ui.controls.get('IFRAME_opp').getObject().contentWindow.postMessage('test', "*")
To receive the message on the iFrame, you need to listen for the message event. Let’s take a look at the JavaScript on our example iFrame page:
1: function receiveMessage(e) {2:3: if (e.origin == 'https://way.crm.dynamics.com'){4:5: if (e.data == 'test'){6: document.getElementById('msgBox').innerHTML = 'testing...<br />' + e.origin + '<br />' + e.data;7: }8:9: if (e.data == 'save'){10: saveForm();11: }12: }13:14: }15:16: saveForm = function() {17: document.getElementById('msgBox').innerHTML = 'saving...';18: }19:20: window.attachEvent("onmessage", receiveMessage);
It is very important to check the origin and the message if you are dealing with publicly facing sites.
If the idea of messaging is new to you, here are some additional resources:
- http://msdn.microsoft.com/en-us/library/cc197015(VS.85).aspx
- http://msdn.microsoft.com/en-us/library/cc511311(v=vs.85).aspx
The OnSave
To initiate the message from the CRM 2011 form, you need to register an OnSave event from within the “Form Properties”.
How about .NET Pages?
If your iFrame is an ASPX page, all of this still applies. The only difference is that you want to have an ASP.Net button on your page to call the server side code. Instead of the saveForm event, simply call the click event of the button.
When dealing with server side saves though, you can either post a message back to say “I’m done” which will then complete the CRM save or you can put a set timeout in the onSave on the CRM side.
Conclusion
Hopefully you have seen how easy it is to integrate with another web site while inside of CRM 2011. If you have had problems with iFrames in the past, I really encourage you to use iFrames for your integration needs as they can really enhance your users experience and can allow for improved data across systems. I hope you enjoy!
Posted by Paul Way on January 12, 2012 at 01:22 PM in CRM Javascript, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Online, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (2) | TrackBack (0)
January 03, 2012
Microsoft Dynamics CRM 2011 – Pre-Populated Emails (Alternative Approach)
Recently, we came across the need for a single-click email feature on the ribbon of a custom entity. The idea was to be able to share CRM data as quickly and easily as possible. Another interesting caveat was the client wanted to have the email come directly from Outlook.
For those of you old enough to have used the internet before the iPad, you may recall that fancy-dancy “mailto” feature. (Granted I’m only 29 but my Omron body age says I’m 50 ).
Back to the point at hand though, CRM already has a few built in email features. Our situation calls for something a little different.
What’s Installed By Default
CRM already has the feature of sending a link to the CRM entity you’re working with.
CRM also has a really nice Email Template feature.
Which can automatically mail-merge information pertaining to the entity to eliminate any of that “automated” feel.
What We Need
Our situation is somewhat unique, but really we want both features combined. We want to open a new message like the link feature: inside outlook and with one click. However, we need more than just the link. We want to have a brief summary of the call report with a link to it.
Here’s an example email of what we’d like to send:
Adding the Button
The first thing we need to do is add the button to the ribbon like so:
If you are unfamiliar with the ribbon, Microsoft offers some really nice walkthroughs. I’d start with http://msdn.microsoft.com/en-us/library/gg334341.aspx.
For our situation, the XML that matters is our Action
<Actions> <JavaScriptFunction Library="$webresource:cei_example.js" FunctionName="emailContact" /></Actions>
Adding our JavaScript
At this point, the button should be on the form. From here, we need to add the JavaScript to open our populated email message.
To do this, add to the Form OnLoad events the following function:
emailContact = function() {
if (Xrm.Page.data.entity.getId() != null) {
var sRptID = Xrm.Page.data.entity.getId().replace('{', '').replace('}', '');
var sLink = "http://paul.customereffective.com/userdefined/edit.aspx?id=" + sRptID + "&etc=10011";var sSubject = 'Contact - ' + Xrm.Page.getAttribute("firstname").getValue() + ' ' + Xrm.Page.getAttribute("lastname").getValue();
var sCustomer = '';
if (Xrm.Page.getAttribute("parentcustomerid").getValue().length > 0){
sCustomer = Xrm.Page.getAttribute("parentcustomerid").getValue()[0].name;
}
var sBody = '---\n\nCustomer:\t' + sCustomer + '\n\n' + sLink + '\n\n---';
var sMailTo = 'mailto:?subject=' + escape(sSubject) +
'&body=' + escape(sBody);parent.location=sMailTo;
} else {
alert('You must save the contact before emailing.');
}
}
Note: If you change the name of the function, make sure it matches the name inside your Ribbon XML.
What about Window.Open
If you use Window.Open to trigger the email (or OpenStdWin), the user will get a blank IE page inside of Outlook. By using the parent.location for your mailto, you’ll have a consistent user experience across IE and Outlook.
Seeing the Final Results
Now whenever a user wants to share a call report, they can easily hit the “Email Call Report” button. Add any of the fields you want.
For our needs I ended up with the following; however, this technique can add whatever field you’d like.
Well this isn’t a tool in the toolbox I’d use daily, it can be valuable when the situation calls for it. I hope you enjoy!
Posted by Paul Way on January 03, 2012 at 01:58 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM for Outlook, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
December 23, 2011
Dynamically Generating URLs for Cached Web Resources for Microsoft Dynamics CRM
CRM 2011 supports several methods for accessing web resources. These includes using the $webresource: directive when referencing web resources from the site map or ribbon, a relative URL, or an absolute URL. While this allows a web resource to be accessed in virtually any situation, if a scenario arises in which an absolute URL must be used, the benefit and speed of retrieving the cached web resource is lost. The reason for this has to do with the caching mechanism used by CRM for web resources. Take the following URL that could be generated by CRM when using the $webresource: directive or when a web resource is included on a CRM form as an example.
https://yourorgname.crm.dynamics.com/%7B634594655730000000%7D/WebResources/cei_/test.htm
Notice the magic number that is highlighted in the example URL. CRM automatically inserts this token into the URL and the CRM 2011 SDK indicates that this magic number is a GUID value that ensures the latest cached version of the requested web resource is used. If you use a tool like Fiddler to examine the response returned by the GET request for the web resource, you will notice that it has caching headers that set it to be cached for one year. If the magic GUID value is removed from the URL so that the URL would resemble an absolute URL to the web resource, the response returned by the GET request for the web resource, depending upon your version of CRM, will be a non cached version or a version that is cached for a short amount time such as 24 hours.
This caching functionality provided by CRM is great, but what if you need to dynamically generate the URL for a large web resource? It would be preferable to access the cached version, but in order to do that you need the magic GUID value. Fortunately, the magic GUID value is not magic at all, and not actually a GUID. The caching token inserted into the URL for the web resource by CRM is merely a timestamp that represents the modified time of the last web resource to be modified. The timestamp is represented as ticks in UTC time as generated by the .NET Framework.
Now in order to get the URL for a web resource, it is merely necessary to generate this timestamp. The example function below shows exactly how to generate the timestamp for the caching token using a quick call to the REST service in CRM and a simple conversion from the JavaScript Date object to the timestamp value.
1: function getWebResourceUrl(webResourceName) {
2:
3: var dotNetMillisecondsAt_1970_01_01 = 62135596800000,
4: ticksPerMillisecond = 10000,
5: lastWebResourceModifiedOn = null,
6: webResourceCachingToken = null,
7: webResourceUrl = "/WebResources/" + webResourceName;
8:
9: // Retrieve the modified date of the last web resource to be modified
10: $.ajax({
11: async: false,
12: beforeSend: function (xhr, settings) {
13: xhr.setRequestHeader("Accept", "application/json");
14: },
15: contentType: "application/json; charset=utf-8",
16: datatype: "json",
17: global: false,
18: success: function (data, textStatus, xhr) {
19:
20: // Extract the modified date from the results
21: lastWebResourceModifiedOn = +(/\/Date\((\d*)\)\//.exec(data.d.results[0].ModifiedOn)[1]);
22:
23: // Convert the JavaScript Date value to the caching token value
24: webResourceCachingToken = (lastWebResourceModifiedOn + dotNetMillisecondsAt_1970_01_01) * ticksPerMillisecond;
25: },
26: type: "GET",
27: url: Xrm.Page.context.prependOrgName("/XRMServices/2011/OrganizationData.svc/WebResourceSet()?$select=ModifiedOn&$orderby=ModifiedOn%20desc&$top=1")
28: });
29:
30: // If we have a caching token value then prepend it to the current URL.
31: if (webResourceCachingToken !== null) {
32: webResourceUrl = "/%7B" + webResourceCachingToken + "%7D" + webResourceUrl;
33: }
34:
35: // Return the URL to the web resource prepended with the organization name if necessary
36: return Xrm.Page.context.getServerUrl().replace(/\/$/,"") + Xrm.Page.context.prependOrgName(webResourceUrl);
37: }
Notice the function takes the name of a web resource, e.g. cei_/mypage.htm, and prepends it with the caching token, organization name if necessary, and the server URL. Now this function can be used to dynamically generate the URL for a web resource and still retrieve the cached web resource. Of course, there could be some improvements made, such as wrapping the call to retrieve the caching token in a closure so that it is only retrieved the first time a URL is generated, but I will leave that up to you.
**Note, that generating a URL in this manner is not supported by Microsoft and the caching mechanism used by CRM for web resources could be changed in the future.
Posted by Nick Doriot on December 23, 2011 at 09:28 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
December 16, 2011
Setting Lookup Field Values on Microsoft Dynamics CRM Forms with Silverlight
Recently, I have found myself developing a lot of Silverlight customizations for CRM 2011. If you happen to find yourself in the realm of Silverlight and CRM you will more than likely have the need to interact with the CRM form in context. The tool that Silverlight provides for interacting with the hosting web page is the HTML Bridge. The types and methods exposed by the HTML Bridge allow access to the hosting pages DOM and JavaScript objects. By utilizing the types and methods of the HTML Bridge it is possible to access the Xrm JavaScript object of the CRM SDK to set field values so let’s take a look at how the HTML Bridge can be leverages to do so.
There are a couple of different methods that can be used when accessing the Xrm object of CRM. One is to create an object model around the Xrm object that mirrors the Xrm object in JavaScript and the other is to use a dynamic object in Silverlight. I am going to use a dynamic object in this instance as it requires much less code, however, the downside is that there is not any compile-time type checking and errors may not be discovered until runtime.
1: // Get a reference to the Xrm object
2: dynamic Xrm = HtmlPage.Window.GetProperty("Xrm");
3:
4: // Set a string field value
5: Xrm.Page.getAttribute("cei_stringfield").setValue("Hello World!");
6:
7: // Set a floating point field value
8: Xrm.Page.getAttribute("cei_floatfield").setValue(3.14159);
Notice in the previous example I simply use the HtmlPage.Window object to get a reference to the Xrm property on the hosting page. Also, notice that the local Xrm object is declared as a dynamic variable. Declaring the local Xrm object as a dynamic object allows the object to bypass static type checking. At this point, it is simply a matter of using the Xrm object just as if it were being used from JavaScript. In line 5 of the previous code snippet I have done just that. A reference to the cei_stringfield attribute is obtained and the value is set to a “Hello World!”. In line 8, the same approach is used to set a numeric field.
After seeing how the previous snippet of code works, you may think, as I did, that setting a lookup field’s value could be done using the same technique with an anonymous array object that mimics a lookup fields value as the argument to the setValue function. However, calling the setValue function for a lookup field will not work from within Silverlight due to the way the HTML Bridge marshals managed objects to JavaScript. Fortunately, there is another method that can be used, although it is not quite as elegant. The following snippet shows how a lookup field can be set from within Silverlight.
1: private void SilverlightSetLookupValue(string fieldName, Guid? id, string entityLogicalName, string name)
2: {
3: // Define eval statements for setting lookup to a value and null
4: string setLookupJscript = @"Xrm.Page.getAttribute(""{0}"").setValue([ {{ id: ""{1:B}"", typename: ""{2}"", name: ""{3}"" }}])";
5: string setLookupToNullJscript = @"Xrm.Page.getAttribute(""{0}"").setValue(null)";
6: string evalStatement = null;
7:
8: // Set the statement to be evaluated based upon the value of the id argument
9: if (id.GetValueOrDefault().Equals(Guid.Empty))
10: {
11: // Setting the lookup to null
12: evalStatement = string.Format(setLookupToNullJscript, fieldName);
13: }
14: else
15: {
16: // Setting the lookup to a value
17: evalStatement = string.Format(setLookupJscript, fieldName, id, entityLogicalName, name);
18: }
19:
20: // Set the lookup
21: HtmlPage.Window.Eval(evalStatement);
22: }
That’s right, I broke out the big dog, the EVIL EVAL function. As I said previously, it is not the most elegant solution, however, sometimes the heavy hitters like the eval function need to be dusted off and it works beautifully. As you can see, the method in the snippet builds the JavaScript necessary to set a lookup field dynamically as a string and then evaluates the string on the hosting page using the HtmlPage.Window.Eval method within Silverlight.
Posted by Nick Doriot on December 16, 2011 at 09:55 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
December 08, 2011
Using the CRM 2011 REST Services and Deep Insert to Create Activities
One of the nice features of CRM 2011 is the REST based OrganizationData service. Lately, I have found myself using the REST service more often as it provides a simpler and cleaner method of accessing data from client side JScript. The REST service also provides the ability to do deep inserts of entity and related records. This capability can be leveraged to provide an elegant solution to creating activity records using client side JScript.
In my particular use case, I needed to create a new email activity from the currently logged in user with the regarding record set to a new custom entity record. It would have been possible to issue a couple of calls to the REST service that created the regarding record and then the email, but it is just as easy to create the email and related records in one call avoiding the need to clean up any records should one of the calls fail.
The first thing that needs to be done is to create the activity party record for the from field on the email. The activity party is a separate entity that stores all of the parties related to an activity, i.e. sender, recipient, etc. The activity party data needs to be specified as an array of activity party entities. The following snippet creates an activity party record for the from field on the email.
1: // Create the activity party records
2: var emailPartyEntities [{
3: PartyId: { LogicalName: "systemuser", Id: Xrm.Page.context.getUserId() },
4: ParticipationTypeMask: { Value: 1 }
5: }];
As I mentioned earlier, I also needed to create a custom entity record that will be set as the regarding on the created email. In my case, this custom entity is used to track an email thread. Since this is a N:1 relationship for the email entity, the record created does not need to be stored in an array and is merely an object with properties specifying the fields of my custom entity.
1: // Build the activity thread entity
2: var activityThreadEntity = {
3: cei_Department: { Value: 29669000 },
4: cei_Subject: "Activity Thread for Account",
5: };
Finally, I need to build the email entity that will be passed to the REST service during the create request. In order for the deep insert to work, the activity party array and the activity thread object created earlier will need to be set to properties on the created object that specify the relationships to the related entities as shown below.
1: // Build the email record
2: var emailEntity = {
3: Subject: "Test Email",
4: cei_activitythread_Emails: activityThreadEntity,
5: email_activity_parties: emailPartyEntities,
6: };
In the previous snippet, notice that the activityThreadEntity object and the emailPartyEntities array are set to their corresponding relationships, i.e. cei_activitythread_Emails is the name of the N:1 relationship for my custom entity to the regarding field on the email entity, and email_activity_parties is the name of the 1:N relationship from the email entity to the activity party entity.
Now that all of the required objects have been created we can issue the call to the REST service to create the email and related records. The following snippet utilizes a jQuery plugin from our custom JScript library. It is merely doing a POST request to the REST service with the data for the POST being the JSON serialized emailEntity object that was created.
1: // Create the email by POSTing the data to the REST service
2: var createResult = $.xrm.orgData.buildRequest().
3: async(false).
4: dataset("Email").
5: data(emailEntity).
6: create();
7: if (createResult.errorExists) {
8: alert("There was an error creating the email.");
9: } else {
10: alert("Success!");
11: }
That’s it! It really is that simple. The email, activity party, and regarding records were all created in a single call.
In this example, I utilized the deep insert capabilities of the REST services to create an email, but the deep insert feature can be used on any entity in order to create multiple records in a single call. The biggest hurdle that you will probably run into is determining whether to create the related records as JScript objects or arrays of objects. Just remember that 1:N relationships require and array of objects and that N:1 relationships require a single object.
Posted by Nick Doriot on December 08, 2011 at 08:05 AM in Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (1) | TrackBack (0)
October 26, 2011
CRM 2011 and Windows Identity Foundation
This is a quick reminder that if you are deploying custom applications on servers other than where you install CRM, you need to manually install WIF (Windows Identity Foundation). These pieces are not included in the .Net Framework 4. Here is a sample error message you may receive if you run a custom application without WIF installed.
You can download and install the appropriate WIF from here. I also noticed that Microsoft upgraded the download site for this piece. You no longer need to know what OS you are on to download the appropriate file.
Posted by Sean Shilling on October 26, 2011 at 03:30 PM in CRM Development, Microsoft CRM Customizations, XRM | Permalink | Comments (0) | TrackBack (0)
October 25, 2011
Microsoft CRM 2011 -- When to Use an iFrame vs a Web Resource
Sit back and relax a bit because we aren’t going to get into any code today. Instead we are going to take a higher level look at some interesting caveats within CRM 2011.
Even if you aren’t a developer, if you customize the form with Web Resources or iFrames, this should be helpful to you.
Silverlight
First of all, I came across this due to a colleague using Silverlight. Like many of you out there, we do some crazy stuff with Silverlight. Silverlight is awesome and can be leveraged to do some serious heavy lifting. I’m actually more of a JavaScript guy, but until JavaScript supports multi-threading, Silverlight cannot be overlooked.
When using Silverlight, you have three options to put Silverlight on your form:
- Embed a Web Resource and select the XAP file (Figure A – 1)
- Embed a Web Resource and select an HTML wrapper (Figure A – 3)
- Embed an iFrame and enter an HTML wrapper (Figure A – 4)
Figure A (Before)
If you are embedding the XAP file (1), then all is good in the world and everything should behave like any other control. Problems only arise with the magnitude of what you are doing. Let’s say you have a couple of JavaScript resources you need to support your Silverlight control to talk to the form. If that’s the case, setup and maintenance is a little more cumbersome.
To make life easier for you and your customer, you could upload your own HTML file with everything self-contained. Now that you have everything nicely contained in an HTML web resource, we can either insert a web resource and select our HTML file (3) or we can use an iFrame and point to our HTML file (4). What’s the difference? Watch what happens when I select a Related entity (e.g. Activities) and then come back to my form. Notice how the Web Resource is blank? That’s because the form reloaded my web resource.
Figure B (After)
HTML
Ok, some of you out there may be saying, “but I don’t use Silverlight”. That’s fine, but this actually applies to HTML files as well. When embedding an HTML page, again you can either use a web resource or an iFrame. If you choose a web resource however, the page will reload when toggling to a new related entity. If you choose an iFrame, it will NOT reload (look back at Figure A&B paying attention to 2 and 5).
Well why?
This is all about your purpose. If you have a HTML page or Silverlight control that is read-only (e.g. graphs, charts, or accessing another system), then a Web Resource would be the better method most of the time. If you are expecting users to input data, then you’ll want to use an iFrame instead of a Web Resource so that the data is persistent. For some of you this could be a gotcha and for others it can be a feature. Hopefully none of your users are on the wrong end of this! Enjoy.
Posted by Paul Way on October 25, 2011 at 07:43 AM in CRM Best Practices, CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
August 29, 2011
CRM 2011 Form Customization (Part 2)
A while back we looked at customizing the CRM 2011 header and footer. I got some really positive feedback, but some people were tripped up. Troubleshooting via blog is difficult, so whenever you have problems feel free to tweet me (@paul_way). Today, I’m going to go into great detail of changing the form again, but this time we’re going to add a new twist. Take a look at the before and after:
Before
After
Now some of you may be wondering what I changed. First, we still have the light blue shading on the header. Secondly, we moved the footer to the left navigation. User’s may never request changing the color of the forms, but that was intended to show you that you could style the forms any which way you wanted. Moving the footer to the left hand navigation is quite practical. Some users may be faced with smaller screen resolutions and this will really free up space if you intend to put a lot of information in the footer (especially if you are adding to the header already).
Step-by-Step
Setting up our Web Resources
The first thing you’ll need to do is create a solution. I called mine “Form CSS Customization”. In it we are going to add two web resources.
For our initial web resource we’ll create is a CSS file:
.ms-crm-Form-HeaderContainer {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffB3CAEC, endColorstr=#fff6f8faE)
}
.ms-crm-Form-Footer {
display: none;
}
.ftLabel {
color: #999;
font-style: italic;
padding-left: 4px;
}
.ftValue {
padding: 2px 2px 8px 12px;
text-align: left;
}
#myCustomNav { padding: 2px }
.myNavFoot {
margin-left: 4px;
border: solid 1px #999;
width: 99%;
}
Take note of the name of your CSS file, as we’ll use this in a minute. The CSS file contains the shading on the header as well as some custom table formatting that we’ll use with our JavaScript file.
Now, let’s add our JavaScript file:
function load_css_file(filename) {
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
document.getElementsByTagName("head")[0].appendChild(fileref)
}function myFormOnLoad() {
load_css_file('/WebResources/cei_/css/case.css'); // don’t forget to change the file!
moveFooterToNav();
}function moveFooterToNav() {
var tbl = document.getElementById('crmNavBar');
var row = tbl.insertRow(tbl.rows.length);
var cFoot = row.insertCell(0);
var txt = document.createTextNode('testing...');
cFoot.appendChild(txt);
cFoot.id = 'myCustomNav';
document.getElementById('myCustomNav').innerHTML = "<table class='myNavFoot' cellspacing='0' cellPadding='0'>" +
"<tr><td class='ftLabel'>Created</td></tr>" +
"<tr><td class='ftValue'>" + document.getElementById('footer_createdby_d').childNodes[0].innerHTML +
"<br />" + document.getElementById('footer_createdon_d').childNodes[0].innerHTML + "</td></tr>" +
"<tr><td class='ftLabel'>Modified</td></tr>" +
"<tr><td class='ftValue'>" + document.getElementById('footer_modifiedby_d').childNodes[0].innerHTML +
"<br />" + document.getElementById('footer_modifiedon_d').childNodes[0].innerHTML + "</td></tr>" +
"</table>";}
The JavaScript file is doing two important things. First, it is injecting the CSS file into the header of the page. This basically tells the browser to pickup the CSS file as if it was always part of the page. After loading the CSS file, the JavaScript will append a table to the left area nav bar. This table is completely custom and you can style it any which way you want. Be careful using Xrm.Page.getAttribute() here, because even though the field is in the footer it may not be accessible to the Xrm object. In my case, the attributes I’m using were not on any of my tabs.
Changing the JavaScript
You must change the “load_css_file” function to contain your specific web resource. To find the web resource name, it’s easiest to look at the CSS web resource inside of CRM:
Configuring the Form
Now we need to go to the form we are customizing. The first step is to add everything we want to onto the footer.
Next, we’ll edit the form properties.
When editing the Form Properties, we’ll need to add the JavaScript library and add our onLoad function to the Form OnLoad. In our example, the function we’re using is called “myFormOnLoad”.
After this, we simply hit OK, Save, and Publish.
Reviewing the Finished Product
As you can see, we made subtle changes to improve the overall look and feel for the user. We also provided a real-life situation where you may need to squeak out some additional room for the user. Hope you enjoy!
Posted by Paul Way on August 29, 2011 at 03:58 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
August 23, 2011
CRM 2011–Changing the Form Header and Footer Colors
CRM 2011 has a beautiful default theme. Microsoft has already organized the CRM 2011 folder structure to support themes, but currently only supplies the Silver theme. Office 2010 on the other hand contains Silver, Blue, and Black.
Today we aren’t going to modify the entire theme, but modify the form header and footer. The code I’m going to include is a small gradient, but you could choose a solid background or any color you’d like. I was going to use a Clemson, I mean Customer Effective, Orange, but instead focused more on the blue theme look.
Before
After
Adding our CSS Web Resource
Granted this change was very minor, but you could do about anything you wanted to with it. To accomplish this basic change you must first upload a CSS web resource (I named mine new_header.css) with the following styling:
.ms-crm-Form-HeaderContainer{
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffB3CAEC, endColorstr=#fff6f8faE);
}.ms-crm-Form-Footer{
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffB3CAEC, endColorstr=#fff6f8faE);
background-image: none;
}
Note: The filter property is an IE specific CSS attribute. It isn’t supported in any other browsers (they will just ignore it), but Internet Explorer 7+ supports it just fine.
Modifying the OnLoad
Our next step is to modify the onLoad to inject the CSS file. We’ll do this by adding the following JavaScript to a web resource:
function load_css_file(filename){
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
document.getElementsByTagName("head")[0].appendChild(fileref)
}function myFormOnLoad(){
load_css_file('/WebResources/new_header.css');
}
Changing the Form Properties
Finally we need to change the form properties to include this JavaScript web resource in the library and add our “myFormOnLoad” function to the OnLoad Event for the Form:
Conclusion
After going through this blog, hopefully you will see how easy it is to change the form header, footer, or anything else. By embedding a CSS file, our ideas are our only limits. Hope you enjoy!
Posted by Paul Way on August 23, 2011 at 08:00 AM in Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (4) | TrackBack (0)
Technorati Tags: CRM, header, Microsoft CRM, ribbon, ribbon color
July 25, 2011
Using FetchXML Aggregation to Create a Rollup Plugin in Microsoft Dynamics CRM 2011
One of the most common requirements for a CRM implementation is for a rollup of data on a set of child records to the parent record. CRM has several built in entities, e.g. Orders and Quotes, that implement rollups, but it does not natively support rolling up of fields for custom entities or for customizations of existing entities. Fortunately, it is quite easy to implement a rollup using a simple plugin. Generally, implementation of the rollup involves retrieving all of the child records related to the parent when one of the child records is created, updated, or deleted, calculating the rollup value and updating the parent. This can be done by iterating through all of the child records after retrieving them using the organization service, but why not make things a little easier and use the new aggregation features of FetchXML to calculate the rollup value.
Aggregation support was added to FetchXML with the release of CRM 2011 and it supports the basic aggregation operations of sum, average, minimum, maximum, and count. The query can be built dynamically in the plugin which allows us to create a generic configurable plugin that will work for most standard rollups.
The first piece of code that we will need is a method to build the aggregation query dynamically. The following method will allow us to build the needed query.
1: public string BuildRollupQuery(string rollupEntity, string rollupAttribute, string aggregateOperation, EntityReference parentLookup) {
2:
3: return string.Format(
4: "<fetch distinct='false' mapping='logical' aggregate='true'>" +
5: "<entity name='{0}'>" +
6: "<attribute name='{1}' aggregate='{2}' alias='{1}_rollup' />" +
7: "<filter type='and'>" +
8: "<filter type='and'>" +
9: "<condition attribute='{3}' operator='eq' value='{4:B}' />" +
10: "</filter>" +
11: "</filter>" +
12: "</entity>" +
13: "</fetch>",
14: rollupEntity,
15: rollupAttribute,
16: aggregateOperation,
17: parentLookup.Name,
18: parentLookup.Id);
19: }
This method will allow us to dynamically create the rollup query given the parameters that define the child entity and attribute that is being rolled up, the rollup operation to perform, and the lookup field to the parent of the child. The query will aggregate the rollup attribute for all of the child records related to the parent.
Using this query we can call the following method to execute the query using the organization service and return the rollup value.
1: public object GetRollupValue(IOrganizationService orgService, string rollupQuery, string rollupAttribute)
2: {
3: return orgService
4: .RetrieveMultiple(new FetchExpression(rollupQuery))
5: .Entities
6: .First()
7: .GetAttributeValue<AliasedValue>(rollupAttribute + "_rollup")
8: .Value;
9: }
As you can see, this method executes the query by passing it to the RetrieveMultiple method of the organization service and retrieves the rollup attribute returned from the first record returned by RetrieveMultiple. Notice that the attribute value is returned by RetrieveMultiple as an AliasedValue. This is how all aggregate attributes are returned by the RetrieveMultiple method. I have made a few assumptions with this method for simplicity. For example, if the attribute being rolled up, the Value property of the returned AliasedValue attribute would be returned as a Money object. If that were the case it would be necessary to return the Value property of the Money object. As I mentioned, for simplicity I have assumed that the attribute being rolled up is a simple type, i.e. decimal, int, or double.
We now have the value for our rollup and simply need to update the parent object. Finally, we can make a call to the organization service to perform the update using the following method.
1: public void UpdateParentRollupField(IOrganizationService orgService, EntityReference parentLookup, string parentRollupField, object rollupValue)
2: {
3: Entity rollupParentEntity = new Entity(parentLookup.LogicalName);
4: rollupParentEntity.Id = parentLookup.Id;
5: rollupParentEntity[parentRollupField] = rollupValue;
6:
7: orgService.Update(rollupParentEntity);
8: }
At this point we have all of the tools needed to create the rollup plugin. In order for the plugin to operate properly and efficiently it is necessary to add a few checks and images to the plugin. For example, it is only necessary to update the rollup if the parent lookup field was changed or the field being rolled up on the child record was changed. These values can be checked by examining the InputParameters object that is passed to the plugin. It will also be necessary to check to see if the parent lookup field for the rollup has changed. If it has changed it will be necessary to update the rollup field on the old parent and the new parent. The old parent lookup field value can be retrieved from a Pre Image entity that is registered for the update and delete messages. The following snippet shows an example of the required checks and assumes that the Pre Image registered for the plugin is called “Target”.
1: Entity targetInput = context.InputParameters["Target"] as Entity;
2: Entity targetPreImage = context.PreEntityImages["Target"] as Entity;
3:
4: bool parentLookupChanged = targetInput.Contains(parentLookupFieldName);
5: EntityReference parentLookupOldValue = targetPreImage.GetAttributeValue<EntityReference>(parentLookupFieldName);
6:
7: bool rollupFieldChanged = targetInput.Contains(rollupFieldName);
All that remains at this point is to wrap the code up in a plugin implementation and call the methods as needed. The plugin can be made configurable by setting a configuration when registering the plugin using the plugin registration tool supplied with the SDK and retrieving the configuration string from the configuration argument passed to the plugin constructor. Happy coding!
Posted by Nick Doriot on July 25, 2011 at 08:48 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (3) | TrackBack (0)
July 20, 2011
Creating Links to CRM Records from External Systems Using the CRM 2011 REST Services
A common scenario with a CRM implementation includes the need to link to CRM from an external system. The CRM SDK actually describes the URL format for accessing CRM Forms using a URL at the following link, Open Forms, Views, and Dialogs with a URL. Great, problem solved! However, if you take a close look at the URL for a CRM Form you will see that it takes the following format.
http://<mycrm>/<myOrg>/main.aspx?pagetype=entityrecord&etn=<entityLogicalName>&id=<entityId>
As you can see, the problem that presents itself is the need for the GUID of the record. However, quite often the external system/page linking into CRM may not have access to the GUIDs for CRM records. One solution to this problem is to use a tool to get the GUIDs for the CRM records into the external system. A better solution is to leverage CRM Web Resources and the REST services to create a HTML page that can redirect the user to the proper record given some identifying information from the external system, such as an external identifier from the system that is synchronized and stored in CRM.
The first thing needed is to create a HTML Web Resource within CRM. This is a very simple page that merely lets the user know that they are being redirected to a CRM record.
1: <html>
2: <head>
3: <title>Query Redirection</title>
4: <script src="../scripts/json2.min.js" type="text/javascript"></script>
5: <script src="../scripts/jquery_1.6.1.min.js" type="text/javascript"></script>
6: <script src="../scripts/jquery.xrm.orgData_1.0.1.min.js" type="text/javascript"></script>
7: <script src="../../../../ClientGlobalContext.js.aspx" type="text/javascript"></script>
8: <link href="../styles/RedirectQuery.css" rel="stylesheet" type="text/css" />
9: </head>
10: <body>
11: <table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
12: <tr>
13: <td id="contentContainer" align="center" valign="middle">
14: <div class="content">
15: <img src="../images/spinner_circle.gif" />
16: <div>Searching for record...</div>
17: </div>
18: </td>
19: </tr>
20: </table>
21: <script src="../scripts/RedirectQuery.js" type="text/javascript"></script>
22: </body>
23: </html>
As you can see, I have included some JavaScript files to make things a little easier when calling the REST services. Also, notice the inclusion of the ClientGlobalContext.js.aspx script. This script file gives the Web Resources the needed XRM context, such as the Xrm.Page.context object. The last script included at the bottom of the page, i.e. RedirectQuery.js is where all of the magic is going to happen. Let’s take a look at what this script needs to do.
The RedirectQuery.js script is going to take a REST query that is passed as a query string parameter, execute the query, extract the logical name of the entity and the ID (GUID) of the record from the result, and redirect the user to the CRM Form for the record.
The first piece to this puzzle is to get the REST query from the query string. HTML Web Resources in CRM are only allowed to have a single query string parameter called “data”, so the REST query is going to be encoded in this query string parameter.
1: // Get the query parameters data
2: queryData = decodeURIComponent(getQueryParam("data"));
Now that we have our REST query we can execute it using a bit of jQuery and a homegrown jQuery plugin that wraps up the calls to the REST services.
1: // Get the data for the target record
2: targetData = $.xrm.orgData.retrieveMultiple({ async: false, query: targetQuery });
The next step is to extract the logical entity name from the original query and the ID (GUID) of the entity from the first record in the results of the REST query.
1: // Get the entity type name from the supplied target query
2: targetEtn = targetQuery.match(/^([a-zA-Z0-9_]+?)Set/)[1];
3:
4: // Extract the ID from the data
5: targetId = targetData[targetEtn + "Id"];
Finally, we can build the URL to the CRM Form with the data we have extracted from our original query and the query’s results and redirect to the CRM Form.
1: // Build the URL
2: targetUrl = Xrm.Page.context.getServerUrl().replace(/\/$/, "") + "/main.aspx" +
3: "?etn=" + targetEtn.toLowerCase() +
4: "&id=" + encodeURIComponent(targetId.toLowerCase()) +
5: "&pagetype=entityrecord";
6:
7: // Redirect to the CRM Form
8: window.location.replace(targetUrl);
As you can see, this CRM Web Resource can help solve the problem of linking to CRM Forms from an external system. All that is left to do is to create the REST query and link on the external web page. The REST query used can contain any criteria necessary to retrieve the desired CRM record. The criteria is commonly an external identifier stored in CRM, but the possibilities are only limited by the capabilities of the CRM REST services.
Posted by Nick Doriot on July 20, 2011 at 08:53 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
July 18, 2011
CRM 2011 – JavaScript Execution Context - Xrm
A while back, I had two posts concerning the execution of Fetch against the 2011 service (1 & 2). A couple days ago, I received some great comments concerning these posts. One of the comments highlighted a frequent stumbling block I have seen with CRM 2011 – JavaScript Execution Context. Mainly, I’m talking about the JavaScript variable “Xrm”. I think we all got a little spoiled with “crmForm.all”; however, after you get acquainted to using “Xrm”, I think you’ll feel “crmForm.all” was a little bloated. (With it being summer, “Xrm” is ready for the beach!)
In these examples, I’m going to demonstrate executing the getServerUrl() method from an entity’s form.
Inside of a JavaScript Web Resource
To access the getServerUrl() method, simply use:
Xrm.Page.context.getServerUrl()
Inside of an embedded HTML Web Resource (aka an IFrame)
Now since we’re talking context, you can always reference the “ClientGlobalContext.js.aspx” and use:
But I’m wanting more than what’s inside Xrm.Page.context; I want everything inside the “Xrm” variable. For this we can use:
window.parent.Xrm.Page.context.getServerUrl()
To use the “window.parent.Xrm”, you will need to make sure Cross-Scripting is enabled.
Inside of Developer Tools (F12)
I’ve seen people use crmForm.all and then use the CRM 4 to CRM 2011 conversion utility to code. Instead, it’s much easier to use:
document.getElementById('contentIFrame').contentWindow.Xrm.Page.context.getServerUrl()
Summary
If you aren’t familiar with Execution Context, I would recommend delving a little deeper. My intentions for this was merely to demonstrate how to use the “Xrm” variable in the different scenarios. So whether you are using the Console inside Developer Tools or whatever, you can see using the “Xrm” variable is easy once you have the right execution context.
Posted by Paul Way on July 18, 2011 at 08:03 AM in Dynamics CRM 2011, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (1) | TrackBack (0)
July 08, 2011
Using the FetchXML CRM 2011 Service within a JavaScript Web Resource
Last time we looked at using the FetchUtil.js file inside of an HTML file. This time, I’m going to show you how to execute fetch from JavaScript. I’m also going to talk a little about organizing your web resources and talk briefly about when to use the fetch. Before we begin, let’s lay some of the ground work here.
Keeping it Organized
CRM 2011 has some awesome features and web resources is certainly one of them. It’s important when you create your web resources to have some organization. The more you extend CRM, the easier it would be to start messing others up. I think the best route is to use your abbreviation, followed by the the folder, followed by the file. That sounds a little confusing, so lets look at an example.
Today we are going to create two files. The first is our JavaScript Fetch utility. The second is a JavaScript file specific to our incident entity. In fact, the incident entity is all part of our incidentX project. On the other hand, the Fetch Utility is going to be used across multiple projects. Now there isn’t any need to upload the Fetch Utility inside each project that needs it. So we will create two web resources like so:
- new_utils/FetchUtil.js
- new_incidentX/showMainPhone.js
Now, if I want to add some custom icons and other things to my incidentX project, I can simply do so in the new_incidentX folder.
Adding the Web Resources
Let’s look at both of the files and how they will appear on the form.
FetchUtil.js
var XMLHTTPSUCCESS = 200;
var XMLHTTPREADY = 4;function FetchUtil(sOrg, sServer) {
this.org = sOrg;
this.server = sServer;if (sOrg == null) {
if (typeof (ORG_UNIQUE_NAME) != "undefined") {
this.org = ORG_UNIQUE_NAME;
}
}if (sServer == null) {
this.server = window.location.protocol + "//" + window.location.host;
}
}FetchUtil.prototype._ExecuteRequest = function (sXml, sMessage, fInternalCallback, fUserCallback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", this.server + "/XRMServices/2011/Organization.svc/web", (fUserCallback != null));
xmlhttp.setRequestHeader("Accept", "application/xml, text/xml, */*");
xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");if (fUserCallback != null) {
//asynchronous: register callback function, then send the request.
var crmServiceObject = this;
xmlhttp.onreadystatechange = function () {
fInternalCallback.call(crmServiceObject, xmlhttp, fUserCallback)
};
xmlhttp.send(sXml);
} else {
//synchronous: send request, then call the callback function directly
xmlhttp.send(sXml);
return fInternalCallback.call(this, xmlhttp, null);
}
}FetchUtil.prototype._HandleErrors = function (xmlhttp) {
/// <summary>(private) Handles xmlhttp errors</summary>
if (xmlhttp.status != XMLHTTPSUCCESS) {
var sError = "Error: " + xmlhttp.responseText + " " + xmlhttp.statusText;
alert(sError);
return true;
} else {
return false;
}
}FetchUtil.prototype.Fetch = function (sFetchXml, fCallback) {
/// <summary>Execute a FetchXml request. (result is the response XML)</summary>
/// <param name="sFetchXml">fetchxml string</param>
/// <param name="fCallback" optional="true" type="function">(Optional) Async callback function if specified. If left null, function is synchronous </param>var request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
request += "<s:Body>";request += '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services">' + '<request i:type="b:RetrieveMultipleRequest" ' + ' xmlns:b="http://schemas.microsoft.com/xrm/2011/Contracts" ' + ' xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<b:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic">' + '<b:KeyValuePairOfstringanyType>' + '<c:key>Query</c:key>' + '<c:value i:type="b:FetchExpression">' + '<b:Query>';
request += CrmEncodeDecode.CrmXmlEncode(sFetchXml);
request += '</b:Query>' + '</c:value>' + '</b:KeyValuePairOfstringanyType>' + '</b:Parameters>' + '<b:RequestId i:nil="true"/>' + '<b:RequestName>RetrieveMultiple</b:RequestName>' + '</request>' + '</Execute>';
request += '</s:Body></s:Envelope>';
return this._ExecuteRequest(request, "Fetch", this._FetchCallback, fCallback);
}
FetchUtil.prototype._FetchCallback = function (xmlhttp, callback) {
///<summary>(private) Fetch message callback.</summary>
//xmlhttp must be completed
if (xmlhttp.readyState != XMLHTTPREADY) {
return;
}//check for server errors
if (this._HandleErrors(xmlhttp)) {
return;
}var sFetchResult = xmlhttp.responseXML.selectSingleNode("//a:Entities").xml;
var resultDoc = new ActiveXObject("Microsoft.XMLDOM");
resultDoc.async = false;
resultDoc.loadXML(sFetchResult);//parse result xml into array of jsDynamicEntity objects
var results = new Array(resultDoc.firstChild.childNodes.length);
for (var i = 0; i < resultDoc.firstChild.childNodes.length; i++) {
var oResultNode = resultDoc.firstChild.childNodes[i];
var jDE = new jsDynamicEntity();
var obj = new Object();for (var j = 0; j < oResultNode.childNodes.length; j++) {
switch (oResultNode.childNodes[j].baseName) {
case "Attributes":
var attr = oResultNode.childNodes[j];for (var k = 0; k < attr.childNodes.length; k++) {
// Establish the Key for the Attribute
var sKey = attr.childNodes[k].firstChild.text;
var sType = '';// Determine the Type of Attribute value we should expect
for (var l = 0; l < attr.childNodes[k].childNodes[1].attributes.length; l++) {
if (attr.childNodes[k].childNodes[1].attributes[l].baseName == 'type') {
sType = attr.childNodes[k].childNodes[1].attributes[l].text;
}
}switch (sType) {
case "a:OptionSetValue":
var entOSV = new jsOptionSetValue();
entOSV.type = sType;
entOSV.value = attr.childNodes[k].childNodes[1].text;
obj[sKey] = entOSV;
break;case "a:EntityReference":
var entRef = new jsEntityReference();
entRef.type = sType;
entRef.guid = attr.childNodes[k].childNodes[1].childNodes[0].text;
entRef.logicalName = attr.childNodes[k].childNodes[1].childNodes[1].text;
entRef.name = attr.childNodes[k].childNodes[1].childNodes[2].text;
obj[sKey] = entRef;
break;default:
var entCV = new jsCrmValue();
entCV.type = sType;
entCV.value = attr.childNodes[k].childNodes[1].text;
obj[sKey] = entCV;break;
}}
jDE.attributes = obj;
break;case "Id":
jDE.guid = oResultNode.childNodes[j].text;
break;case "LogicalName":
jDE.logicalName = oResultNode.childNodes[j].text;
break;case "FormattedValues":
var foVal = oResultNode.childNodes[j];for (var k = 0; k < foVal.childNodes.length; k++) {
// Establish the Key, we are going to fill in the formatted value of the already found attribute
var sKey = foVal.childNodes[k].firstChild.text;jDE.attributes[sKey].formattedValue = foVal.childNodes[k].childNodes[1].text;
}
break;
}}
results[i] = jDE;
}
//return entities
if (callback != null) callback(results);
else return results;}
function jsDynamicEntity(gID, sLogicalName) {
this.guid = gID;
this.logicalName = sLogicalName;
this.attributes = new Object();
}function jsCrmValue(sType, sValue) {
this.type = sType;
this.value = sValue;
}function jsEntityReference(gID, sLogicalName, sName) {
this.guid = gID;
this.logicalName = sLogicalName;
this.name = sName;
this.type = 'EntityReference';
}function jsOptionSetValue(iValue, sFormattedValue) {
this.value = iValue;
this.formattedValue = sFormattedValue;
this.type = 'OptionSetValue';
}
showMainPhone.js
var _oService;
var _sOrgName = "";
var _sServerUrl = Xrm.Page.context.getServerUrl();
function fetchOnLoad()
{
// Get the ID of the Customer
var sCustGUID = Xrm.Page.getAttribute(' customerid ').getValue()[0].id;
var sFetch = "<fetch mapping='logical' count='10'>" +
"<entity name='account'>" +
"<attribute name='telephone1' />" +
"<filter type='and'>" +
"<condition attribute = 'accountid' operator='eq' value='" + sCustGUID + "'/>" +
"</filter>" +
"</entity>" +
"</fetch>";_oService = new FetchUtil(_sOrgName, _sServerUrl);
_oService.Fetch(sFetch, myCallBack);
}function myCallBack(results){
alert(results[0].attributes["telephone1"].value);
}
Now I have added my code to the incident entity. I also called my files by a somewhat different name; in this case “showMainPhone.js” is the same as “usingFetch.js”. The great thing here is the only text that really has to match up is the function name in the onLoad. Everything else was made pretty much bullet proof by CRM 2011.
Seeing the Results
When opening an incident, I will now see an alert box with the phone number of the account associated.
Granted, this may not be the particular feature you users are begging for (in fact the “showMainPhone.js” file needs plenty of error handling), but hopefully you can see how easy it is to embed a fetch call into your JavaScript. Whether you are summing up values from related entities or whatever, it is quite easy to use the fetchUtil.js file to enhance your end user’s experience.
Switching to Synchronous Fetch
This example used an asynchronous callback because that is what I would recommend most of the time. An asynchronous callback will let the browser execute other code; whereas the synchronous approach will wait for the fetch to finish. To use the synchronous version, you will need to change the “fetchOnLoad()” function to like so:
function fetchOnLoad() {
// Get the ID of the Customer
var sCustGUID = Xrm.Page.getAttribute('customerid').getValue()[0].id;var sFetch = "<fetch mapping='logical' count='10'>" +
"<entity name='account'>" +
"<attribute name='telephone1' />" +
"<filter type='and'>" +
"<condition attribute = 'accountid' operator='eq' value='" + sCustGUID + "'/>" +
"</filter>" +
"</entity>" +
"</fetch>";_oService = new FetchUtil(_sOrgName, _sServerUrl);
var res =_oService.Fetch(sFetch);
alert(res[0].attributes["telephone1"].value);
}
Posted by Paul Way on July 08, 2011 at 03:34 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
June 06, 2011
Microsoft CRM 2011 Tops With Analysts: Nucleus, Gartner, Forrester
Three independent research analysts recently gave strong validation that Microsoft Dynamics CRM 2011 is setting itself apart from the competition in the CRM space. As you read the reviews of these very different and independent analyses, you will see a common thread of productivity. Productivity is what happens when users have familiar tools (like Outlook and Office), high adoption rates, and actionable intelligence. Productivity is what ultimately leads to high ROI.
Forrester: Microsoft Dynamics CRM 2011 gives a 243% Return on Investment
A recent in-depth review by Forrester Research focused on nine companies that have installed Microsoft Dynamics CRM 2011. They reviewed the productivity gains of sales and customer service, as well as the efficiency gains in marketing dollars spent. The study concluded that the composite organization achieved a three-year NPV of U.S. $665,086, and that the payback period was just four months. It is a large report and can be downloaded here. Forrester's summary of benefits:
- Increased sales productivity of 5 percent with the ease of use of Microsoft Dynamics CRM software and integration with Outlook leading to higher adoption, improved opportunity and lead tracking and routing, and a more accurate picture of its sales pipeline.
- Customer service productivity savings from a better UI and documented processes.
- Streamlined processes and operations leading to lower cost of sales as Microsoft Dynamics CRM enabled the composite organization’s technical consultants to shorten time spent on the proposal process by 10 percent.
- Acceleration of sales conversion cycle by 50 percent and corresponding revenue gain as teams worked better together across different business systems, processes, and geographies.
- Marketing cost savings of more than $200,000 (risk-adjusted over three years) due to more real-time insights and improved campaign management from better analytics.
- Productivity savings of 16 man-hours per month due to the better reporting tools in Microsoft Dynamics CRM with data consolidation, reporting automation, and richer dashboard capabilities.
- Improved customer service delivery with quicker response times, dialog boxes, and segmented levels of customer service through insights from Microsoft Dynamics CRM.
- Improved cross-sell collaboration within sales and customer service teams by providing access to reporting tools and other collaboration software such as Microsoft SharePoint Server 2010.
- Lower costs and effort to customize the solution to unique requirements, including extended CRM
scenarios. - Ease of integration with other business technology systems, improving process efficiencies.
Nucleus: Microsoft CRM 2011 is "easier to use" than Salesforce and half the cost!
Nucleus Research recently rolled out its "Technology Value Matrix 1H11: CRM". The report focused on international CRM software that was deployed both on-premises and in the cloud. It focused on core CRM (sales, service, and marketing) and not xRM applications. The 'Value Matrix' is based on "functionality and usability, the two core measures that Nucleus has found indicate an application's ability to deliver initial ROI and, ultimately, maximum value. "
Not surprising to those who work with Microsoft CRM every day, Microsoft was one of only two upper quadrant leaders in usability and functionality. MSCRM rated highly in usability because of:
- Office integration
- Outlook integration
- Role-based views
- Personalizable interfaces
Nucleus also took note of Microsoft's aggressive pricing strategy, which Nucleus found "in many cases is less than half the average per-user price of Salesforce.com".
After highlighting the leadership positions at the top, Nucleus noted that other CRM Offerings "do not have compelling positions".
Gartner: Microsoft CRM 2011 is leader in Customer Service Contact Centers
Gartner recently produced a report that looked at CRM in the customer service contact center. Specifically, it looked at CRM business applications, contact infrastructures (CTI, chat, email, alerts, etc.), workforce optimization tools, feedback management and analytic tools. Gartner recognized Microsoft CRM 2011 a leader in this space and noted their xRM expertise. "There are many scenarios across industries such as government, healthcare, higher education, real estate, and retailing where the flexibility of the system to support a range of interactions makes it a good shortlist product."
The Gartner report reiterated the Office integration and productivity theme of the other analysts. "The Microsoft Outlook look and feel, together with the integration with SharePoint and Microsoft Office, are commonly mentioned pluses of the system for customers."
Leadership in the Customer Service space reinforces Microsoft's commitment to deliver a comprehensive customer service solution. Microsoft recently introduced a new version of its Customer Care Accelerator (CCA) for 2011. This is available for download on the Microsoft Dynamics Marketplace.
The CCA helps customer care and contact center organizations provide a comprehensive and consistent customer service experience by combining data elements from disparate applications, such as mainframes, databases or websites, for display in a single-user interface. These disparate data sources are surfaced in the CCA and tracked CRM to complete the 360 view of a customer to a Customer Service Agent.
Posted by Brad Koontz on June 06, 2011 at 03:47 PM in Dynamics CRM 2011, Microsoft CRM for Outlook, XRM | Permalink | Comments (0) | TrackBack (0)
May 18, 2011
FetchXML 2011–Total Record Count
Fetch in 2011 has added some really nice features. One of those nice features is the total record count of the applied filter. In 4.0 you had to execute two fetch statements, but now with 2011 you can add the ReturnTotalRecordCount attribute.
<fetch mapping='logical' count='25' returntotalrecordcount='true'>
<entity name='account'>
<attribute name='accountid' />
<attribute name='accountnumber' />
<attribute name='address1_city' />
<attribute name='address1_telephone1' />
<attribute name='name' />
<order attribute='name' />
<filter>
<condition attribute='address1_city' operator='like' value='Greenville%' />
</filter>
</entity>
</fetch>
Upon executing this fetch statement with the ReturnTotalRecordCount attribute set to true, the “TotalRecordCount” is returned.
Note: This is not the total number of entities, it is the total number of records that match the filter.
Posted by Paul Way on May 18, 2011 at 11:11 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
March 07, 2011
Trade Funds Management Application Using Microsoft Dynamics CRM 2011
Consumer Products Good (CPG) Manufacturers set up Trade Funds each year to account for the millions of dollars that are spent promoting their products with food, drug, mass and club class of trade retailers. One only has to bring up the term “Trade Funds” with a senior or regional sales manager to elicit a cringe on their faces. The reason for this lies in how complex TFM is within the company.
Some sample promotions for a given product brand might include:
- Temporary Price Reduction (TPR)
- New Distribution Allowances
- End-Cap Display Allowances
- In-Store Radio
- In-Store Advertising
- Newspaper Ad
- Television Ad
- Coupons
- Free Standing Insert (FSI) Placement
These are just a sample of the many types of advertising expenses that a sales manager for a large CPG company would encounter in dealing with a national retailer. In addition, the funds can be set up on a conditional basis, meaning the retailer must “perform” in order to receive the allowances. Typically, the list of performance criteria relate to advertising and price, but it could include store shelf placement, for example.
CPG companies usually incorporate some type of company-wide ERP solution to track Trade Funds Management (TFM) activity. Complexities arise with tracking promotion funding, in areas such as:
- Deduction Resolution Management
- Off-Invoice Performance
- Bill-Back / Check Payments
- Proof of Performance (PoP) Tracking
- Over-Spending and Under-Spending
In addition, the allocation for funding can be a very difficult issue to determine amongst the retailers. For example, should the division of funding be based upon Market Share, Dollar Share, % Growth from Previous Year, % of Total Sales or % of Brand Sales? - All of these methods could be argued as valid.
At the end of the day, however, the fact remains that at the very heart of the administration of TFM lies two basics things: 1) Trade Funds; and 2) Promotional Events. I would argue that, using Dynamics CRM, a sales organization can manage these funds and track performance very easily, without having to use a full-blown ERP solution, which can be very expensive, complex and time-consuming to the sales group.
In roughly 30 minutes, I built out a Trade Funds Management entity and Trade Fund Event entity in CRM. See screenshots below:
Figure 1: Trade Fund
Figure 2: Trade Fund Event
In these two entities, we have captured the main components of TFM and linked the funds to the fund events. These main components include:
- Fund Amounts
- Brand/Category Alignment
- Fund Status
- Fund Start/End Dates
- Account Alignment
- Promotion Dates
- Promotion Performance Type
- Event Costs
- Proof of Performance
I have seen organizations take 2 years to implement a Trade Funds Management system, spending millions and millions of dollars and utilizing hundreds of people. I think there is an inherent genius in simplicity. Just as organizations evolve, so should systems, processes and capabilities. Using Microsoft Dynamics CRM 2011, an organization could quickly implement a solution – and then build on that solution. Initial user adoption would be high due to the relative ease-of-use of the application. Over 3-6 months, feedback would help to shape “Phase 2” of the application. Lastly, if needed, integration processes can output/input data to the application, but would not be visible to the user. This would eliminate “system clutter” and help make the user interface cleaner.
Obviously, this isn’t meant to attempt to replicate the full capabilities of a traditional ERP application. However, it does represent a viable alternative for a lot of companies that are looking to quickly develop processes and skillsets in this area, and shows the power of XRM for rapid development of line of business applications.
Good luck in your 2011 implementations!!
Posted by Patrick Picklesimer on March 07, 2011 at 10:51 AM in XRM | Permalink | Comments (0) | TrackBack (0)




Recent Comments