Friday, March 28, 2008

Removing the Price List field from an Opportunity

The Price List field is an interesting field in that it is a field has script that fires from it but you cannot see it from the form editor where you place your own scripts. I recently tried removing this field from an Opportunity and the Account form. The Account didn't show any repercussions but the Opportunity wouldn't allow me to add an Opportunity Product to the Opportunity.

With the Price List field missing I just got this random non-descript scripting error. When I added the field back and tried to add an Opportunity Product I received the error I expected of having no Price List set. The problem now became that when I tried to select a Price List for the Opportunity I couldn't see any Price Lists to choose from. Basically the script that was running and populating the filtered list of Price Lists had been removed when I removed the field from the form and it doesn't come back when I put it back onto the form.

The question now became how do I fix it? How do I get that script back. I have to give credit to Nahi for his post in the Microsoft.Crm.Public group who ran into this issue and posted the solution he found. He didn't go into the details, he just went over the process. I'm going to try and give you a little bit more.

Basically the idea was, grab the script from another entity or another system where the Price List had not been removed. The way to do that is to export the Opportunity and look through the entity XML for the missing script. Then copy and paste the script XML into the entity XML of the entity that had the Price List field removed from it. Save changes and reimport it back into CRM, publish and you're set. It worked great for me and I now no longer have any issues with the Opportunity, Price List or adding Opportunity Products.

The best way to find the script from the clean entity XML is to search for "Price List" and then look through to where you see the script next to one of your finds for "Price List". Here's the XML block that I found and added (it should go between the LABEL and CONTROL tags):

<events>
<event name="setadditionalparams" application="true" active="true">
<script>
<![CDATA[
var oLookup = event.srcElement;
AddTransactionCurrencyParam(oLookup);]]>
</script>
<dependencies>
<dependency id="transactioncurrencyid" />
</dependencies>
</event>
</events>

Hope this helps someone else out.

David Fronk
Dynamic Methods Inc.

Friday, March 21, 2008

CRM Document Management...the low budget way

For those of you who work in the small to medium business industry, keeping tabs on budget is key, and typically (though not always) you are asked to do deliver a hot rod on a moped budget, and/or you have to deliver in half the time you would like to spend to make the solution really solid.

When this is the case for my clients I do my best to give them something usable that may or may not do 100% of everything they requested but will get them by until they are ready to spend the time and money on the complete solution.

When implementing MSCRM, document management is one of the topics where "baby steps" are necessary. I am a fan of SharePoint for document management, most of my clients are as well. However, as soon as they see that rolling out SharePoint is a whole other implementation, with software, licenses, server, installation, design, training, etc, I find out that they aren't as big of fans as was originally thought. From a solution implementators' perspective, that's totally fine. The goal that I then have is to help get them ready for the complete solution, just when they as a company feel ready to make the jump.

In the mean time, the company still needs to have some kind of document management and of course they want it either directly inside of MSCRM or to integrate with it. Here are my "low budget solutions" for document management.

1. Use and FTP site
2. Use a file server, or a shared folder on a server with a sufficient amount of disc space

Now, remember this is "low budget" so that means that there will be a number of manual steps but keeping those to a minimum will go a long way. Also, these are work-arounds, not full solutions, so there will be limitations to each solution. These solutions are very similar upon implementation, it's just a matter of where the data is stored.

Using an FTP site will allow users to store files while local or remote to the network, whether the FTP site is hosted by the company or someone else will dependent on the needs are desires of the company you are implementing the solution for. You can then embed the FTP site in an IFRAME on a specific form or put a link on the left NavBar, or give them a button that will open it in a new window. However, opening to the main page of an FTP site (whether they have to log in or not is entirely up to you) then makes the users have to click more in order to drill down to the level at which he/she wants to store a given document. Here's the customization that can go a long way.

I did this as a button but the concept is the same for a NavBar item as well. Using an IFRAME is a bit different, but I'll go over an IFRAME solution shortly.

In the isv.config.xml document on the CRM server add a button to the desired form similar to the following XML:

<Button Title="FTP" ToolTip="View product images stored on FTP site" Icon="/_imgs/ico_18_debug.gif" JavaScript="window.open('ftp://fptserver/Clients/' + crmForm.all.name.DataValue + '/')" PassParams="0" WinParams="" WinMode="0" />

Using this code will require that the folder structure on the FTP site is maintained so that there is a folder for each client under a Client folder but users are now able to get to shared documents for a given client (or opportunity, product, project, etc) with a single click. For the end users, this is fantastic. For the admin to administrate this may not be as fun however. But through custom code one could maintain the folder structure if necessary. Again it depends on budget and timing.

Also, take note that you will need to use the JavaScript attribute because the Url attribute expects an HTTP link. Lastly, note that it will be absolutely necessary for you to put the "ftp://" full URL in the code, otherwise the link will be assumed to be HTTP.

Now, for a shared directory. The limitation here is that users have to be local, or on a VPN in order to access it. If that isn't that big of a deal to the client then this can be great solution for end users.

First, on the entity you are using, add an plain text attribute called "Shared Folder". Place it where you want on the form (I suggest a tab solely dedicated to file sharing, but to each his own). Then create an IFRAME. Name it FileShare (or whatever you want), allow for cross frame scripting, hide the display name, etc. Set the URL to be "about:blank".

Now add script to the onLoad of the form. Add the following:

if(crmForm.all.new_sharedfolder.DataValue != null && crmForm.all.new_sharedfolder.DataValue != "")
{
document.all.IFRAME_FileShare.src = crmForm.all.new_sharedfolder.DataValue;
}
else
{
document.all.IFRAME_FileShare.src = "about:blank";
}

Then all users have to do is type in the UNC path, or directory path to the directory desired. This could be scripted upon the creation of a form for a static path with a dynamic name at the end, much like the FTP example from above, but again remember that the folder structure will need to be maintained one way or another.

Examples:
UNC: \\fileservername\Clients
Directory path: Z:\Clients

If you use the directory path option then you will have to make sure that the drive is mapped correctly on the user's machine, otherwise it will break. Otherwise UNC works every time (as long as the computer is on the network).

There isn't any auto version control but it gives the users a place to store documents "inside" MSCRM in a form that they are used to and comfortable with. Once they out grow this then it's time to move onto a real document management solution. But until then, happy "low budget" coding!

David Fronk
Dynamic Methods Inc.

Saturday, March 15, 2008

Allowing QuickFind to search Inactive records

In Microsoft CRM v3.0 there were certain entities that were allowed to search inactive records through the QuickFind/QuickSearch feature (the "Look For" query at the top of each main entity listing). Rollup 2 changed some of those entities that were capable of searching inactive records and now CRM v4.0 has made is so that the QuickFind/QuickSearch does not look for inactive records at all in any entities (at least in my testing, this is what I have found, if someone has found otherwise please let me know). Now, I won't say that I want this capability for every entity in the system but sometimes it might be nice. For instance, Cases has been an entity that has been requested that be allowed to search both active and inactive records.

What would be nice would be if there were some option that users could choose or set in their preferences, or even at the entity level, to allow or disallow the ability to search both active and inactive records. Something ask for in v5.0.

But until then there is hope. Depending on what version of CRM you are on depends on the solution you can implement.

CRM v3.0 involves modifying entity XML. CRM v4.0 requires a Plugin be written on the PreRetrieve method.

Today I'm going to talk about the CRM v3.0 modification. Technically this customization will work in CRM v4.0 as well, but let me be very clear when I state, THIS IS AN UNSUPPORTED CUSTOMIZATION. I cannot gaurantee that this will will break your upgrade or not, though I am very inclined to say that it will. Also, any repair or update could potentially undo your change as this customization requires you to modify the actual entity XML. So, if a patch or rollup doesn't include the entity XML then you should be pretty safe.

Steps:
1. Export the entity XML of the entity you wish to modify.
2. Copy your XML export file so that you have a backup to roll back to in case something goes wrong.
3. Open the XML file in your editor of choice and search for "Quick Find".
a. NOTE - don't let the XML fool you, it puts the name at the bottom of the "SavedQuery" XML chain, what you will be modifying should be above the name tag.
4. Remove the following filter from the Quick Find saved query:

<filter type="and">
<condition attribute="statecode" operator="eq" value="0" />
</filter>

5. Locate the "iscustomizable" tag and change the "name" attribute to say "Yes" instead of "No".
6. Save your modified XML file
7. Upload your modified XML file into CRM
8. Publish Customizations
9. Refresh your page and test

That's it. Personally I think is a pretty safe "unsupported" customization because the worst that will happen in a repair or upgrade is the entity XML will get replaced. The customization is pretty localized to the Quick Find. If you modify other pieces of the entity XML you're on your own.

Good luck!

David Fronk
Dynamic Methods Inc.

Friday, March 07, 2008

Hiding a Section

Hiding a section has been an interesting customization to figure out. I definitely got help by reading through posts and groups, but then again, who doesn't? When hiding things in CRM typically it's done by referencing a specific control name, something like the tab number or the field name. Sections are strange in that they don't have any of the normal "English" identifiers that one would expect. It's actually reference directly through a GUID. Each section gets assigned a GUID upon creation, so writing code that is dynamic for hiding sections directly really wasn't pretty...or possible.

However, if you reference a section indirectly things actually get much easier. For example, everything in the CRM forms are contained within something larger. Fields are contained by Sections, which are contained by Tabs, which are contained by the web form itself. That means that there is a direct parent/child relationship between objects on the form. JScript has thought about this and allows you to reference ParentNode's through code.

With this new knowledge referencing sections actually becomes very easy. All that has to be done is make a reference to a field on the form and then get it's parent node and do what you need to do to it.

Here's the code:

document.getElementById("ownerid").parentNode.parentNode.style.display = "none";

You'll notice that I have to parentNode references, that's not a mistake, this is the progression on how to get to the section. Fields are actually contained by Labels and Labels are contained by Sections on a form. I didn't realize this until I tried using only the first parentNode reference. Play with it a bit and you'll see what I mean.

Then to make the section visible again just use empty quotes:

document.getElementById("ownerid").parentNode.parentNode.style.display = "";

And now you have full control over what to make visible to users upon any condition you want.

Happy coding!

David Fronk
Dynamic Methods Inc.