2009/04/30

Coding CRM Lookup Fields

Coding CRM Lookup Fields
A Lookup is a reference or a pointer to another record (GUID) in crm. As opposed to other crm types, lookups are handled differently in many aspects. One “interesting” aspect which I relate to here is how a lookup DataValue is assigned when used in JavaScript.

The lookup DataValue is actually an array which holds a “lookup item” object or objects. Each lookup item exposes a set of properties we need to address in code for crm to acknowledge
its value. In reality an entity with a lookup is comprised of 3 table columns, so for example the primary contact lookup field on the account entity is actually a combination of primarycontactid (guid “{1234…}”) , primarycontactidname (name “Adi Katz”) and primarycontactiddesc columns in the database.

Now, crm only cares about GUIDs so until we start calling our selves by GUID (and not by NAME) we need to bridge that gap. Ms does relate to this gap, but only from the UI / End user perspective (auto complete feature). Unfortunately this ability is not exposed through code in any supported way and we need to build that bridge (automate the process) our selves using custom Ajax calls.

In the simplest scenario the lookup DataValue requires only a GUID (id) and entity name(typename). For example:
view plaincopy to clipboardprint?


1. var LookupItem = new Object();
2. LookupItem.name = “Crm only cares about GUIDs (pls Save...)”;
3. LookupItem.id = “{D04D44AD-36D0-DC11-AA32-0003FF33509E}”;
4. LookupItem.typename = “contact”;
5. crmForm.all..DataValue = [LookupItem];


The problem is that we usually don’t know the entity id (GUID) only its name e.g.
view plaincopy to clipboardprint?


1. var LookupItem = new Object();
2. LookupItem.name = “Adi Katz”;
3. LookupItem.id = “”;
4. LookupItem.typename = “contact”;
5. crmForm.all..DataValue = [LookupItem];


And of course this won’t work unless we lookup “Adi Katz" GUID by name and assign
it to the LookupItem.id.

The following “LookupHelper” class facilitates / automates the process by providing
the following features:

If “Adi Katz” does not exist then a clean lookup dialog is opened.
If “Adi Katz” exists more then once a lookup dialog is opened with the name “Adi Katz” inside the search box.
If one “Adi Katz” exists then the lookup is assigned a valid DataValue

The Lookup Helper works with all lookup types e.g. (single entity, customer, regarding and party lookup).

In all lookups, setting the entity name will set the lookup defaulttype property so when you open the lookup dialog the entity being searched is set in advance.

The OnCrmPageLoad bellow describes the How-to and possibilities you might encounter
in your daily “lookup programming”.
view plaincopy to clipboardprint?
1. var curLookup;
2. function OnCrmPageLoad()
3. {
4. /*
5. - Both GUID (id) and name are known
6. - entity name is taken from the lookup
7. */
8. curLookup = new LookupHelper( "transactioncurrencyid" );
9. curLookup.SetValue( "US Dollar" , "{944038FC-65C1-DC11-B67A-0003FFBB057D}" );
10.
11. /*
12. - Search Mode
13. - Open the lookup dialog window
14. */
15. curLookup = new LookupHelper( "transactioncurrencyid" );
16. curLookup.SetValue("");
17. /*
18. - only GUID (id) is known (usually not the case)
19. - name is a temporary text until you save the record
20. */
21. curLookup = new LookupHelper( "transactioncurrencyid" );
22. curLookup.SetValue( "" , "{944038FC-65C1-DC11-B67A-0003FFBB057D}");
23. /*
24. - only name is known, the id is fetched from crm
25. - must provide PrimaryField of the lookup entity in this case currencyname
26. */
27. curLookup = new LookupHelper( "transactioncurrencyid" , "currencyname" );
28. curLookup.SetValue( "US Dollar" );
29. //OR
30. curLookup = new LookupHelper( "transactioncurrencyid" );
31. curLookup.PrimaryField = "currencyname";
32. curLookup.SetValue( "US Dollar" );
33. /*
34. - Customer Lookup
35. - Change from account to contact entity
36. - the id is fetched form crm
37. */
38. curLookup = new LookupHelper( "customerid" );
39. curLookup.SetEntity( "contact" , "fullname" , "Adi Katz" );
40. //is the same as
41. curLookup = new LookupHelper( "customerid" );
42. curLookup.SetEntity( "contact" );
43. curLookup.PrimaryField = "fullname";
44. curLookup.SetValue( "Adi Katz" );
45. /*
46. - getting the actual lookup field
47. */
48. curLookup = new LookupHelper( "customerid" );
49. alert(curLookup.Lookup.DataValue[0].name); //assuming datavalue exists.
50. }
51.
52. /*
53. Parameters:
54.
55. lookupId - Mandatory, The lookup schema name.
56. primaryField - Optional, Required for fetch. can be set here or later
57. entity - Optional, Taken from the lookup control
58. */
59. function LookupHelper( lookupId , primaryField , entity )
60. {
61. //Lookup Reference
62. this.Lookup = document.getElementById( lookupId );
63. //Checks if the lookupId is valid
64. if(isUndefined(this.Lookup))
65. return alert('lookup ' + lookupId + ' is undefined');
66.
67.
68. /* Private Fields */
69.
70. var Instance = this;
71. var data = this.Lookup.lookuptypenames.split(',')[0].split(':');
72. var Entity = isUndefined(entity)? data[0]:entity;
73. var PK = Entity + "id";
74.
75. /* Public Fields */
76.
77. //Holds the lookup text e.g. Adi Katz
78. this.Text = "Value Selected (Save...)";
79. //Holds the default operator for the fetchxml condition e.g. 'fullname eq "Adi Katz"'
80. this.Operator = "eq";
81. //Holds the lookup entity primary field e.g. contact = fullname , account = name
82. this.PrimaryField = primaryField;
83.
84. /*public Functions
85.
86. Descrition:
87.
88. Sets the Entity and PrimaryKey - Required For Fetch
89. Sets the Primary Field - Required For Fetch
90. Sets the Default Lookup Type - Feature for lookup popup window
91. If the text is supplied then the id is Fetched from crm
92.
93. Parameters:
94.
95. entity - Mandatory, Required For Fetch
96. primaryField - Mandatory (unless supplied in the ctor),Required For Fetch
97. text - Optional
98. */
99.
100. this.SetEntity = function( entity , primaryField , text )
101. {
102. Entity = entity;
103. PK = entity + "id";
104.
105. if(!isUndefined(primaryField))
106. Instance.PrimaryField = primaryField;
107.
108. var regDefType = new RegExp(entity + ":(\\d+)","gi");
109. regDefType.exec(Instance.Lookup.lookuptypenames);
110. Instance.Lookup.defaulttype = RegExp.$1;
111.
112. if(!isUndefined(text))
113. Instance.SetValue(text);
114. }
115.
116. /*
117. Description:
118. Sets The lookup DataValue
119.
120. if text is undefined and guid is supplied then
121. use default text e.g. 'Value Selected (Save...)'
122. else
123. use supplied text
124.
125. if guid is supplied then
126. set the lookup datavalue
127. else if text is undefined then
128. open clean lookup dialog
129. else
130. fetch for lookup item id
131. if 1 record is returned then set datavalue
132. else
133. open lookup dialog with specified search condition
134.
135. Parameters:
136. guid - record id - global unique identifier
137. text - name displayed inside the lookup
138. */
139. this.SetValue = function( text , guid )
140. {
141. if(!isUndefined(text))
142. Instance.Text = text;
143.
144. if(!isUndefined(guid))
145. {
146. var LookupItem = new Object();
147. LookupItem.name = text;
148. LookupItem.id = guid;
149. LookupItem.typename = Entity;
150. Instance.Lookup.DataValue = [LookupItem];
151. }
152. else if(isUndefined(text)) Search( "" );
153. else LookupValue( text );
154. }
155.
156. /* Clear Lookup Value - DataVale = null */
157. this.Clear = function()
158. {
159. Instance.Lookup.DataValue = null;
160. }
161.
162. /* Private Functions
163.
164. builds a valid Fetchxml for the current lookup type
165. fetch value and ( set datavalue or open lookup dialog )
166. */
167. function LookupValue( text )
168. {
169. var xmlHttp = CreateXmlHttp();
170. var xml = "
";
174.
175. xml += GenerateAuthenticationHeader();
176. xml += "
";
177. xml += "";
179. xml += "";
180.
181. var fetchxml = "";
182.
183. fetchxml += "";
184. fetchxml += "";
185. fetchxml += "";
186. fetchxml += "";
189. fetchxml += "
";
190. fetchxml += "
";
191. fetchxml += "
";
192.
193. xml += _HtmlEncode(fetchxml);
194. xml += "
";
195. xml += "
";
196. xml += "
";
197. xml += "
";
198.
199. xmlHttp.open("POST", "/mscrmservices/2007/crmservice.asmx", false );
200. xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
201. xmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
202. xmlHttp.send(xml);
203.
204. var resultDoc = loadXmlDocument(xmlHttp.responseXML.text);
205. var resultRecords = resultDoc.selectNodes("//" + PK );
206.
207. if( resultRecords.length == 1 )
208. {
209. var guid = resultRecords[0].text;
210. Instance.SetValue( Instance.Text , guid );
211. }
212. else
213. {
214. Search( Instance.Text );
215. }
216. }
217.
218. /* Fill search
219. condtion and open lookup dialog */
220.
221. function Search( text ){
222. Instance.Lookup.AddParam("search" , text );
223. Instance.Lookup.click();
224. }
225.
226. /* Checks object definition */
227. function isUndefined(obj){
228. return obj == null typeof(obj) == "undefined" obj == "";
229. }
230. }
231.
232. OnCrmPageLoad();

2009/04/16

More CRM Jscript Code Snips!

Replacing the content of an IFRAME
If you really want to do some funny things in your CRM form, you can create an IFRAME serving as a placeholder for your real HTML code. Create an IFRAME in an entity and name it "IFRAME_TEST". In the Onload event put the following code:
crmForm.all.IFRAME_TEST_d.innerHTML ="Some HTML text";
Note the "_d" at the end of IFRAME_TEST. This single line replaces the whole IFRAME element with your own HTML.


Adding additional values to duration fields



The following script adds the new option "42 Minutes" to the actualdurationminutes field in a task:

var duration = crmForm.all.actualdurationminutesSelect;

var tables = duration.getElementsByTagName("table");
var table = tables[1];
var row = table.insertRow();
var newOption = row.insertCell(-1);
var newValue = "42 Minutes";
newOption.setAttribute("val", newValue);
newOption.innerText = newValue;


Changing the title of a CRM

formdocument.title = "Hello World!";

Changing the Link of a Ticker Symbol from MSN Money to Yahoo Finances

This is an unsupported solution as it requires modifications to server files!Open the following directory on your CRM server:
C:\Inetpub\wwwroot\_forms\controls (may vary depending on your installation)
Open the following file: INPUT.text.ticker.htc
Search the following function:

function Launch()
{
if(value.length > 0)
{
Parse();
window.open("

http://go.microsoft.com/fwlink?linkid=8506&Symbol=" + encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) + ",width=" + (screen.availWidth * .75) + ,scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
}}Replace it with the following:function Launch()
{
if(value.length > 0)
{ Parse();
//window.open("http://go.microsoft.com/fwlink?linkid=8506&Symbol=" + encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) + ",width=" + (screen.availWidth * .75) + ,scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
window.open("http://finance.yahoo.com/q?s=" + encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) + ",width=" + (screen.availWidth * .75) + ",scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
}}

Save the file.On your client machine: Open Internet Explorer and remove all temporary internet files, so that the new htc source is retrieved. Navigate to any ticker symbol, enter a valid value and double click it.


Counting the number of backslashes in a string

Of course you can use the two code snippets with any character you like.
Solution 1

var text = "hardware\\printers\\Epson";var paths = text.split("

\\");alert(paths.length -1);

Solution 2

var text = "hardware\\printers\\Epson";
var numBs = 0;
var index = 0;while(index != -1)
{
index = text.indexOf("\\", index);
if (index != -1)
{
numBs++; index++; }}alert(numBs);


Changing the label of a field at runtime

Lets say you want to change the label of the revenue field, then you simply write:

crmForm.all.revenue_c.innerText = "Amount Euro";

Simply append the "_c" to the field name. This should work for most labels.

Changing the colour of a single option in a picklist

You can change the colour and background colour of an option element using the following syntax:
//TODO:
Replace with the schema name of the picklist in question.
var list = crmForm.all.;
//using the first option here, you need to find the one you need.
var option = list.options[0];
//Set the background colour to red.
option.style.backgroundColor = "#FF0000";
//Set the text colour to white.
option.style.color = "#FFFFFF";

Opening a new window without the IE menu and status bars

The following is a sample HTML page that you can use to test. Replace the link with the URL of your web page.

return false">Domain

Retrieving the text of a lookup control

var lookup = crmForm.all..DataValue;
if (lookup[0] != null)
{
var theText = lookup[0].name;
}

Disabling a form at runtime

document.body.disabled = true;

This will disable all controls except the Notes tab, where you still can enter new notes.
Automatically changing the value of a text field to capitalsPut the following script in the OnChange event of the text field:
crmForm.all..DataValue = crmForm.all..DataValue.toUpperCase();

Getting notified when the user selects a another tab on a form

Put the following in the OnLoad event of your form:

crmForm.all.tab0Tab.onclick = function()
{

alert("Tab 0 clicked");
}crmForm.all.tab1Tab.onclick = function()
{
alert("Tab 1 clicked");
}

It seems that the tab pages are always named "tabxTab", where x is a number starting from 0 (first tab) to (n-1), where n is the total number of tabs. Maybe you find better events that handle the activation instead of a click, but that's the general way it should work.
The current record idThe primary key is always available in crmForm.ObjectIdIt is set to null, if the record hasn't been saved yet. There are some other useful properties. Look at the "Form Object Model" in the SDK docs for more information.
The current object type codeThe type code of the entity being displayed in a CRM form is available through
crmForm.ObjectTypeCode
There are some other useful properties. Look at the "Form Object Model" in the SDK docs for more information.

Initializing a date field with the current date

crmForm.all..DataValue = new Date();

Changing the URL of an IFRAME at runtime

Set the initial URL of the IFRAME to about:blank and execute the following line:

document.all.IFRAME_.src =
http://domain/file;

Displaying a picture in a CRM form,
Using a text field to store the data source ,
Let's say you have a custom field storing the image name called new_pictureName. Then in the OnLoad event do the following:

if ((crmForm.all.new_pictureName != null) && (crmForm.all.new_pictureName.DataValue != null))
{
document.all.IFRAME_.src =
http://server/dir/images/ + crmForm.all.new_pictureName.DataValue;
}
In the OnChange event of the new_pictureName place the following:
if (crmForm.all.new_pictureName.DataValue != null)

{
document.all.IFRAME_.src = http://server/dir/images/ + crmForm.all.new_pictureName.DataValue;
}
else
{ document.all.IFRAME_.src = "about:blank"
}

Checking crmForm.all.new_pictureName for null in the OnLoad event is a sanity check. Bulk edit forms will not display all fields and if it is not contained in the form, it will be null. This check is not required in the OnChange event, as this event will never fire if the field does not exist.

Changing the default lookup type

When opening a lookup that can select from multiple entity types, you may want to use a different default entity in the lookup dialog. The following script changes the default entity of the regardingobjectid field in an activity to contacts:if (crmForm.all.regardingobjectid != null) { crmForm.all.regardingobjectid.setAttribute("defaulttype", "2");}Checking if a field is present in a formif (crmForm.all. != null) { //do your JavaScript processing here}
You should always check if a field is present before accessing it, because your script may throw an exception if it is not included in a form (like in quick create forms if your field is not required or recommended).
Calculating a date field based upon the selection of a picklistThe following script assumes that you have a picklist with three values: "1 year", "2 years" and "3 years".

var years = 0;
switch(crmForm.all..DataValue)
{
//You need to check the picklist values if they have the values 1, 2 and 3. Look at the attribute in the entity customization and note the option values.
case "1": years = 1;
break;
case "2": years = 2;
break;
case "3": years = 3; b
reak;
}if (years != 0) {var date = new Date();
date.setYear(date.getYear() + years);
crmForm.all..DataValue = date;
}
Creating a new email when double-clicking a standard text field
if (crmForm.all. != null)

{
crmForm.all..ondblclick = function()
{
var email = crmForm.all..DataValue;
if ((email != null) && (email.length > 0))
{
window.navigate("mailto:" + email);
}
}}

Resetting a field value if validation fails

In the OnLoad event, place the following code:

crmForm.all..setAttribute("lastKnownGood", crmForm.all..DataValue);

This adds a new attribute to field for later access.
In the OnChange event put the following:

//checkFailed contains either true or false and must be set to the result of your validation routineif (checkFailed)
{
crmForm.all..DataValue = crmForm.all.fieldToCheck.getAttribute("lastKnownGood");
else
{
crmForm.all..setAttribute("lastKnownGood", crmForm.all.fieldToCheck.DataValue);
}

Inserting line breaks in a text area control

Put the following in the OnLoad event:

crmForm.all..style.whiteSpace = "pre";

Hiding an entire row of a CRM form

//the field you want to hidevar field = crmForm.all.name;
//search the enclosing table rowwhile ((field.parentNode != null) && (field.tagName.toLowerCase() != "tr"))
{
field = field.parentNode;}//if we found a row, disable itif (field.tagName.toLowerCase() == "tr")
{
field.style.display = "none";
}

Disabling the time selection of a date/time field
You can dynamically enable or disable the time selection box of a date/time field using the following syntax:

var dateField = crmForm.all.;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (dateField.all.time != null)
{
//Disable the time field
dateField.all.time.disable();
//Enable the time field
dateField.all.time.enable();
}

CRM automatically enables the time selection box if the date value changes to a non-null value, so in order to always disable the time selection box, you need to add the following OnChange event script to the datetime field:

var dateField = crmForm.all.;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (dateField.all.time != null)
{
//Disable the time field
dateField.all.time.disable();
}

To initially disable the time field, put the following into the form OnLoad event:

var dateField = crmForm.all.;
//Check the existence of the datetime field. It may not be included in a quick create form!
if (dateField != null)
{
//Call the OnChange event handler
dateField.FireOnChange();
}

Changing the time interval in date/time fields

The time selection box of a date/time field uses a 30 minute interval. You can change it to a different interval using the following code in an OnLoad event:

var dateField = crmForm.all.;
//Check the existence of the datetime field. It may not be included in a quick create form!
if (dateField != null)
{
var timeField = dateField.all.time;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (timeField != null)
{
//The new interval in minutes.
var interval = 15;
var tables = timeField.getElementsByTagName("table");
if ((tables != null) && (tables.length > 0))
{
var table = tables[1];
//Remove all existing values from the selection box while (table.firstChild != null)
{
table.removeChild(table.firstChild);
}
//Add the new values for (hour = 0; hour <>
{
for (min = 0; min <>
{
var row = table.insertRow();
var cell = row.insertCell();
var time = ((hour <>
cell.setAttribute("val", time);
cell.innerText = time;
}
}
}
}}

Getting the quote id inside a quotedetail form

There's a hidden field in the quotedetail form storing the quote id. You can access it usingalert("Quote ID = " + crmForm.all.quoteid.DataValue);How to know which button triggered the OnSave eventYou can use the event.Mode property in the OnSave event. It will tell you which button was used to save an entity. Not all values are documented in the SDK, e.g the "Save as completed" in a phone call has a value of 58. As it's not documented, this number may change in future releases.To get started enter the following into your OnSave script:

alert ("event.Mode = " + event.Mode);
event.returnValue = false;
return false;

This codes makes it impossible to save the entity, but allows you to write down all of the values passed to OnSave. You will see 1 for a normal save operation and 2 for Save and Close. Most but not all constants are defined in /_common/scripts/formevt.js.

Accessing the CRM database from JavaScript

To access the CRM database from JavaScript, use the following as a template:

var connection = new ActiveXObject("ADODB.Connection");
var connectionString = "Provider=SQLOLEDB;
Server=STUNNWARECRM;
Database=stunnware_mscrm;
Integrated Security=sspi";connection.Open(connectionString);
var query = "SELECT name FROM FilteredAccount";
var rs = new ActiveXObject("ADODB.Recordset");rs.Open(query, connection, /*adOpenKeyset*/1, /*adLockPessimistic*/2);
rs.moveFirst();var values = "";while (!rs.eof)
{
values += rs.Fields(0).Value.toString() + " ";
rs.moveNext();
}
connection.Close();
alert(values);

You may need to adjust your security settings in IE in order to run this code.

Modifying the Quick Create Form

Allthough the quick create form isn't listed in the forms and views dialog, it uses the same scripts entered in the main application form. You can use the following in the OnLoad script to run code when inside of a quick create form:

if (crmForm.FormType == 5 /* Quick Create Form */)
{
//modify the form using DHTML}

Forcing the selection of an account in the potential customer field of an opportunity.

Put the following into the opportunity's OnLoad event:

crmForm.all.customerid.setAttribute("lookuptypes", "1");

This tells the customer lookup to only include the account (object type code 1). A value of 2 forces the lookup to only display contacts.

Hiding and showing fields dynamically based on other field values

You can do this in client-side JavaScript. Let's say you have a picklist named "new_category" and you have assigned the following options to it:
1 – Business
2 – Private
3 - Unspecified
Let's further assume that you want to hide the fields "new_a" and "new_b" whenever a user selects "Private", but they should be visible if a different option is selected in the picklist.

Your OnChange event script of the new_category field will be:


var hideValues = (crmForm.all.new_category != null) && (crmForm.all.new_category.DataValue == "2");
var displayStyle = hideValues ? "none" : "";
crmForm.all.new_a.style.display = displayStyle;
crmForm.all.new_b.style.display = displayStyle;

To run the code when the form is initially loaded, put the following into the OnLoad event:

if (crmForm.all.new_category != null)
{
crmForm.all.new_category.FireOnChange();
}

The test for a null value of crmForm.all.new_category is included to avoid errors if the field is not available on a form. This is true for quick create forms if the field required level is not set to business required or business recommended.

Removing the value from a lookup field

crmForm.all..DataValue = null;

Hiding tabs at runtime

You can hide and show entire tabs dynamically using the following code://Add your condition here, usually a field comparison in an OnChange event:

if (condition == true)
{
//hide the second tab
crmForm.all.tab1Tab.style.display = "none";
}
else
{
//show the second tab
crmForm.all.tab1Tab.style.display = "";
}

The tabs are named "tab0Tab", "tab1Tab", "tab2Tab" and so on.

Setting the active tab

From the SDK docs:SetFocus: Sets the focus, changes tabs, and scrolls the window as necessary to show the specified field. Example:

// Set focus to the field.
crmForm.all.SOME_FIELD_ID.SetFocus();

Displaying related entities instead of the form when opening a record

To open a related entity view of the left navigation bar (like contacts in an account entity), you can use the following:


loadArea("areaContacts");

Each entry in the navigation bar has a unique area name (areaContacts is just a sample). If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the entry you're after. Some instructions on how to use it are in http://www.stunnware.com/crm2/topic.aspx?id=CrmLookAndFeel1;
though it is a different topic, I included some screenshots using the developer toolbar in CRM.

Modifying the color of disabled form fields

Disabled form fields are sometimes hard to read. Making the color a little bit darker greatly helps to read all of the content without loosing the information that a field is read-only. Open the following file in your CRM web:

/_forms/controls/controls.css.

Then find the following style:


INPUT.ro,TEXTAREA.ro,DIV.ro,SPAN.ro{background-color: #ffffff;color:#808080;border-color: #808080;}

The color attribute is the light gray you're seeing by default. You may change it to #404040 to get a slightly darker gray. If you don't see the new colors in IE, hit CTRL-F5 to reload the source files, including the updated css file.

Removing a navigation bar entry at runtime

To remove a navigation bar entry dynamically, you can use the following code:


var navigationBarEntry = document.getElementById("navProds");
if (navigationBarEntry != null)
{
var lbArea = navigationBarEntry.parentNode;
if (lbArea != null)
{
lbArea.removeChild(navigationBarEntry);
}}

If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the navigation bar entry.

Changing the colour of a label

var label = crmForm.all._c;label.innerHTML = "" + label.innerText + "";

Hide a section on a form

crmForm.all.new_attribute_c.parentElement.parentElement.parentElement.style.display = 'none'

To show the section again:

crmForm.all.new_attribute_c.parentElement.parentElement.parentElement.style.display = 'block'

2009/04/15

CRM Tools Just makes Life so much easier!

Hi
CRM Tools is one of those things that just makes your life easier.Below is a list of tools found from Patrick Verbeeten.

http://www.patrickverbeeten.com/pages/ContentListPage.aspx?id=9&&&&

Make sure that befor you install these tools you comply with the system requirements:
.Net 3.5 will be required so make sure it is installed on the machine on which the toll is being deployed.

Full Installation package:
http://download.microsoft.com/download/6/0/f/60fc5854-3cb8-4892-b6db-bd4f42510f28/dotnetfx35.exe
Redistrubutable package:
http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe


Event Listner
A new tool is available which allows you to view CRM plug-in events as they occur, including all details available from the IPluginExectionContext.

Tracing and Debugging error in CRM 4.0
If errors in Microsoft CRM occur there can be a user friendly or otherwise limited message presented. To find out what actually is the problem is not difficult you just have to know where to look.
Wether it is an authentication issue or a SoapException 'Server was unable to process request'. This article offers some pointers on where to look for additional details.

Microsoft CRM Date values
CRM support user working in multiple time zones by storing alle date time values as UTC. This way the time can be easily adjusted to the users time zone setting. This article explains this in a bit more detail.

As for date values, some dates such as a birth date can be fixed regardless of time zone. CRM will still adjust these to the time zone, this may show as a full day difference in the interface. This article contains a plugin to allow you to use dates without timezone dependecies in CRM.

CRM 4: Waiting workflows
In CRM 4 workflows can have the status waiting when an they are waiting for an event such as a timer or they will also have this status if an error has occured. To find out which take the following steps.

Translation assistant
With version 4.0 Microsoft CRM the option to have multiple languages in a single interface became available. To translate the various available labels CRM offers an option to export all labels to an excel file.

The problem I found with this file is that it lacks context of the text you are translating. For instance the text "Address 1: Line 1" this appears for each entity, form and view that uses this fields. So if you want to change only one label it will take you a lot of trial and error to find out which field you want to change.

Cross domain remote debugging
When you are developing with a virtual PC which is running in its own domain or want to debug any other system which is in a different domain than you own user remote debuggin will normally fail on authentication errors.

Crm 4.0 Sitemap & ISV.Config Editor
As of CRM 4.0 the sitemap and isv.config file are no longer available on the file system but stored in the database. Which means that for each change you have to export the customization, make your changes and then reimport the customizations file.

Plug-ins and Workflows: Extension of Microsoft CRM 4.0
Microsoft CRM 4.0 has several methods to add functionality, to create custom workflow in CRM you have two options plugins and workflows.

Generating an Time table in SQL
In reporting queries it happens often that you need a ruler of some sort for instance a table containing all days between a start and end date. You can generate such tables by using a looping construct but this isn't the strong point of SQL.

Source control server setup
For my source control I use subversion and I found the easiest method to install a fully working subversion server (combined with trac for issue tracking) is to do that on linux. I can setup a clean server from scratch in about an hour including all required configuration. For a windows server I would need that just to install windows and get all the software packages. Best thing is I have a single script to do this, this also includes some scripts to facilitate backups and setting up new subversion/trac repositories.

CRM 4.0 Plug-in message input parameters
The CRM 4.0 plug-in model offers great capabilities. But when working with the plug-in messages you would need to know which input and output parameters are available. Unfortunaly this is not (yet) in the supplied documentation, but here it is.

Crm Developers Tool
A combined Entity and Plug-in Browser for Microsoft Dynamics CRM. Allows you to browse the entire entity model beyond the standard list.aspx Entity Browser. And a plug-in browser which offers a GUI to view and update plug-in registrations.


CrmService Testing v1.1
I have added some new features to the Crm Test service proxy which you can read about in this article. The changes allow you to dicate which requests are expected and validation is then handled by the base class.

Embedded Sql, LINQ like behaviour
When ever you create an application which uses a database. There certainly are variatons but you have basically two option when it comes to how you have de SQL-Statements. You can choose to type the SQL-statements in the source code or you can create stored procedures. Both of these have their own advantages and disadavatanges.
In-code statements ensure that when te application is deployed, no stored procedures have to be updated. Stored procedures on the other hand offer better speed and reusablility.

To allow stored procedures to be created, maximize easy of deployment I created the this. The .sql files can be edited in either Visual Studio or the SQL Management studio with full support of the syntax checkers and code-Highlighting. This class does have one other main feature it makes each declared procedure available as a strong type .NET class to access the parameters.

Testing without touching the CrmService
If you have ever tried to do unit testing on code which uses the Microsoft Crm Webservice proxy you will is nearly impossible to test just that code. I will allways rely on the Crm Server for data and callouts to function and test these in to process. This article offers a class implementation which actually stubs out the CrmService request.

Command Crm Customization Export, Import and Publication
Import, Exporting and publishing customizations in CRM 3.0 using the web interface can be time consuming. Also when an export fails the information returned is very limited. I created a small command line application to do just the same. It uses three WebServices provided by CRM specifically for this purpose.

Localhost and integrated security
One thing I allways run across when installing a new server is that it won't allow connections over the loopback interface when using integrated security. I usually test the applications from the server console and everytime they give errors; 401.1 for most applications but the Microsoft Virtual server administration console for instance returns status 500 with a message parameter incorrect.

Captcha
For this site I wanted to add some mechanism to avoid getting the article comments spammed with links and other junk. To this end I added validation using captcha. I found the code to generate the image on the internet and extended it a little. I extended a TextBox control and created a Http Handler to facilitate easy use. The code for these is downloadable here.

Version Control
Version control is important when your developing software. I know I am not the first guy to throw away the wrong file or section off code. When properly installed using source control will also service as atleast a partial backup you of your source. And as an added benefit you can see who did what and when which can be usefull in tracking bugs.

The CRM webservice
The Crm 3.0 webservice is a great improvement on the 1.2 version but still due to the nature of webservices working with it can require some quite elaborate code. You need to instantiate the class, configure security, create a ColumnSet, add columns to the column set and finally execute the Retrieve method and cast its result. This results in about 10 lines of code for such a simple action. Using partial classes introduced in the .Net 2.0 framework you can extend the Webservice without lossing the easy of recreating the webproxy when you change or and a type. Using generics you can even enforce type safety at compiletime.

This site
As you probably will guess and easily can see this site is written in .NET to be more specific in C# .NET 2.0. It also uses some content management. This basic content management system was written in a single weekend. Granted it isn't entirely fool-proof nor does it offer the options of the system available like DotNetNuke, but using some of the framework .NET features this system offers the same extensibility. The content management is an extension of the framework it self.

Generic strong Type String to Any Converter
This is one of the oldes pieces of code i wrote in .NET and still use. This class is written for conversion of xml node values to a .NET type but can also be used to with plain strings. Every time I needed a node value I had to check if the node existed, is it an element or an attribute before I could even start converting the value. This class handles all this and using generics and the IConvertible interface returns all strongly types and correctly converted.

2009/04/09

Rename Left Navigational Display Names

/* Retext/Rename Left Nav Display links. */
function leftNavRename(leftNav, oldName, newName)
{
var navItem = document.getElementById(leftNav);
if(navItem != null)
{
navItem.innerHTML = navItem.innerHTML.replace(oldName, newName);
navItem.innerHTML = navItem.innerHTML.replace(oldName, newName);
}
}
leftNavRename ('navWriteInProducts','Write-In Products','Products');


/* Remove Left Nav bar links */
function leftNavRemove(lNav)
{
var lNavItem = document.getElementById(lNav);
if (lNavItem != null)
{
lNavItem.parentNode.style.display ='none';
}
}
leftNavRemove('navExistingProducts');

2009/04/08

Additional CRM 4.0 Tools

HI
Please look at the "Additional CRM Tools & Software" (on the right navigation pane ) section of my blog.Here you will find all the tools I tested myself ,which made my life so much easier.The Links will take you
to the developers sites were you can download them.

Enjoy

2009/04/07

CRM 4.0 Installation Requirements

Microsoft .NET Framework
Download From:
http://go.microsoft.com/fwlink/?LinkId=91338&clcid=1033
Location: DOTNETFX\dotnetfx3.exe


Microsoft Visual C++ Runtime
Download From:
http://go.microsoft.com/fwlink/?LinkId=91355&clcid=1033
Location: VcRedist\vcredist_x86.exe

Tip:Extract this file within the root of the installation folder from where you run the setup.exe from.

SQL Reporting Service Report Viewer Control
Download From:
http://go.microsoft.com/fwlink/?LinkId=91351&clcid=1033
Location: ReportViewer\ReportViewer.exe


For 64 Bit installations rather update the installation files from the internet.

CRM Report Performance

Report Performance

Performance related issues are often caused by poor report design or an incorrectly configured report server. Below are a few of the common reasons for poor performance when using reports.
The best way to improve report performance is by limiting the data that is used in each report and sub-report. This is accomplished by adding parameters that users must specify when they run the report, such as date ranges or owner, or by adding additional selection criteria to a report. This will greatly reduce the data that is filtered for the report each time it is run by a user.



NOTE: For more information on writing reports using SQL Reporting Services refer to training variety of published resources available from Microsoft.




If there is a noticeable delay (5 seconds or more) in displaying data in a Microsoft Dynamics CRM list, or when opening a Microsoft Dynamics CRM form on a local area network, it could be caused by configuration problems in the SQL Reporting Server (SRS). Typically, this only occurs when Microsoft Dynamics CRM is installed on a separate server from the SRS server.
Reasons for this delay can include the following:
*Incorrect SRS locations in the SQLRSServerURL registry key in HKEY_LOCAL_MACHINE_SOFTWARE Microsoft MSCRM
*DNS problems
*Missing or invalid Service Principal Names (SPNs) for the Host name or Microsoft SQL Server (both the SQL Server that contains the Microsoft Dynamics CRM databases and the SRS Microsoft SQL Server)
*Incorrect permissions on the computer that is running Microsoft SQL Server for SRS


NOTE: Incorrect permissions can cause problems accessing Microsoft Dynamics CRM reports. This can prevent Microsoft Dynamics CRM from displaying the list of valid reports in lists and forms.

Microsoft Dynamics CRM Client for Outlook Performance

Procedure: Set synchronization options for optimal performance

*In Microsoft Dynamics CRM client for Outlook, on the CRM menu, click Options.
*In the Set Personal Options dialog box, click the Synchronization tab, within Synchronize Microsoft Dynamics CRM items with my default folders, select only record types required to be synchronized.
*Within Schedule automatic synchronization with Outlook, increase the interval between each occurrence of automatic synchronization.
*Within Schedule what to do if duplicates are found during synchronization, click Do not create duplicates to block the creation of records if key fields contain the same data.
*Click OK to close the Set Personal Options dialog box and save the configuration.



Procedure: Limit record types synchronized

If a user must synchronize contacts, tasks, and appointments, limit the record types synchronized to those that the user must have offline.
*In Microsoft Dynamics CRM client for Outlook, on the CRM menu, click Options.
*In the Set Personal Options dialog box, on the Synchronization tab, select only the record types that must be synchronized.



Procedure: Deactivate local data groups not used

Deactivate local data groups for areas the user does not use in the Microsoft Dynamics CRM client for Outlook. Local Data groups are a set of filters that determines what data is available offline and stored on the local computer.
*In Microsoft Dynamics CRM client for Outlook, on the CRM menu, click Modify local data groups.
*On the Data Groups tab, select one or more data groups.
*On the toolbar, click Deactivate. This deactivates all the local data groups, and then moves them to the Inactive Data Groups tab.


NOTE: After implementing any of these suggestions, synchronize the data manually while still online. The first synchronization will be slower than later synchronizations because Microsoft Dynamics CRM must remove records from the local data store. It is best to complete the initial synchronization while connected to the LAN or a High Speed internet connection.



Procedure: Set Address Book options for optimal performance

*In Microsoft Dynamics CRM for Outlook, on the CRM menu, click Options.
*In the Set Personal Options dialog box, click the Address Book tab, within Select how e-mail recipients are reconciled with Microsoft Dynamics CRM records, to the right of Contacts, verify that Match only against contacts synchronized to Microsoft Dynamics CRM is selected.
*To the right of Other record types, ensure that Match only the items I own is selected, and then click OK to save the configuration and close the Set Personal Options dialog box.


NOTE: The Address Book tab also provides the option to configure the frequency with which the Address Book is updated from Microsoft Dynamics CRM

Performance - View Optimization CRM 4.0

View Optimization

Modifying Quick Find Search Columns


The following example shows how to check the default search columns and change them if necessary. In this case, the steps below describe how to modify the columns on one of the most commonly used views, the Active Accounts view. This is such an important view because for many organizations, the list of Accounts represents the largest and most commonly used data sets of all the Microsoft Dynamics CRM data.

In the Microsoft Dynamics CRM Navigation Pane, click Settings.
In the Customization sub-area, click Customize Entities.
In the Customize Entities view, double-click to open Account.
In the left hand navigation area of the Account entity form, click Forms and Views.
Double-click to open the Quick Find Active Accounts view.
In the Common Tasks area, click Add Find Columns.
In the Add Find Columns dialog, mark the check boxes for the columns that you want to include in your Quick Find search and clear any check boxes for columns you want to remove. Click OK to close the dialog.
Click the Save and Close button to save the changes and return to the Account entity form.
From the Actions menu of the Account entity form, select Publish.
The Add Find Columns dialog box displays all fields available for the Account entity. Any fields that have the check box selected are fields that are searched when users use the Quick find feature.

For example, if Account Name and Account Number are selected, the Quick Find feature searches both Account Name and Account Number columns to find records that match that text.

IMPORTANT: To minimize the performance effect on the Microsoft SQL Server that is used for Microsoft Dynamics CRM, whenever possible, it is recommended to select three or fewer columns.

2009/04/06

Adding a toolbar button in CRM 4.0

How to add a toolbar button in CRM

Before these steps are done please backup or copy your ISVConfig.xml,so you have a master to restore if anything goes wrong.

This is very useful when you maybe want to do some branding of your company on your customer's CRM system,and link it to your website

Export the ISVConfig.xml from crm and open with editor, I use notepad.

Locate the following section within the xml file;



Below the section paste this code below!

Installing CRM 4.0 with SQL Server


This post will address the requirements for installating CRM on the relavant platforms.We will look at SQl Server 2005 and SQL Server 2008.

CRM JScripts- IFrames








All code can be used to save time in adding funtionality to CRM.All Code published on this post is for use at your own risk.MSCRMER will not be held responsible in any way for any errors or system failures by using the code provided. Code formatting might be affected by table size.


IFrame Code for NAV items in CRM.You will find alot of code across the internet explaining how to do this.The code below is also the same but just add your iframe and change the code.Enjoy!

==========

IFrames

//Contacts IFrame

function GetFrameSource(tabSet)
{
if (crmForm.ObjectId != null)
{
var oId = crmForm.ObjectId;
var oType = crmForm.ObjectTypeCode;
var security = crmFormSubmit.crmFormSubmitSecurity.value;
return "areas.aspx?oId=" + oId + "&oType=" + oType + "&security=" + security + "&tabSet=" + tabSet;
}
else
{
return "about:blank";
}
}
crmForm.all.IFRAME_Contacts.src = GetFrameSource("areaContacts");

=========

Custom Related Entities in a IFRAME

I used this code to have 5 iframes to related custom entities on a CRM form:Replace The (*) with your field and relationship names.

//OnLoad - JavaScript - (placed into the entity you want an iframe in)
function GetFrameSource(tabSet) {
if (crmForm.ObjectId != null) {
var oId = crmForm.ObjectId;
var oType = crmForm.ObjectTypeCode;
var security = crmFormSubmit.crmFormSubmitSecurity.value;
return "/userdefined/areas.aspx?oId=" + oId + "&oType=" + oType + "&security=" + security + "&tabSet=" + tabSet;
}
else {
return "about:blank";
}}
//Iframe 1
crmForm.all.IFRAME_*.src = GetFrameSource("*Unique relationship name*");
//Iframe 2
crmForm.all.IFRAME_*.src = GetFrameSource("*Unique relationship name*");
//Iframe 3
crmForm.all.IFRAME_*.src = GetFrameSource("*Unique relationship name*");
//Iframe 4
crmForm.all.IFRAME_*.src = GetFrameSource("*Unique relationship name*");
//Iframe 5;
crmForm.all.IFRAME_*.src = GetFrameSource("*Unique relationship name*");

========

Maximize your CRM Screens

if (window.screen)
{
var aw = screen.availWidth;
var ah = screen.availHeight;
window.moveTo(0, 0);
window.resizeTo(aw, ah);
}

==========

Confirm Save

if (confirm("Are you sure you want to save this form?"))
{
}
else
{
event.returnValue = false;
validationErrors = true;
}

==========

Making a field Read-Only

Copy this code in the onload on the crm form.

crmForm.all.*fieldname*.readOnly=true

==========

Autofill other dates form a selected date

This is very usefil if for example if you have and entity like contracts and depending on the date slected for "Start date" other date fields are updated.Copy this code to the respective field ,change event.

function dateAddExtention(p_Interval, p_Number)
{
var thing = new String();
p_Interval = p_Interval.toLowerCase();
if(isNaN(p_Number))
{
throw "The second parameter must be a number. \n You passed: " + p_Number;
return false;
}
p_Number = new Number(p_Number);
switch(p_Interval.toLowerCase())
{
case "yyyy": {// year this.setFullYear(this.getFullYear() + p_Number);
break;
}
case "q":
{
// quarter this.setMonth(this.getMonth() + (p_Number*3));
break;
}
case "m":
{
// month this.setMonth(this.getMonth() + p_Number);
break;
}
case "y":// day of year case "d":
// day case "w":
{
// weekday this.setDate(this.getDate() + p_Number);
break;
}
case "ww": {
// week of year this.setDate(this.getDate() + (p_Number*7));
break;
}
case "h":
{
// hour this.setHours(this.getHours() + p_Number);
break;
}
case "n":
{
// minute this.setMinutes(this.getMinutes() + p_Number);
break;
}
case "s":
{
// second this.setSeconds(this.getSeconds() + p_Number);
break;
}
case "ms": { // second this.setMilliseconds(this.getMilliseconds() + p_Number);
break;
}
default: { throw "The first parameter must be a string from this list: \n" + "yyyy, q, m, y, d, w, ww, h, n, s, or ms. You passed: " + p_Interval; return false;
}
}
return this;
}
Date.prototype.dateAdd = dateAddExtention;
var expireson= new Date(crmForm.all.activeon.DataValue );
var *new_increasedate*= new Date(crmForm.all.activeon.DataValue );

//CONTRACT EXPIRES ON 60 MONTHS FROM START DATE
expireson.dateAdd("m",60);
//RENEWAL DATE IS 55 MONTHS FORM START DATE
*new_increasedate*.dateAdd("m",55);
crmForm.all.expireson.DataValue = expireson;
crmForm.all.new_increasedate.DataValue = new_increasedate;
crmForm.all.billingendon.DataValue = expireson;
crmForm.all.billingstarton.DataValue = crmForm.all.activeon.DataValue;

===========



2009/04/04

Re-Deploy CRM


This post will address all tips and tricks of re-deploying CRM to new servers.

Upgrade CRM

Installing CRM 4.0 Server





This post will be dedicated to the installation of CRM on Server Platforms.We will
address Windows Server 2003 and Windows server 2008 (32 bit and 64 bit) installations.