This post is a continuation of the Simplifying Navigation theme in Dynamics CRM. The most recent post regarding this topic discussed making Site Map changes to optimize the CRM Areas or "wunderbar" experience. This post is an extension of that topic i.e. making changes to the menu items in each of these areas.
When creating a custom entity, CRM by default adds the entity into a new group called "Extensions" as illustrated below.
The issues with this default configuration is that the navigation can be kind of haphazard. That is, it is quite likely that "Banks" in the example given more naturally belongs under the "Customer" grouping. Like so:
Or perhaps in one of the other groupings or even in a more aptly named custom grouping. Assuming this is the case, this default configuration makes the user have to "hunt" when navigating between the different areas. So although this is nothing more than a minor tweak it is an important one that should not be overlooked. Kind of similar to updating the widget icon that custom entities have by default to a more relevant uniquely tailored icon (as also illustrated above) i.e. the application will work fine without these small tweaks but that a conscientious solution integrator should be sure to take the time to tweak. This is the proverbial crossing of the i's and dotting of the t's - if these type tweaks are not made then it likely reflects other more fundamental design shortcuts that are being implemented elsewhere in the application due to the general lack of attention to detail. This is something akin to this post in the Harvard Business Review about grammar - the principle of which I generally subscribe to (ok - who is going to be the first person to point out the mistakes in grammar that I have made? Principle or principal? :).
This change can be implemented quite simply by exporting the Site Map and removing the SubArea from the Extensions group (the Extensions group can be removed as well).
Subsequently copy the SubArea into the Group in which you want it to appear. The order of its appearance will match the order in which it is inserted into the Site Map.
Import and publish and you should be all set.
The intention of this blog is to focus on the business application of Microsoft CRM and its surrounding ecosystem. In doing so, whenever discussing a topic I will endeavor to avoid presenting dry facts but rather to relate it to the practical application and/or impact it might have on the business, the pros, cons, best practices etc. The correct way of thinking is paramount when confronting a business challenge and this is what I hope to bring to the table.
Tuesday, October 16, 2012
Monday, October 15, 2012
Ribbon: Get View ID (Context Sensitive)
Let's say you want to execute an action that will act on the view that is currently being viewed i.e. the objective is that the action will be context sensitive to the current view. How can this be achieved?
The first thing to do is of course to add the a button to the application ribbon and you'd be well advised to use the Ribbon Workbench Tool to do this. As we are adding a button that will appear in the grid views you'll need to be sure to select the HomePage option.
As illustrated in the screenshot above, the action for the ribbon button references a JavaScript function, so you will of course need to create a JScript web resource and a custom function to match what you define for the ribbon action command.
The JScript function will then take care of launching the custom action and passing in the View ID of the view currently being viewed - which is the crux of this post. The script below provides the method for obtaining the view ID:
The result of the above exercise will be a button on the ribbon that when clicked will pop up with the ID of the current view:
You can of course tailor this jscript to pass in the retrieved View ID to whatever custom action you wish to execute.
The first thing to do is of course to add the a button to the application ribbon and you'd be well advised to use the Ribbon Workbench Tool to do this. As we are adding a button that will appear in the grid views you'll need to be sure to select the HomePage option.
As illustrated in the screenshot above, the action for the ribbon button references a JavaScript function, so you will of course need to create a JScript web resource and a custom function to match what you define for the ribbon action command.
The JScript function will then take care of launching the custom action and passing in the View ID of the view currently being viewed - which is the crux of this post. The script below provides the method for obtaining the view ID:
function GetViewID() {
try {
if (document.getElementById('crmGrid_SavedNewQuerySelector')) {
var view = document.getElementById('crmGrid_SavedNewQuerySelector');
var firstChild = view.firstChild;
var currentview = firstChild.currentview;
if (currentview) {
var viewId = currentview.valueOf();
alert(viewId);
}
}
else {
alert("No Element");
}
}
catch (e) {
alert(e.message);
}
}
The result of the above exercise will be a button on the ribbon that when clicked will pop up with the ID of the current view:
You can of course tailor this jscript to pass in the retrieved View ID to whatever custom action you wish to execute.
Friday, October 12, 2012
context.getServerUrl() not getting correct context
Whenever you need to run a web service or fetch query from within the jscript of the form you need to first obtain the server URL of your environment. Typically that is performed by running the following set of commands:
However there are a few issues with the context that this returns. For example, if I open up a contact form that has some fetch logic on the CRM server, then form opens up and renders cleanly. However opening the same contact form remotely will result in an error such as the one shown below:
The reason for this is quite simple:
When running CRM from the server, the URL does not need to include the domain name. For example: http://crm/org1. However when running from a client machine you need to specify (in certain scenarios) the full domain in order for CRM to open e.g. http://crm.acme.com/org1.
The problem with the context.getServerUrl() command is that in both of the above cases it will return http://crm/org1 (without the acme.com) which is going to be valid when running from the server but not when running from a remote client.
Although I have not confirmed I believe there will be similar issues if you're running CRM over https i.e. it will cause browser security warning popups to be issued as the command will try and execute over the http context rather than https.
The solution that we found (thanks to this post) was instead to use the following syntax in favor of the more standard formula listed above:
The result is that when running CRM using the http://crm/org1 URL, serverUrl will return http://crm/org1. And when running CRM using the more fully qualified http://crm.acme.com/org1, serverUrl will correspondingly return http://crm.acme.com/org1. Which of course is what you should expect.
And this small tweak results in the experience being the same no matter which machine CRM is accessed from.
var context = Xrm.Page.context; var serverUrl = context.getServerUrl();
However there are a few issues with the context that this returns. For example, if I open up a contact form that has some fetch logic on the CRM server, then form opens up and renders cleanly. However opening the same contact form remotely will result in an error such as the one shown below:
The reason for this is quite simple:
When running CRM from the server, the URL does not need to include the domain name. For example: http://crm/org1. However when running from a client machine you need to specify (in certain scenarios) the full domain in order for CRM to open e.g. http://crm.acme.com/org1.
The problem with the context.getServerUrl() command is that in both of the above cases it will return http://crm/org1 (without the acme.com) which is going to be valid when running from the server but not when running from a remote client.
Although I have not confirmed I believe there will be similar issues if you're running CRM over https i.e. it will cause browser security warning popups to be issued as the command will try and execute over the http context rather than https.
The solution that we found (thanks to this post) was instead to use the following syntax in favor of the more standard formula listed above:
var context = Xrm.Page.context; var serverUrl = document.location.protocol + "//" + document.location.host + "/" + context.getOrgUniqueName();
The result is that when running CRM using the http://crm/org1 URL, serverUrl will return http://crm/org1. And when running CRM using the more fully qualified http://crm.acme.com/org1, serverUrl will correspondingly return http://crm.acme.com/org1. Which of course is what you should expect.
And this small tweak results in the experience being the same no matter which machine CRM is accessed from.
Thursday, October 11, 2012
Phone Number Formatting
It is a fairly common and understandable requirement that telephone numbers be formatted when entering via the CRM front end. For example, if a user enters "5551234123" or "555-1234123" or "555-123-4123" or any other variation thereof it should be formatted into the standard format of "(555) 123-4123".
The jscript logic mentioned below can be used to this end. Specifically this logic will work as follows:
The steps to achieve this are as follows --
First, define the PhoneNumberValidation function that performs all the heavy lifting:
Then for each attribute on the form you wish to format, create an "on change" function as follows:
Where:
Finally, reference the "on change" event from the form "on save" event to prevent the form from saving unless the phone number has the correct format. For example, the following will work:
Don't forget to check the "pass execution context as first parameter" in the "on save" event or otherwise it will not work!
The jscript logic mentioned below can be used to this end. Specifically this logic will work as follows:
- Strip any non-numeric values from the phone number field
- If the phone number is less than 10 digits issue a warning
- If the phone number is equal to 10 digits, format as in the example above
- If the phone number exceeds 10 digits assume it to be an international number and do not perform any additional formatting (besides stripping non-numeric values)
- Prevent the form from being saved unless the phone numbers adheres to the above rules
The steps to achieve this are as follows --
First, define the PhoneNumberValidation function that performs all the heavy lifting:
function PhoneNumberValidation(phone,phoneDesc) {
ret = true;
var phone1 = Xrm.Page.getAttribute(phone).getValue();
var phone2 = phone1;
if (phone1 == null)
return true;
// First trim the phone number
var stripPhone = phone1.replace(/[^0-9]/g, '');
if ( stripPhone.length < 10 ) {
alert("The " + phoneDesc + " you entered must be at 10 digits. Please correct the entry.");
Xrm.Page.ui.controls.get(phone).setFocus();
ret = false;
} else {
if (stripPhone.length == 10) {
phone2 = "(" + stripPhone.substring(0,3) + ") " + stripPhone.substring(3,6) + "-" + stripPhone.substring(6,10);
} else {
phone2 = stripPhone;
}
}
Xrm.Page.getAttribute(phone).setValue(phone2);
return ret;
}
Then for each attribute on the form you wish to format, create an "on change" function as follows:
function Attribute_OnChange() {
PhoneNumberValidation("attributeName", "attributeDescription");
}
Where:
- attributeName is the name of the attribute the "on change" function is acting on e.g. telephone1
- attributeDescription is a the friendly name for the attribute (used for the warning error messages)
Finally, reference the "on change" event from the form "on save" event to prevent the form from saving unless the phone number has the correct format. For example, the following will work:
function Form_onsave(executionObj) {
var val = true;
if (!Attribute_OnChange())
val = false;
if (!val) {
executionObj.getEventArgs().preventDefault();
return false;
}
}
Don't forget to check the "pass execution context as first parameter" in the "on save" event or otherwise it will not work!
Wednesday, October 10, 2012
Reports: Excel Export Formatting
When exporting an SSRS report to Excel all kinds of interesting things can happen from a formatting point of view. The report which might otherwise render quite nicely in SSRS may appear to have been attacked by a bunch of gremlins in its exported Excel format. Several things might contribute to this haphazard effect:
- The cells of data on the spreadsheet might be scattered all over the place
- Some elements of data may occupy several excel cells having been auto-merged in the export
- The report might export into multiple different sheets within the spreadsheet with no apparent rhyme or reason
The report below illustrates this showing cells of data being spread way out and multiple sheets being created from a single export file.
The main reason for this is that Excel is a highly structured document and it needs to fit the results into neat rows and columns. If the cells or rows are not aligned then Excel makes automatic decisions in terms or rendering the export. For example, merge cells may bring 2 sets of non-aligned data elements into alignment; expanding a column width or height may similarly bring a non-aligned element into alignment with other report elements.
Therefore it goes without saying that one of the keys to ensuring that a report renders well when exporting to Excel is to ensure alignment. This might seem obvious and is fairly easy when it comes to grid controls which are fairly structured elements to begin with. However if you look at the report above, you will notice that many of the data elements are text fields and you will need to ensure that these all align quite nicely. It might be difficult to discern with the eye - for example, a location of a cell might be 2.51322 vs. 2.5 and that small discrepancy can throw things off in the export to Excel. The following guidelines should therefore be used when creating SSRS reports (in general in terms of overall quality but obviously with extra emphasis when you know the report will be exported to Excel):
- Ensure alignment - you should use the text box properties to ensure that cells or aligned exactly
- Use rectangles extensively for grouping. It is quite common for different parts of a report to have different formatting structures. Using rectangles for grouping helps excel cope with changes to the format.
- If you are using sub-reports in your main report - ensure the page size of your sub-report adheres to the size of the parent report and once again - I'd recommend using a rectangle to enclose the sub-report
- Be careful with page breaks before or after sections. When excel encounters a page break it will create a new sheet.
One other important feature to be aware of is that you can also control the name that Excel assigns when creating a new sheet. As illustrated in the screenshot above - the export created default Sheet1, Sheet2 etc. sheet names. You can refer to the following post for a detailed write up of this feature (only available in RS 2008 R2). In my case, I just entered the page details as follows:
The end result is much cleaner looking export:
Monday, September 24, 2012
A brief history of XRM
I was recently asked by a client about the difference between CRM and ERP solutions. In particular I was asked whether a particular custom solution we were trying to implement fell more neatly into a CRM or ERP framework. As the answer I provided quickly evolved into a brief history of CRM or XRM (according to yours truly), I decided to share in this post -
Generally CRM is considered to be “front office” and ERP is considered to be “back office”. And what you’ll find is that the definition of what is front office and what is back office will vary from company to company depending on their business model. You’ll similarly find that CRM and ERP products have areas of overlap where a particular function can be achieved in either system. Which system to do it will ultimately depend on where it makes sense to perform the hand off between front office and back office. If they’re both equal, then you’d typically look to the system that’s traditionally more geared towards handling the requirement. For example – take payments. Typically that’s geared to the back office because the minute you enter something like this, you’re entering a world of credit memos, taxes, recogition, collections etc. But there’ll be many cases where clients would need to handle such a requirement directly from within their CRM application.
Another good example – which is more to the point – is inventory or product management. That is more typically handled by the back office applications as inventory movement is inherently an accounting activity which ERP systems handle quite well. But again you’ll find that there are areas of overlap here too.
I think all the above though is somewhat beside the point. Because I think the XXX (Customer Requirement) that we are talking about is not a typical ERP function. In fact, I’d venture to say that it is neither an ERP function nor a CRM function.
So why are we looking at CRM for this? To answer that question you’ll have to go back to our first session where I presented it not as a "CRM" solution but rather as an "XRM" solution. CRM stands for Customer Relationship Management and grew out of the need to manage the selling process. As being able to sell is inherently linked to performance on the operations side, it was necessary for sales folk to have insight into the service delivery organization. Such as support and ticketing to understand how things were proceeding on that front (as information is power in terms of being able to anticipate and predict the customer’s overall level of satisfaction). And of course, marketing is just the flip side of sales i.e. one feeds the other and vice versa, so a CRM solution is not complete without a marketing module. This is why the classic definition of CRM is an application that covers sales, marketing and service.
But then as a CRM solution essentially became an application that deals with the “front office” as driven by the need to understand the customer’s or prospect’s overall experience, there were always going to be cases where in a particular business model, other types of information would be essential to this visibility. For example, a sales person going to a client meeting might need to be able to easily access information housed in a home grown application, medical records, or purchase history. As such – being a front office application – a CRM solution became something that needs to essentially be something that can model a business in whichever shape or form it takes i.e. CRM solutions needed to be flexible enough to be configured to model a particular business model.
At this point, a CRM solution is in effect doing a whole lot more than just Customer Relationship Management. Not only might the “customer” be a vendor, partner, reseller etc. (i.e. not the strict definition of “customer”), but also the platform became highly configurable such that it is more common than not to incorporate functionality that does not fit into even a loose definition of “customer relationship management”. Hence the term “XRM” was born which unofficially stands for “anything relationship management” attempting to reflect the fact that the platform is not just limited to “customer relationship management”. Of course even the “R” in XRM might neither be relevant based on the above description since we don’t need to necessarily even be dealing with a “relationship”. But as far as I know, from a naming standard convention, we haven’t yet evolved beyond “XRM”.
In contradistinction to the above, ERP solutions are typically (at least at this point in time) more standardized than CRM solutions. This is because the “back office” tends to be more standardized from business to business than the front office. To be sure, back office systems have deep and complex functionality but in my humble opinion (from a non-accountant) standard accounting practices have in principle not changed all that much since the double entry system was first invented (perhaps a little over-simplified) and therefore companies are more likely to be modifying their business practices to model the “best practice” of the ERP solution they are implementing than the other way round. Whether the last statement is true or not, I think you’ll definitely find that CRM solutions are much more flexible solutions than their older ERP counterpart due to the nature of the business problem they are trying to solve.
In summary, although I don’t think the XXX requirement is either a classic CRM or ERP type application, I do believe that it is a great candidate for the XRM model. And we definitely require the flexibility of the XRM platform in order to accommodate the requirements.
Friday, September 14, 2012
CRM Outlook Form and Display Rules
There seems to be an issue with the Outlook CRM create form as it relates to the Display Rule for ribbon buttons. More specifically - and by way of example - you can add a custom button to the ribbon and set it up with a Display Rule to control when the button should show. For instance, you can set up a FormStateRule where State = Existing and this will tell the application that the custom button should only show on the edit (update) form and not the new (create) form.
So where's the issue?
Well in addition to the Display Rule, you can also define an Enable Rule which controls whether the button should appear in an enabled or disabled (i.e. greyed out) state. The Display Rule trumps the Enable Rule i.e. if the Display Rule returns a "Do not display" result, then there's no point in executing the Enable Rule to determine how to display it. Which of course makes sense and is how it should work.
The good news is that this is precisely the behavior if you open up the create form using Internet Explorer navigation. Yay!
The bad news is that it seems that the same behavior is not exhibited if you open up the create form using Outlook navigation. And just to make sure you know what I mean by "Outlook navigation" below is a screenshot to help clarify this:
Versus the same form opened using Internet Explorer:
The issue of course is that in the case of the Outlook Create Form it will execute the Enable Rule function whereas in the IE Create Form it will not. And the result might be an error in the Outlook Create Form as the function may not be relevant to such a form state.
Fortunately there is a work around until Microsoft fixes this issue (which I haven't made them aware of as the workaround is quite effective and not too onerous). Simply add a condition to your custom jscript function to limit it to working with update form as highlighted in the example below.
So where's the issue?
Well in addition to the Display Rule, you can also define an Enable Rule which controls whether the button should appear in an enabled or disabled (i.e. greyed out) state. The Display Rule trumps the Enable Rule i.e. if the Display Rule returns a "Do not display" result, then there's no point in executing the Enable Rule to determine how to display it. Which of course makes sense and is how it should work.
The good news is that this is precisely the behavior if you open up the create form using Internet Explorer navigation. Yay!
The bad news is that it seems that the same behavior is not exhibited if you open up the create form using Outlook navigation. And just to make sure you know what I mean by "Outlook navigation" below is a screenshot to help clarify this:
![]() |
| Outlook Create Form - note the icon at top left |
Versus the same form opened using Internet Explorer:
![]() |
| IE Create Form - note IE container |
The issue of course is that in the case of the Outlook Create Form it will execute the Enable Rule function whereas in the IE Create Form it will not. And the result might be an error in the Outlook Create Form as the function may not be relevant to such a form state.
Fortunately there is a work around until Microsoft fixes this issue (which I haven't made them aware of as the workaround is quite effective and not too onerous). Simply add a condition to your custom jscript function to limit it to working with update form as highlighted in the example below.
function CustomButtonEnableRule() {
if (Xrm.Page.ui.getFormType() == 2) {
// function logic
} else {
return false
}
}
Subscribe to:
Posts (Atom)












