Pages

Showing posts with label Microsoft Dynamics CRM 2011. Show all posts
Showing posts with label Microsoft Dynamics CRM 2011. Show all posts

Friday, December 27, 2013

CRM Manipulation Library Date Calculation Issue

Speaking of the CRM Manipulation Library solution, I encountered an issue with the date calculation logic. Specifically with regards to the somewhat misleadingly named "Add Days" function. Misleading in that if you look at the function, you will notice that in addition to the capability of adding days, you can also use this to add years, months, weeks, hours and minutes to a given date.


This is an important clarification as adding a month to date is not always going to be the same as adding 30 days to a date. Similarly for years (as in the case of a leap year). And obviously being able to specify the more granular hour and minute parameters is also helpful. We probably could have worked around not having the week parameter but it does make life a little easier by not having to perform additional calculations.

Anyway, the issue that I encountered is that ironically if you do not specify a non-zero "Days To Add" value and just supply one of the other parameters, the calculation will not work. That is, if we were to specify parameters in the screenshot above (where we're trying to add years to the date) it would not work.

Luckily there is a simple workaround solution. Below is the same function call except we trick it by subtracting a day value and then cancel it out by passing in the equivalent days in the week parameter.

If you needed to use the week parameter, then you could similarly pass in 24 hours and cancel it out by passing -1 to the day parameter.


Thursday, December 26, 2013

Workflow Plugin CRM Online Compatibility

There are a lot of useful free add-ons available for Dynamics CRM that can add some serious customization features. The issue in many cases is that these add-ons were only released for the on-premise version of CRM.

For example, take the very useful CRM Manipulation Library which contain some important calculation capabilities. I especially find the Date Utilities to come in handy as there is often a requirement to calculate an off-set of days or business days as part of workflow logic. But when you try import the solution into a CRM Online instance you will receive an error that looks something like the following:



The good news is that this can be made compatible with CRM Online by following these steps:


  1. Unzip the solution into a folder
  2. Open the customizations.xml file
  3. Perform a search for the IsolationMode node
  4. Change IsolationMode value from 1 to 2
  5. Update the original zip file with the modified file


Once you have done so you should be able to import it into CRM Online. Applying this trick does not necessarily mean that the plugin will work as it might fail on something when trying to execute. However it also just may function without a problem which is indeed the case with the CRM Manipulation Library. So it's definitely worth a shot. If your plugin does not work after performing this tweak, you'll have to do some debugging at the code level.

Wednesday, December 18, 2013

Publish External Reports - Dynamic Content

Time to complete what I started... So in my last post we walked through the steps to make an externally published report accessible to non-CRM users which would seem to be the objective of the whole publishing externally exercise as documented in the introduction to this topic.

However by following those steps we are limited to a fairly static report based on the report parameters of the originally published report. This is going to be of limited benefit. For example, consider the scenario where I want to include a link via an email to a non-CRM user that would allow them to view data from within CRM - for instance - we might want to send them a report on an opportunity whereby clicking on the link they are able to view the opportunity details. Well, unless we're able to pass in parameters to this published report we will be prevented from doing so i.e. it will lack the necessary context sensitivity.

In order to overcome this limitation we'll be manipulating the CRMFilteredEntity report parameter mentioned in my previous post. We'll continue using the example of the User Summary report for the sake of illustration.

The first thing to do is to navigate to the report using the Report Server navigation i.e.:


Now click on your externally published report (in our case "User Summary") and retrieve the execution URL i.e. http://crm2011/ReportServer/Pages/ReportViewer.aspx?%2fCRM_MSCRM%2fUser+Summary&rs:Command=Render and you should be presented with something that looks like this:


Now take the URL and carefully graft on the value from the CRMFilteredEntity parameter (provided in my previous post). That is, your URL should now include the CRM_FilteredSystemUser parameter and look something like the following:

http://crm2011/ReportServer/Pages/ReportViewer.aspx?%2fCRM_MSCRM%2fUser+Summary&CRM_FilteredSystemUser=select  [systemuser0].*  from  FilteredSystemUser as "systemuser0"  where  ("systemuser0".systemuserid = N'{43AF6573-C97C-E011-9556-00155DA5304E}')&rs:Command=Render

And voila - your report will now render the User Summary report only returning the corresponding user specified by the report parameter.


Also test with a non-CRM user to ensure that this is working outside of CRM authentication.

This exercise proves that we can now manipulate the URL of this report to pass in parameters to the report. Essentially all you need to do is to update the query specified in the CRM_FilteredEntity parameter and this will filter the results of the report. Of course the same approach can be applied to any other report in the system. For example, if we were working with an account report we'd manipulate the query for the CRM_FilteredAccount parameter; if we were working an opportunity report we'd manipulate the query for the CRM_FilteredOpportunity parameter. And so on and so forth.

Now of course this result doesn't lend itself very well to specifying report parameters manually - your users would lynch you if you tried proposing this to them!  But it does lend itself quite well to automated mechanisms such as via workflow whereby you can construct a URL using dynamic workflow fields resulting in a report URL in an email that a user just has to click on in order to see the relevant, context-sensitive, dynamic report content.

In the next post we'll take a look at performing those steps as well as seeing how this approach can also be leveraged to generate PDF documents that can be sent to external users or customers.

Wednesday, September 18, 2013

Publish External Reports - Credentials

My previous post introduced the topic of publishing reports for external usage and the conclusion drawn was that - as far as I can tell - the only reason why this action would be performed would be to grant non-CRM users a view into CRM data. But unless we make some changes to the report credentials, external users will be prevented from running the report. This post describes how to go about performing this step.

We'll use the default "User Summary" report for the sake of this exercise. In order to demonstrate how this works end to end, first modify the Default Filter for this report and arbritarily add some filter criteria. We'll add a filter by user name.


Subsequently edit the report and select the "Publish Report for External Use" option. We'll publish the default "User Summary" report for the sake of this exercise.


The report will now appear in the CRM report server home page (http://CRM/Reports/Pages/Folder.aspx?ItemPath=%2fCRM_MSCRM&ViewMode=Detail).In its current state it will only be accessible to CRM users. In order to make this accessible to other users we need to have it impersonate a user who has the necessary credentials to run the report. By doing so, you will actually be giving permission to anyone who has the URL to run the report.

To do so, hover over the report name and click Manage from the menu that appears.


Let's first take a look at the report parameters. What's interesting to note is that anything that was specified as part of the default filter will be stuffed into the single parameter called CRM_FilteredEntity (in our case CRM_FilteredSystemUser).




So you'll see something like the following appear for this parameter based on the default filter that we used:
select  [systemuser0].*  from  FilteredSystemUser as "systemuser0"  where  ("systemuser0".systemuserid = N'{43AF6573-C97C-E011-9556-00155DA5304E}')

The "systemuserid" is the identity (guid) of Adam Barr's user record (see above). If we had instead specified a default filter of "last name = Smith" then this parameter would like like this:

select  [systemuser0].*  from  FilteredSystemUser as "systemuser0"  where  ("systemuser0".lastname = N'Smith')

You get the picture. Understanding how this parameter is constructed will feature prominently in the next related post. Now let's move onto updating the report credentials.

Click on "Data Sources" and create a custom data source and specify a user that has the necessary permissions as shown.



Alternatively, create a custom data source using the same input as shown above and reference it using the "a shared data source" option shown above. This approach is the recommended approach because if you have more than one external report, you can just link to the same shared data source for each rather than specifying the same credentials in each case (which also helps with maintenance as passwords need to change and such like). And finally bear in mind that if your report has sub-reports then you will already have more than one report that needs to have the credentials updated (yes, all generated reports need to have this applied).

Now that you've performed this action, test the report by running it with a non-CRM user and it should return a report that looks something like what we have below. Note how the result is being limited to the default filter defined for this report in CRM.


Next: We will build on this walk through by manipulating the parameters of the report in order to have the output be dynamic and context sensitive versus the static report output that we've just presented. Being able to do so provides all kinds of interesting options.

Tuesday, September 17, 2013

Publishing reports externally

It's been a while since I last posted. Things got a little busy over the summer. Anyway, during that period I collected quite a few tidbits here and there and hope to be posting them over the course of the next few weeks. We'll see how it goes.

As anyone who has been using CRM for a while knows, CRM reports can be published externally as shown in the screenshot below. Note: this option is only available to CRM On Premise since there is no report server accessible via online.


After the report has been published it is copied to the Org_MSCRM home page in the report server i.e. http://reportserver/Reports/Pages/Folder.aspx?ItemPath=%2fOrg_MSCRM&ViewMode=Detail. Navigating to this location you will see the result of your publish action - the report along with any sub-reports that it referenced will be published as shown. In this case, the published report contained 3 sub-reports resulting in 4 reports being published.



But the question has to be asked - seems like a nifty little piece of functionality - but what is the practical application of it? To compound it further - if you try and run this report from the reporting server location - it will only work for a CRM user that has access to the CRM database. And therefore why would you not just execute the same report from within CRM? The latter option provides you with the CRM pre-filter option and a generally much better overall user experience - so there is no reason that I can think for a CRM user to execute the report from anywhere other than from within CRM.

In light of all of the above, the only reason that I can think of for using this feature is to provide a report that can be accessed by non-CRM users. There are a number of scenarios in increasing degrees of complexity:

  1. Just provide a URL for external users to be able to access the report
  2. Use workflow to send out a report link to non-CRM users based on a given business scenario
  3. Use workflow to email a PDF document based on an externally published report

As will be demonstrated over the next few posts, each of these builds on top of one another. And an issue that we'll have to contend in both cases 2 and 3 is one of context sensitivity i.e. typically if you're automating something you're going to want it to generate a report that is specific to the quote, order, invoice etc. that it was generated from.

The first thing we need to deal with is giving access to an externally published report. Because, if you've been paying attention, there is an inherent contradiction - externally published reports cannot be accessed by non-CRM users... By default.

In the next post, I will walk through the steps for allowing external users to access the CRM published report.

Wednesday, May 29, 2013

Deleting iPhone Contacts

One of the side benefits of writing blog posts is that over time you create your own knowledge base that you can refer to. I know I am not alone when I say that there have been countless occasions where although I seem to recall having implemented a particular solution in the past... only to struggle to figure out exactly where, when and how it was performed. My experience is that once you have blogged about a topic, it is much more likely to be registered to long term memory which when combined with the power of search... voila! I guess blogging is an example of the proverb: "You never really own something until you're able to give it away" (Bill Nichols).

In light of that long-winded intro...

As part of rolling out the Outlook client functionality to end users we needed to of course clean up existing contacts so that duplicate contact records are not created when syncing from CRM.

In most cases, this is just a matter of cleaning up your Outlook contacts because Outlook contacts sync to the various mobile apps so cleaning up in Outlook results in a clean up everywhere else.

We were however presented with a case where the contacts were stored directly on the iPhone and we needed to get rid of these. Given that various users had thousands of contacts going through the list was not really an option.

Fortunately we were able to purchase a very inexpensive iPhone app in order to solve the problem. The app name is Contacts In : Import CSV & Manage Groups. Steps:

Select All Contacts:


 Click Action menu and choose "Delete contacts":


Tuesday, May 28, 2013

CRM 2011 Installation Requirements

This question gets posed to me every so often. In light of that, the following post will provide information describing the installation requirements for CRM 2011. I will also highlight what I consider to be practical best practice recommendations and finally at the end I have provided server breakdown installation scenarios.

First of all, please refer to the very useful link below. It contains a step by step visual walk through of all the installation components, including SQL, CRM prerequisites, CRM 2011, Email Router etc. The rest of this post makes abundant reference to this walk through.

http://social.technet.microsoft.com/wiki/contents/articles/3176.how-to-install-microsoft-dynamics-crm-2011.aspx

The following link similarly provides a visual walk through for installing and configuring the Microsoft Dynamics CRM E-mail Router (Online & Exchange Online).

http://social.technet.microsoft.com/wiki/contents/articles/4686.microsoft-dynamics-crm-e-mail-router-configuration-crm-2011-online-exchange-online.aspx

Finally, bookmark this link. It contains a very useful set of Q&A for commonly asked questions related to CRM 2011 server setup.

http://blogs.msdn.com/b/crminthefield/archive/2012/02/11/crm-2011-server-setup-commonly-asked-questions.aspx

Hardware and Software Requirements

  • See sections "Hardware Requirements" and "Software Requirements" 
Credentials
  • Installation User - see section "Required for installation of Dynamics CRM 2011" for permissions required for this user. 
    • Recommendation: Create a user called "CRMAdmin" under which you will install CRM
  • Services - CRM creates a number of windows services
    • Recommendation: It is recommended to have a separate to have a separate account for each of these. For example: 
      • Sandboxing service – e.g. create account CRMSandbox (see section “Microsoft Dynamics CRM Sandbox Processing Service” for permissions required for this user)
      • Async service – e.g. create account CRMAsyncProcess (see section “Microsoft Dynamics CRM Asynchronous Processing” for permissions required for this user)
      • Web Deployment service e.g. – create account CRMDeploymentWS  (see section “Web Deployment Service (CRMDeploymentServiceAppPool Application Pool identity)” for permissions required for this user)
      • Application service – e.g. create account CRMAppService (see section “Application Service (IIS Application Pool identity CRMAppPool)” for permissions required for this user)
Ports

See section "Ports" for a full description of default ports that are used as part of the CRM solution. You can also refer to the following post for a visual representation and link to a white paper covering the topic.

Installation Summary

The reference provides a walk through of each of these procedures:
  • SQL Server 2008 R2 – Scroll to section “Installing SQL 2008 R2” for a step by step guide. I’d follow these instructions although I’d select Mixed Mode (step 5)
  • CRM Prerequisites – Scroll to section “Installing pre-requisites in Dynamics CRM Server 2011” – Walks through server roles that need to be installed on the CRM server.
  • CRM Installation – Scroll to section “2011 Dynamics CRM Installation” – this walks you through the wizard installer screens. Note:
    • License key - you should obtain this prior to beginning the CRM installation.
    • Server Roles - See write up below assuming a 3 server configuration.
    • Organizational Unit – This links to AD. 
      • Recommended: Create an OU called MSCRM or similar (can hang off the main OU). Note. The OU is required for the installation. CRM just copies security groups into the OU (UserGroup, ReportingGroup, PrivUserGroup, SQLAccessGroup). It is good practice to use a separate OU rather than an existing OU.
    • Service Accounts – per above
    • Web site – recommend that we’ll install to the Default Web Site. If that’s an issue we can use the 5555 port. No real practical difference other than URL (if configuring IFD, the IFD URL will override this)
    • Email Router – this will point to the server which will has the Email Router installed.
    • Organization Settings 
      • Display name should just be Your Company Name. This will create a database called YourCompanyName_MSCRM. Specify if you wish a different display name.
      •  Review other settings - generally no need to change the defaults supplied
    •  Reporting Server – This can point to any reporting server. Typically you would point to the report server of the database against which CRM is installed but if you have a dedicated reporting server, you can point it to that location.
  • CRM Reporting Extensions – Scroll to section “Installing Microsoft Dynamics CRM Reporting Extensions”
    • This will be installed on the SQL Server Reporting Services instance which essentially allows authentication from CRM to pass through to SSRS. 
  • Email Router – See server breakdown below for recommendation on which server to install
  • Rollups – Applied as necessary to all the CRM products (CRM Server, Email Router, Reporting Extensions). The latest rollup can be obtained from here.

Production Server Breakdown

The following is the recommended set up in a production environment. If you do not have two application servers then combine the two app servers together.

Database Server
  • SQL
  • Reporting Extensions
Front End Server 
  • CRM Pre-requisite roles (see above)
  • Install CRM (all roles) and Disable the Back End Server Roles (Async Processing Service, Sandboxing Service)
    • Having the roles available allows these roles to be started as a failover solution

Async Server (Mid Tier) 
  • CRM Pre-requisite roles (see above)
  • Install CRM and disable Front End Server Roles (Web Application Server, Organization Web Service, Discovery Web Service, Help Server)

Development/Test Server Breakdown

Typically it is sufficient for a development/test environment to be hosted on a single server. If that is the case, then install SQL and CRM with all server roles on the server.


Tuesday, April 30, 2013

Contact Address Solution

In my previous post I provided some background with regards to the challenge of managing account and contact addresses in CRM. I also provided a solution for handling a simple scenario where the contact's account only ever has a single address. But the reality in most cases is more involved than that.

We will be walking through the scenario described at the end of my previous post i.e.
It would therefore be better to stick to a single account with multiple addresses and allow for the contact records to be associated to one of these account addresses. In the next post, I will present an elegant approach to handling this requirement.
The objective of this solution is to provide the flexibility of a custom lookup solution while not compromising any features/functions in the system by ensuring that the default (out of the box) address fields are populated with the address information.

This requirement covers a number of configuration scenarios and therefore may be useful as a good overall "configuration" tutorial. Topics referenced:

  • Default vs. Custom entities - when to resort to custom
  • Use case for the "Related Records Filtering" configuration option
  • REST API
  • Distribute Workflow
  • Plugin requirement

First of all, we need to create a new address entity. The reason we do so is because the ability to leverage the default More Addresses entity is limited. Specifically, the system does not allow custom relationships to be created against More Addresses which is a key requirement to the solution we are trying to implement. So first of all, we create a custom entity like the one shown and in order to eliminate user confusion we rename and remove the default More Addresses from the account and contact forms.


 Next we create an N:1 relationship with Account - this essentially replaces the More Addresses link in the account entity.


... and a 1:N relationship with Contact - this is going to allow for an address selector at the contact level.


We now add the relationship field created in the step above to the contact form. In the "Related Records Filtering" section we need to filter it to the Parent Account. This will limit the addresses shown in the lookup dialog to addresses defined at the parent account level.



The next thing we need to do is to add some jscript to the form. The objective here is to retrieve the address fields from the address which has been selected and copy it to the corresponding address fields on the contact record. The jscript below will perform the following logic:

  • Copy/Clear Address fields based on address selected from lookup using the REST API (note this use the retrieveEntityByID function described in this post)
  • Make fields read only if address is selected via the "picker" (the idea here is that you can enter an ad-hoc address if you want at the contact level, but if you select from one of the lookup values then the address values will be controlled by the official parent address records).

function AddressPicker_OnChange() {

 var entity = "snt_address";
 var entityid = Xrm.Page.getAttribute("snt_addressid").getValue();
 var fields = "?$select=snt_Street1,snt_Street2,snt_Street3,snt_City,snt_State,snt_Zip,snt_Country"
 
 if (entityid != null) {
  entityData = retrieveEntityById(entity,entityid[0].id,fields,UpdateAddressFields);
 } else {
  Xrm.Page.getAttribute("address1_line1").setValue(null);
  Xrm.Page.getAttribute("address1_line2").setValue(null);
  Xrm.Page.getAttribute("address1_line3").setValue(null); 
  Xrm.Page.getAttribute("address1_city").setValue(null);
  Xrm.Page.getAttribute("address1_stateorprovince").setValue(null); 
  Xrm.Page.getAttribute("address1_postalcode").setValue(null); 
  Xrm.Page.getAttribute("address1_country").setValue(null);   
 }

 Xrm.Page.getAttribute("address1_line1").setSubmitMode("always");
 Xrm.Page.getAttribute("address1_line2").setSubmitMode("always");
 Xrm.Page.getAttribute("address1_line3").setSubmitMode("always");
 Xrm.Page.getAttribute("address1_city").setSubmitMode("always");
 Xrm.Page.getAttribute("address1_stateorprovince").setSubmitMode("always");
 Xrm.Page.getAttribute("address1_postalcode").setSubmitMode("always");
 Xrm.Page.getAttribute("address1_country").setSubmitMode("always"); 
 
 AddressRender();
}

function UpdateAddressFields(entityData) {
 if (entityData != null) {  
  Xrm.Page.getAttribute("address1_line1").setValue(entityData.snt_Street1);
  Xrm.Page.getAttribute("address1_line2").setValue(entityData.snt_Street2);
  Xrm.Page.getAttribute("address1_line3").setValue(entityData.snt_Street3);
  Xrm.Page.getAttribute("address1_city").setValue(entityData.snt_City);
  Xrm.Page.getAttribute("address1_stateorprovince").setValue(entityData.snt_State);
  Xrm.Page.getAttribute("address1_postalcode").setValue(entityData.snt_Zip);
  Xrm.Page.getAttribute("address1_country").setValue(entityData.snt_Country);
 } 
}

function AddressRender() {
  -- Add function to form onload function
  var disabled;
  if (Xrm.Page.getAttribute("snt_addressid").getValue() != null)
   disabled = true;
  else
   disabled = false;
   
  Xrm.Page.getControl("address1_line1").setDisabled(disabled);
  Xrm.Page.getControl("address1_line2").setDisabled(disabled);
  Xrm.Page.getControl("address1_line3").setDisabled(disabled);
  Xrm.Page.getControl("address1_city").setDisabled(disabled);
  Xrm.Page.getControl("address1_stateorprovince").setDisabled(disabled);
  Xrm.Page.getControl("address1_postalcode").setDisabled(disabled);
  Xrm.Page.getControl("address1_country").setDisabled(disabled);  

}

For the sake of illustration, after the above jscript is implemented, when an address record is selected the fields are copied as shown:


Now we need to implement logic that will keep addresses in sync. For example, if the address shown in the screenshot is changed and there are 5 contacts that are linked to this address, then we want to make sure that the address fields are propagated to all 5 contact records. This is fairly easy to accomplish using the distribute workflow. The screenshots below show the 2 workflows required in order to configure this option.


The last piece of the puzzle requires a plugin. Basically at this juncture we can define addresses against the account entity that will appear in the lookup for contacts tied to this account:


But what about the address defined on the account itself? This in essence is the Primary address of the account and we also want it available for selection for the contacts. Therefore the plugin logic required here is to automatically maintain the PRIMARY address as shown above based on the values entered on the account form (as additional validation, you should also define jscript on the address entity to make the fields read only when the address name = PRIMARY and prevent manually keying in address with this name i.e. reserve PRIMARY for the plugin only).

The end result? A lookup on the contact form that allows you to select from the specified account addresses.



Tuesday, April 23, 2013

Handling Account and Contact Addresses

In my experience one of the most challenging issues to deal with in a CRM implementation is dealing with account and contact addresses. As is well known, the customer in CRM can either be the account or the contact. In a B2C business model, this is not likely to be a big deal as your contact record becomes the center of your universe and therefore all address information should likewise be on the contact record.

In a B2B business model, your customer is represented by the account records and the contacts (usually employees) belonging to these accounts. The challenge in this case is that you have address information on both account and contact records and the storage is neither efficient nor easy to maintain. At least out of the box. Before delving into the justifications for the above statement let's first stipulate that having the address information stored on the contact record in the default address fields (address1_line1 etc.) is important. The importance is because many default functions rely on this information. For example:


  • Syncing contacts to Outlook - if you don't have the address information stored on the contact, the address information will be missing on the synced contact
  • Mail merge - if you want to pull in contact address details while creating your merge documents, these will need to exist on the record

The reason why the storage of address information is not efficient is because in most cases, contacts of an account have the same address of the parent account (for example, generally with employees the address information for that employee is going to be the business address not personal address). Therefore storing it redundantly in 2 places is, well, redundant.

The reason why it is not easy to maintain is because it needs to be updated in multiple places. For example, let's assume you have an account with 5 contacts all with the same address information. If the business address changes then all 5 linked contact records should be updated as well. This of course can be done manually and perhaps even "optimized" by using tools such as the bulk edit tool (in which case the above scenario results in 2 manual steps) - however I am firmly of the opinion that if such updates are not automated the data will inevitably fall out of sync. Call me a skeptic but users can generally not be relied on to apply such manual procedures with rigor.

Fortunately if the scenario that you have resembles the one described above you can apply tools such as the Copy Utility or the Distribute Workflow to automate the updates. Both of these tools can be configured to copy down updates made from the account record to the contact records beneath them.

Frequently however companies have more than one office and the employees will be spread among these office. So simply applying a straight down copy will not work. A simple way to approach this is to use the account hierarchy whereby each office represents a separate account record with the relevant employees linked to each of the offices (with each having a separate address that will feed to the contact records). I would however argue that creating a separate account record solely to deal with the address issue is a little bit of overkill. And it results in disjointed information such that in order to "see" everything related to an account you need to view all account records and/or write custom reports in order to pull all the information together. Essentially you are creating "artificial" accounts in order to address the problem and I am not a fan of that. I believe that the account hierarchy should be used when the accounting requirements (reporting etc.) necessitate this. 

Aside from this, there are also other practical reasons where this approach can introduce problems. For example, if you have a mail merge requirement (or similar) whereby you want to pull the account name into the body of the letter, email etc. - the customer may not be too happy with the fact that letter head will go out from "ACME Medford" instead of just plain old "ACME". On the other hand, naming all these accounts with the same name of "ACME" will result in "duplicates" which will confound end users.

It would therefore be better to stick to a single account with multiple addresses and allow for the contact records to be associated to one of these account addresses. In the next post, I present an elegant approach to handling this requirement.

Friday, March 22, 2013

Searchable Property Updater

I may be a little late to the party but I've recently discovered the "Searchable Property Updater" tool and I have to say that this rates as one of my all time favorite CRM utilities.

Because it addresses a pretty much inevitable need. That is, if improving usability is a major concern in a CRM installation... Out of the box pretty much all fields are searchable. Most are irrelevant in a given implementation e.g. latitude and longitude on the account and contact record. In fact, I'd venture to say that most of the highlighted fields below are going to be irrelevant in most CRM implementations. And that's just a small snippet...


In this case, "usability" means "simplicity". And weeding out the irrelevant values really helps with keeping the UI clean and simple. Confusion being the enemy of a successful implementation, I personally consider this to be a very important tweak.

Of course in CRM you are able to weed these out albeit very ... t e d i o u s l y ... by updating the searchable property. And of course tedium often leads to the lack of execution - important or otherwise.


That's why this tool is so valuable. It removes the tedium and makes it what it should be - a quick and virtually painless effort.

I especially love the "Check Attr. on Forms" option. That is pure genius and really contributes to the painlessness of this task. In most cases, the fields that you have on your user form are the fields that you are going to want to query via Advanced Find. Or at the very least - it's a good starting point. And you can just click that to automatically select all the form fields and deselect everything else. Publish and you're done.



Brilliant! Better late than never but big kudos to the creator of this utility.

Monday, March 4, 2013

Views and Filters Toolkit for CRM Online

While on the subject of CRM Online compatibility - below is a link to the CRM Online compatible version for the "Views and Filters Toolkit" workflow plugin - another exceedingly useful utility. The one currently uploaded in CodePlex fails import into the CRM online environment.



Though there are many conceivable uses for this utility, I find that the ability to update the Offline and Outlook Filters without having to resort to programmatic techniques is perhaps the most useful. A simple way to centralize the maintenance of these download rules has long been a feature missing in the Dynamics CRM product and this utility surmounts this oversight.

Another shout out to Wayne Wittman for his conversion efforts (not to mention the efforts of Alberto Gemin who first published this utility).

As a related aside... another useful utility - namely the CRM 2011 Workflow Utilities - has also recently been updated in its CodePlex location with a CRM 2011 Online compatible version.

Friday, March 1, 2013

Distribute Workflow for CRM 2011 Online

I have spoken previously quite extensively about the virtues of the Distribute Workflow utility. The problem I encountered was that the version that is currently uploaded in CodePlex cannot be imported into CRM Online.Whenever I tried the import would fail as shown below:



But thanks to the efforts of the talented Wayne Wittman this very useful utility has now been made compatible with CRM Online. This utility can be downloaded using the link below.

Thursday, February 28, 2013

Announcement: CRM Grid Editor now freely available

Until now the Limited Edition CRM Grid Editor has been available as a free add on. This edition has been limited to working with out of  the box entities and views. As of today, I am pleased to announce that this restriction has been removed. You can now obtain a free and unlimited version of the CRM Grid Editor which can (drum roll) also be configured to work against custom entities and views.

The CRM Grid Editor works with all versions of Dynamics CRM - on premise, online and IFD.

You can get an overview of product capabilities by viewing the CRM Grid Editor Summary video. You can also view the product installation video.


In order for you to be able to use the product it is necessary to generate a license file for your installation. To obtain a license please send me an email (see details in Contact tab) with the following information:

On Premise
Provide your CRM server URL and organization as shown in the screenshots below.



Online:
Specify the org name that you are using to access CRM i.e.: https://orgname.crm.dynamics.com



Key features:
  • Simple and intuitive Excel-like interface 
  • Edit all editable CRM fields returned in view (all data types handled) 
  • Based on CRM view definitions – no additional setup required 
  • Bulk update of records – make changes on the fly without having to save between row edits 
  • Launch Grid Editor from context sensitive CRM user interface or as a fully navigable standalone application 
  • Drill down to child records from a parent record – not limited to the associated view! 
  • CRM Security model enforced – you cannot do anything with the Grid Editor that cannot be done through the standard CRM user interface 
  • The Grid Editor can be configured to work against all CRM entities and views – including custom entities and personal views 
  • Tabbed interface allowing multiple different views to be open at the same time 
  • Ability to define and save "GE Views" which allows for advanced pivot table-like grouping and filtering 
  • Add new records using the CRM standard, quick or the new Grid Editor view sensitive add-new form 
  • Run CRM workflows against selected records


Other features include:
  • Deactivate and Activate records in line 
  • Manually execute CRM workflows 
  • Link back to CRM records with a single click 
  • Advanced additional filtering and sorting options 
  • Easy to identify color for changed cells, columns 
  • Dynamic export to Excel 
  • Supports opportunity and case close forms 
  • Supports Drag and drop editing and Undo Changes Requirements:

CRM 2011 Copy Utility


This plugin allows you copy fields from a parent record to all child records in a 1:N relationship. For example, this plugin can be used to copy the address fields of an account to all the contacts of that parent account (a common scenario).

Out of the box you are able to map fields from a parent entity to a child entity when the child record gets created. However beyond this point in time if details are changed on the parent record they will no longer be copied to the child records. Similarly there is no out of the box capability to copy changes made to the parent record down to all child records.

This plugin allows this option to be configured. You are able to configure which 1:N relationship this plugin will work against as well as the fields that are to be mapped between the parent and child entities. The plugin does not validate attribute types so you will need to ensure in your configuration that the fields be mapped are of the same type.

The following is a walk through of how to configure this plugin:

  • Download the DLL from here
  • Use the Plugin Registration tool to connect to your environment and register the plugin
  • Register a New Step 
    • Message: Update
    • Primary Entity: Parent entity
    • Remaining settings: Optional


  • In the Unsecure Configuration update the XML to specify the fields to copy
    • <setting name="source"> : parent entity name e.g. account
    • <setting name="sourceid"> :  parent entity primary key e.g. accountid
    • <setting name="target">  : child entity name e.g. contact
    • <setting name="targetid"> : child entity foreign key e.g. parentcustomerid
    • <setting name="numberoffield"> : total number of fields being copied
    • <setting name="src_fieldn"> : name of source field to copy
    • <setting name="dest_fieldn"> : name of corresponding target field to be copied too
Note: There is no validation of any of these fields. You need to ensure that:

    • All the field names are correct
    • The corresponding "src" and "dest" fields have the same definition 

For example, the following XML can be used to copy the address fields from the parent account record to the child contact records: 
<Settings>
<setting name="source">  
<value>account</value>
</setting>
<setting name="sourceid">  
<value>accountid</value>
</setting>
<setting name="target">
 <value>contact</value>
</setting>
<setting name="targetid">
 <value>parentcustomerid</value>
</setting>
<setting name="numberoffield">
 <value>8</value>
</setting>
<setting name="src_field1">
 <value>Address1_line1</value>
</setting>
<setting name="src_field2">
 <value>Address1_line2</value>
</setting>
<setting name="src_field3">
 <value>Address1_line3</value>
</setting>
<setting name="src_field4">
 <value>Address1_city</value>
</setting>
<setting name="src_field5">
 <value>Address1_stateorprovince</value>
</setting>
<setting name="src_field6">
 <value>Address1_postalcode</value>
</setting>
<setting name="src_field7">
 <value>Address1_country</value>
</setting>
<setting name="src_field8">
 <value>Address1_telephone1</value>
</setting>
<setting name="dest_field1">
 <value>Address1_line1</value>
</setting>
<setting name="dest_field2">
 <value>Address1_line2</value>
</setting>
<setting name="dest_field3">
 <value>Address1_line3</value>
</setting>
<setting name="dest_field4">
 <value>Address1_city</value>
</setting>
<setting name="dest_field5">
 <value>Address1_stateorprovince</value>
</setting>
<setting name="dest_field6">
 <value>Address1_postalcode</value>
</setting>
<setting name="dest_field7">
 <value>Address1_country</value>
</setting>
<setting name="dest_field8">
 <value>Address1_telephone1</value>
</setting>
</Settings>


  • Register a New Image called "postEntityImage"


  • Test and ensure that it is working




CRM 2011 UR12 and SharePoint Integration

After having applied the Polaris upgrade (update roll-up 12) the native CRM/SharePoint document integration stopped working. There were two different ways in which this issue manifested itself:


  1. When clicking on the Documents link we just received the "spinning wheel" and the page did not load
  2. On the occasion that it did succeed to load, the "New" and "Add" buttons did not work i.e. nothing happened at all when clicking those buttons preventing new documents from being added.

As I discovered, it is apparently necessary to update the List Components of SharePoint following the application of UR12. The updated List Components can be downloaded from the following location: 


This issue is also described in the following KB article along with the steps required for installing the List Component:


Wednesday, February 27, 2013

Xrm.Utility.openEntityForm no longer working

*** Update 3/5/2013: This issue has now been fixed via a Cloud update ***

Other known issues with UR12 can be tracked here: 
https://community.dynamics.com/crm/b/dynamicscrmsupportblog/archive/2013/01/28/december-2012-update-has-been-released.aspx

******

If you have a CRM Online installation and the Xrm.Utility.openEntityForm jscript function which you *knew* was previously working but apparently is no longer.... well, the good news is that you're not going mad. The bad news is that it is indeed a bug that has been introduced in the current (as of this writing) version of CRM Online. Or as confirmed from Microsoft:


Issue Definition: Xrm.Utility.openEntityForm does not work anymore since Update 5.0.9690.3339 on the CRM Online organization.
I have confirmed that this is an issue with the latest update that has been done with the CRM Online organizations. We are currently working on the code review and should have the fix in place shortly. 


The Microsoft support guy I spoke to indicated that this would likely be fixed within a week although I do not have a definitive date.

For the sake of reference below is the text of the error received when trying to use this form open utility.






Wednesday, January 16, 2013

Cannot sign in to CRM Online: LiveDevice.xml

Ok. I have stumbled on this too many times. Each time I think I'll remember for the next time and then, lo and behold, I spend time scratching my head trying to figure out what the issue is. So it's time I wrote it down - if nothing else for my own sake as a reference post (hopefully I'll remember that I wrote up this post!).

What am I talking about?

Trying to connect to CRM Online. It happens on occasion that I am denied access to CRM Online when trying to connect from a 3rd party application when I'm certain that my credentials are correct. For example, below is what happens when trying to connect using the Visual Ribbon Editor tool.



Usually I spend time thinking that maybe my URL is incorrect. Maybe I should have left off the http/https. Maybe I should use the Organization Unique Name (appearing under Developer Resources in CRM) instead of the "friendly name" in front of the crm.dynamics.com etc. etc.

The solution of course is simply to delete the LiveDevice.xml file from under the Root\Users\<user>\LiveDeviceID folder and then to try and sign in again... Voila!

Perhaps now I'll remember for next time? Let's hope so...


Tuesday, January 15, 2013

Duplicate Field Mapping/Defaulting


I encountered a scenario where whenever a child entity is created from within the context of a parent entity, the mapping automatically creates two references from the parent entity on the child entity.


The reason for this is due to the fact that the parent entity is related in a 1:N relationship to the child entity more than once:


And each of these relationships automatically creates a mapping that resembles the one below. That is both mapping contain a link from the parent entity to each of the foreign keys from the child entity and you are unable to delete these as they are required by the CRM platform.


Moreover any mapping added to one of the relationships is automatically added to the other relationship (and vice versa). I can't figure out if this is absolutely necessary from a technical design perspective or if this is just a design flaw. I suspect the latter because I can't come up with a good reason for this design logic. Or it could just be something specific that managed to sneak into the environment I'm working in.

The major issue is that this results in a confusing design because both fields get defaulted when only one should depending on the navigational context i.e.:
  • When navigating from "Entities Owned" the "Entity" field should be defaulted from the parent entity whereas the "Is Invested In" entity should remain null
  • Conversely, when navigating from "Investors in this Entity" the "Is Invested In" field should be defaulted from the parent entity whereas the "Entity" should remain null



As standard field mapping seems to rule out the ability to differentiate between these two different contexts, the only way to do so would seem to be by passing in custom parameters which is an example of when this technique is absolutely required.

To do so, the form was modified to remove the default "Add New" buttons and replace with custom Add buttons (which as an aside also provides the ability to give much clearer names to the add operation). The Ribbon Workbench Tool ably supports this kind of configuration. Each of these buttons simply calls the Xrm.Utility.openEntityForm to open the same child form button, but passes in parameters to set the lookup fields as required in each case.



Access Main form fields from Nav Area via Jscript

Let's say you need to access form fields via jscript when you have navigated to one of the navigational links on the entity. For example, when the account form is open but you are currently on the "More Addresses" navigation.


A typical scenario for such a requirement is if you have configured a custom button on the sub-grid ribbon that links to a jscript function where you need to pass through some parameters that are set from fields on the main form.

If you try and access the fields using the standard Xrm.Page approach while in the above navigational scenario, your jscript will error out. Like so:

function CustomAction() {

 var parameters = {};
 parameters["snt_customid"] = Xrm.Page.data.entity.getId();
    parameters["snt_customidname"] = Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 
 Xrm.Utility.openEntityForm("snt_custom", null, parameters);

}

The solution is pretty much the same as how page elements are referenced from html web resources linked to a form i.e. using the prefix of "window.parent". Therefore updating the function as follows should do the trick:

function CustomAction() {

 var parameters = {};
 parameters["snt_customid"] = window.parent.Xrm.Page.data.entity.getId();
    parameters["snt_customidname"] = window.parent.Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 
 Xrm.Utility.openEntityForm("snt_custom", null, parameters);

}

Update:

The above design has one flaw and that is if you decide to use a sub-grid to bring in the navigation link into the main body of the form. When you do so, the same sub-grid menu shows up when you click on the sub-grid but there is one essential difference...



In the case of the former, the main form is no longer in view and therefore you cannot access the form elements (Xrm.Page.data) without using the "window.parent" prefix. I you don't use this prefix you will receive a jscript errror.

In the case of the latter the main form is still in view and therefore Xrm.Page.data is accessible. And therefore if you use the "window.parent" prefix you will receive a jscript error.

In order to cater for both scenarios the solution is quite simple - simply use the try/catch exception handling feature. This way if an exception occurs it will go to the alternate access method which will then succeed.

function CustomAction() {

 var parameters = {};
 var id;
 var name;
 try {
  // if called from Nav Link navigation
  id = window.parent.Xrm.Page.data.entity.getId();
  name = window.parent.Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 } catch (e) {
  // if called from Embedded Grid navigation
  id = Xrm.Page.data.entity.getId();
  name = Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 }
 parameters["snt_customid"] = id;
 parameters["snt_customidname"] = name;

 Xrm.Utility.openEntityForm("snt_custom", null, parameters);
}

Wednesday, December 26, 2012

getClientUrl() to replace getServerUrl()

It appears that the javascript getServerUrl() function will become deprecated with the release of Update Rollup 12 for CRM Dynamics 2011 (imminent). As far as I can tell, from that point forward there will be a new function called getClientUrl() that should be used instead.

No doubt the reason for the deprecation is due to the fact that the getServerUrl() function cannot always be relied upon to return the correct server context as described in an earlier post (and therefore the workaround solution described in that post should no longer be necessary).

We'll have to get back to this to confirm once the rollup has been officially released.