Some times we used to put some default values in the form level as per the requirements.
Even if the user is doing to create a new record and with out changing anything doing X-off or close, it will give an alert saying "Are you sure you want to navigate away from this page? ...".
So it should not come, as the user did not do anything.
To avoid this there are ways and it depends on the type of attribute it is.
#1 for non-lookup types
window.setTimeout("crmForm.all.<non-lookup_Name>.defaultValue = crmForm.all.<non-lookup_Name>.DataValue",200);
#2 for lookup types
crmForm.<lookup_name>.DataValue = crmForm.<lookup_name>.defaultValue;
if(crmForm.FormType == 1) {
var lookupItem = new Object();
var lookup = new Array();
lookupItem.name = '<lookup_name>';//which will be displayed in lookup
lookupItem.id = <lookUpGUID> // This is the Guid of the record
languageitem.typename = 'new_lookupname';//schema name of the lookup attribute
lookup[0] = lookupItem;
crmForm.<lookup_name>.DataValue = lookup;
crmForm.<lookup_name>.defaultValue = crmForm.<lookup_name>.DataValue;
}
Sudhanshu
Wednesday, September 29, 2010
Monday, September 27, 2010
get data from grid Ms crm
Here are a few useful examples of getting some data from the grid in CRM. I’ve used these in my ISV.config customizations in order to grab certain data that i want to pass to my custom pages. For the most part, a lot of this stuff gets passed automatically when you specify PassParams = 1, but I often have to construct page links dynamically in ISV.config using Javascript, so the PassParams argument does not work.
First, here’s how to get the Id of the displayed view and the Object Type Code of the records it returns:
// get guid and object type code of view being displayed (otc 1039 = savedquery, system view)
var sViewId = document.all['crmGrid'].GetParameter('viewid');
var sViewType = document.all['crmGrid'].GetParameter('viewtype');
You may notice that the code is slightly different from what I’ve presented before. The method above is a more robust way of getting the data, as it also works with the Associated Grids on an entity’s form.
To get the Object Type Code of the records that are in the grid, use the following:
var sOtc = document.all['crmGrid'].GetParameter('otc');
The following snippet will allow you to retrieve an array containing the Id values of the selected records in the grid:
// get array of selected records
var a = document.all['crmGrid'].InnerGrid.SelectedRecords;
var selectedItems = new Array(a.length);
for (var i=0; i < a.length; i++)
{
selectedItems = a[0];
}
alert(selectedItems);
To get all of the records in the grid (ie. “All Records on Current Page”):
// array of all records on current page
var iTotal = document.all['crmGrid'].InnerGrid.NumberOfRecords;
var o = document.all['crmGrid'].InnerGrid;
var allItems = new Array;
for (var i=0; i < iTotal; i++)
{
allItems[i] = o.rows.oid;
}
alert(allItems);
If the grid you are working with is displaying records from an Associated View, you can use the following to retrieve the Id and Object Type Code of the main record:
// get object id and type code of main record when grid is an associated view
var oId = document.all['crmGrid'].GetParameter('oId');
var oType = document.all['crmGrid'].GetParameter('oType');
For kicks and giggles, here is some code that will retrieve the Grid XML. you can use this for researching other types of properties that are available from the grid object that I have not presented here (and there are a few):
var gridXml = document.all['crmGrid'].gridXml;
Bonus Snippet!I’ve seen a bunch of questions on how to retrieve the query used in an Advanced Find that has not yet been saved. Well, here ya go:
// Advanced Find fetchXml
alert(window.top.resultRender.FetchXml.value);
This piece of code will display the Fetch XML that is used by the Advanced Find to query CRM.
To make scripts a little bit more readable, it is also possible to use crmGrid object directly instead of document.all['crmGrid']. e.g.:
crmGrid.InnerGrid.SelectedRecords
crmGrid.GetParameter("otc")
crmGrid.Refresh()
Sudhanshu
First, here’s how to get the Id of the displayed view and the Object Type Code of the records it returns:
// get guid and object type code of view being displayed (otc 1039 = savedquery, system view)
var sViewId = document.all['crmGrid'].GetParameter('viewid');
var sViewType = document.all['crmGrid'].GetParameter('viewtype');
You may notice that the code is slightly different from what I’ve presented before. The method above is a more robust way of getting the data, as it also works with the Associated Grids on an entity’s form.
To get the Object Type Code of the records that are in the grid, use the following:
var sOtc = document.all['crmGrid'].GetParameter('otc');
The following snippet will allow you to retrieve an array containing the Id values of the selected records in the grid:
// get array of selected records
var a = document.all['crmGrid'].InnerGrid.SelectedRecords;
var selectedItems = new Array(a.length);
for (var i=0; i < a.length; i++)
{
selectedItems = a[0];
}
alert(selectedItems);
To get all of the records in the grid (ie. “All Records on Current Page”):
// array of all records on current page
var iTotal = document.all['crmGrid'].InnerGrid.NumberOfRecords;
var o = document.all['crmGrid'].InnerGrid;
var allItems = new Array;
for (var i=0; i < iTotal; i++)
{
allItems[i] = o.rows.oid;
}
alert(allItems);
If the grid you are working with is displaying records from an Associated View, you can use the following to retrieve the Id and Object Type Code of the main record:
// get object id and type code of main record when grid is an associated view
var oId = document.all['crmGrid'].GetParameter('oId');
var oType = document.all['crmGrid'].GetParameter('oType');
For kicks and giggles, here is some code that will retrieve the Grid XML. you can use this for researching other types of properties that are available from the grid object that I have not presented here (and there are a few):
var gridXml = document.all['crmGrid'].gridXml;
Bonus Snippet!I’ve seen a bunch of questions on how to retrieve the query used in an Advanced Find that has not yet been saved. Well, here ya go:
// Advanced Find fetchXml
alert(window.top.resultRender.FetchXml.value);
This piece of code will display the Fetch XML that is used by the Advanced Find to query CRM.
To make scripts a little bit more readable, it is also possible to use crmGrid object directly instead of document.all['crmGrid']. e.g.:
crmGrid.InnerGrid.SelectedRecords
crmGrid.GetParameter("otc")
crmGrid.Refresh()
Sudhanshu
Filtered Lookup in MS CRM 4.0
MS CRM 4.0 does not provide filtered lookup facility, but we have some couple of options that we can use to implement filtered lookup in ms crm 4.0 like
1.Change lookupsingle.aspx page
http://crm.georged.id.au/post/2008/02/16/Filtering-lookup-data-in-CRM-4.aspx
2. Buy stunware filtered lookup tool
http://www.stunnware.com/default.aspx?area=products&group=fld4&subarea=fld4-download
3. Using JS Code
http://advantageworks.blogspot.com/2008/02/pseudo-filtered-lookup-dialog-in.html
4. Writing plugin.
http://mscrmfilteredlookup.codeplex.com/
Have fun
Sudhanshu
1.Change lookupsingle.aspx page
http://crm.georged.id.au/post/2008/02/16/Filtering-lookup-data-in-CRM-4.aspx
2. Buy stunware filtered lookup tool
http://www.stunnware.com/default.aspx?area=products&group=fld4&subarea=fld4-download
3. Using JS Code
http://advantageworks.blogspot.com/2008/02/pseudo-filtered-lookup-dialog-in.html
4. Writing plugin.
http://mscrmfilteredlookup.codeplex.com/
Have fun
Sudhanshu
Stopping and continuing a save event with different types of save
For some customizations you do need to stop the form onsave event, perform some business logic and continue the save event. An example would be that in specific conditions are met when a record is saved, then a popup will need to be shown. After filling in data in the popup and pressing a continue button on the popup, then the save operation would need to continue. The SDK helps in this situation. Look at the following page for more information.
On that page you will see that you can stop the onsave event by setting the 'event.returnValue = false'. Don't forget to follow that line with a 'return false'. This will cause the save procedure to stop right at that point. Otherwise statements after that will still be executed.
To call the save event from your javascript code, you can use the javascript functions crmForm.Save(); and crmForm.SaveAndClose(). By looking at the 'event.Mode' you can determine which event was executed before. If the code is 1, then it is a crmForm.Save(); or it is 2 for a crmForm.SaveAndClose(). There's only one small issue. There can be other save events as well. There's the 'save and new', 'save as completed', and also the 'send' for emails. Below is a list of save events with the corresponding javascript functions to call. Once again, this is not documented in the SDK, so this might change with a hotfix or new version.
Save
Code: 1
Function: crmForm.Save();
SaveAndClose
Code: 2
Function: crmForm.SaveAndClose();
Send
Code: 7
Function: send();
SaveAsCompleted
Code: 58
Function: SaveAsCompleted();
SaveAndNew
Code: 59
Function: crmForm.SubmitCrmForm(59, true, true, false);
On that page you will see that you can stop the onsave event by setting the 'event.returnValue = false'. Don't forget to follow that line with a 'return false'. This will cause the save procedure to stop right at that point. Otherwise statements after that will still be executed.
To call the save event from your javascript code, you can use the javascript functions crmForm.Save(); and crmForm.SaveAndClose(). By looking at the 'event.Mode' you can determine which event was executed before. If the code is 1, then it is a crmForm.Save(); or it is 2 for a crmForm.SaveAndClose(). There's only one small issue. There can be other save events as well. There's the 'save and new', 'save as completed', and also the 'send' for emails. Below is a list of save events with the corresponding javascript functions to call. Once again, this is not documented in the SDK, so this might change with a hotfix or new version.
Save
Code: 1
Function: crmForm.Save();
SaveAndClose
Code: 2
Function: crmForm.SaveAndClose();
Send
Code: 7
Function: send();
SaveAsCompleted
Code: 58
Function: SaveAsCompleted();
SaveAndNew
Code: 59
Function: crmForm.SubmitCrmForm(59, true, true, false);
Sunday, September 26, 2010
login log out informations for users in ms crm
We all are aware of this thing that ms crm does have the tracking system of the users logging in and logging out to crm.
so here is some idea how to make it
#1 make a custom entity named "LogUserInfo" and create the following minimun required attributes
UserName : this will keep the users full name
OrgName : the corresponding Organization name , this will help in multi-tenancy environment.
LoginTime : the login time of the user(it will be calculated once the loader.aspx page will get loaded.)
LogoutTime : the log out time of the user(it will be calculated once the user will X-off means close the browser or refresh the main window)
#2 write the following bunch of code in the loader.aspx page which is available in the inetpub\wwwroot\, with in the javascript tag
//register the onload and unonload events 1st
window.onload = logLoginInfo;
window.onunload = logLogoutInfo;
//get user full name
function getUserFullName(){
//Create the XML that will fetch the required info.
//You can inspect this web service call using a tool called FIDDLER.
var XMLRequest = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>systemuser</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>systemuserid</q1:Attribute>" +
" <q1:Attribute>fullname</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
try{
//Create Http request object
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", XMLRequest.length);
xmlHttpRequest.send(XMLRequest);
//alert("aaa"+xmlHttpRequest.responseText);
//Store the response which would be XML
var Result = xmlHttpRequest.responseXML;
/*
The return is of type "BusinessEntity" if you were using similar code one server side.
Hence we need to select node of type "BusinessEntity"
In our case It should be not more one than one node
*/
var BusinessEntityNodes = Result.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");
// Check If data was retrived
if (BusinessEntityNodes.length != 0){
var BusinessEntityNode = BusinessEntityNodes[0];
var SystemUserId = BusinessEntityNode.selectSingleNode("q1:systemuserid");
var FullName = BusinessEntityNode.selectSingleNode("q1:fullname");
var SystemUserId = (SystemUserId == null) ? null : SystemUserId.text;
var FullName = (FullName == null) ? null : FullName.text;
// alert("FullName"+FullName);
}
return FullName;
} catch (e){
alert(e.message);
}
}
//get the current Organization(tenant) name
function getOrgName(){
var url = window.location;
var arr = url.toString().split("/");
return arr[3];
}
//get the just login time
function getLoginOutTime(){
var currentTime = new Date();
var month = currentTime.getMonth() + 1;
var day = currentTime.getDate();
var year = currentTime.getFullYear();
var hours = currentTime.getHours();
var minutes = currentTime.getMinutes();
var finalTime = month+"/"+day+"/"+year+" "+hours+":"+minutes;
if(hours > 11){
finalTime += " PM";
} else {
finalTime += " AM";
}
return finalTime;
}
//logging the login info
var loginInfoGUID = "";
function logLoginInfo(){
// Prepare values for the Login User Info.
var sdv_username = getUserFullName();
var sdv_orgname = getOrgName();
var sdv_name = sdv_orgname +"_"+sdv_username;
//var sdv_logintime = getLoginOutTime();
var authenticationHeader = GenerateAuthenticationHeader();
// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>" +
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+
" xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+
authenticationHeader+
"<soap:Body>"+
"<Create xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<entity xsi:type='sdv_loguserinfo'>"+
"<sdv_name>"+sdv_name+"</sdv_name>"+
"<sdv_username>"+sdv_username+"</sdv_username>"+
"<sdv_orgname>"+sdv_orgname+"</sdv_orgname>"+
// "<sdv_logintime>"+sdv_logintime+"</sdv_logintime>"+
"</entity>"+
"</Create>"+
"</soap:Body>"+
"</soap:Envelope>";
//alert("value"+xml);
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Create");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result
var resultXml = xHReq.responseXML;
//alert("xml"+xHReq.responseText);
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.loadXML(xHReq.responseXML.xml);
var guid = xmlDoc.getElementsByTagName("CreateResult");
//this guid will be used for updating the logout time
loginInfoGUID = guid[0].childNodes[0].text;
//alert("guid"+guid[0].childNodes[0].text);
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0)
{
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert("Ignore it, It was for tracking the loging infos"+msg);
}
else
{
//alert("done");
}
}
//logging the louout info
function logLogoutInfo(){
// Prepare variables for updating a contact.
//var sdv_logouttime = getLoginOutTime();
var authenticationHeader = GenerateAuthenticationHeader();
// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>"+
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+
" xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+
authenticationHeader+
"<soap:Body>"+
"<Update xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<entity xsi:type='sdv_loguserinfo'>"+
//"<sdv_logouttime>"+sdv_logouttime+"</sdv_logouttime>"+
"<sdv_loguserinfoid>"+loginInfoGUID+"</sdv_loguserinfoid>"+
"</entity>"+
"</Update>"+
"</soap:Body>"+
"</soap:Envelope>";
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Update");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result
var resultXml = xHReq.responseXML;
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0)
{
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert("Ignore it, It was for tracking the loging infos"+msg);
}
// Display a confirmation message and open the updated contact.
else
{
// alert("done");
}
}
put the above code as it is, if you have created the custom entity with attributes as above.
#3 Finally one plugin need to be registered on ccreate and update of that entity to get the server time
for precreate
#region IPlugin Members
public void Execute(IPluginExecutionContext context)
{
try
{
DynamicEntity entity = getDynamicEntity(context, "sdv_loguserinfo");
entity.Properties.Add(new CrmDateTimeProperty("sdv_logintime", new CrmDateTime(DateTime.Now.ToString())));
}
catch (InvalidPluginExecutionException ipee)
{
throw ipee;
}
catch (Exception e)
{
throw new InvalidPluginExecutionException("Uncaught Exception\r\n" + e.Message + "\r\n" + e.StackTrace);
}
}
#endregion
private static DynamicEntity getDynamicEntity(IPluginExecutionContext context, string name)
{
if (!(context.InputParameters.Properties.Contains("Target") &&
context.InputParameters.Properties["Target"] is DynamicEntity))
{
return null;
}
DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];
if (entity.Name != name)
{
return null;
}
return entity;
}
for preupdate
#region IPlugin Members
public void Execute(IPluginExecutionContext context)
{
try
{
DynamicEntity entity = getDynamicEntity(context, "sdv_loguserinfo");
entity.Properties.Add(new CrmDateTimeProperty("sdv_logouttime", new CrmDateTime(DateTime.Now.ToString())));
}
catch (InvalidPluginExecutionException ipee)
{
throw ipee;
}
catch (Exception e)
{
throw new InvalidPluginExecutionException("Uncaught Exception\r\n" + e.Message + "\r\n" + e.StackTrace);
}
}
#endregion
private static DynamicEntity getDynamicEntity(IPluginExecutionContext context, string name)
{
if (!(context.InputParameters.Properties.Contains("Target") &&
context.InputParameters.Properties["Target"] is DynamicEntity))
{
return null;
}
DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];
if (entity.Name != name)
{
return null;
}
return entity;
}
Register the plugins by using the registration tool and test it.
Have fun.
Sudhanshu
so here is some idea how to make it
#1 make a custom entity named "LogUserInfo" and create the following minimun required attributes
UserName : this will keep the users full name
OrgName : the corresponding Organization name , this will help in multi-tenancy environment.
LoginTime : the login time of the user(it will be calculated once the loader.aspx page will get loaded.)
LogoutTime : the log out time of the user(it will be calculated once the user will X-off means close the browser or refresh the main window)
#2 write the following bunch of code in the loader.aspx page which is available in the inetpub\wwwroot\, with in the javascript tag
//register the onload and unonload events 1st
window.onload = logLoginInfo;
window.onunload = logLogoutInfo;
//get user full name
function getUserFullName(){
//Create the XML that will fetch the required info.
//You can inspect this web service call using a tool called FIDDLER.
var XMLRequest = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>systemuser</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>systemuserid</q1:Attribute>" +
" <q1:Attribute>fullname</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
try{
//Create Http request object
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", XMLRequest.length);
xmlHttpRequest.send(XMLRequest);
//alert("aaa"+xmlHttpRequest.responseText);
//Store the response which would be XML
var Result = xmlHttpRequest.responseXML;
/*
The return is of type "BusinessEntity" if you were using similar code one server side.
Hence we need to select node of type "BusinessEntity"
In our case It should be not more one than one node
*/
var BusinessEntityNodes = Result.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");
// Check If data was retrived
if (BusinessEntityNodes.length != 0){
var BusinessEntityNode = BusinessEntityNodes[0];
var SystemUserId = BusinessEntityNode.selectSingleNode("q1:systemuserid");
var FullName = BusinessEntityNode.selectSingleNode("q1:fullname");
var SystemUserId = (SystemUserId == null) ? null : SystemUserId.text;
var FullName = (FullName == null) ? null : FullName.text;
// alert("FullName"+FullName);
}
return FullName;
} catch (e){
alert(e.message);
}
}
//get the current Organization(tenant) name
function getOrgName(){
var url = window.location;
var arr = url.toString().split("/");
return arr[3];
}
//get the just login time
function getLoginOutTime(){
var currentTime = new Date();
var month = currentTime.getMonth() + 1;
var day = currentTime.getDate();
var year = currentTime.getFullYear();
var hours = currentTime.getHours();
var minutes = currentTime.getMinutes();
var finalTime = month+"/"+day+"/"+year+" "+hours+":"+minutes;
if(hours > 11){
finalTime += " PM";
} else {
finalTime += " AM";
}
return finalTime;
}
//logging the login info
var loginInfoGUID = "";
function logLoginInfo(){
// Prepare values for the Login User Info.
var sdv_username = getUserFullName();
var sdv_orgname = getOrgName();
var sdv_name = sdv_orgname +"_"+sdv_username;
//var sdv_logintime = getLoginOutTime();
var authenticationHeader = GenerateAuthenticationHeader();
// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>" +
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+
" xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+
authenticationHeader+
"<soap:Body>"+
"<Create xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<entity xsi:type='sdv_loguserinfo'>"+
"<sdv_name>"+sdv_name+"</sdv_name>"+
"<sdv_username>"+sdv_username+"</sdv_username>"+
"<sdv_orgname>"+sdv_orgname+"</sdv_orgname>"+
// "<sdv_logintime>"+sdv_logintime+"</sdv_logintime>"+
"</entity>"+
"</Create>"+
"</soap:Body>"+
"</soap:Envelope>";
//alert("value"+xml);
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Create");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result
var resultXml = xHReq.responseXML;
//alert("xml"+xHReq.responseText);
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.loadXML(xHReq.responseXML.xml);
var guid = xmlDoc.getElementsByTagName("CreateResult");
//this guid will be used for updating the logout time
loginInfoGUID = guid[0].childNodes[0].text;
//alert("guid"+guid[0].childNodes[0].text);
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0)
{
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert("Ignore it, It was for tracking the loging infos"+msg);
}
else
{
//alert("done");
}
}
//logging the louout info
function logLogoutInfo(){
// Prepare variables for updating a contact.
//var sdv_logouttime = getLoginOutTime();
var authenticationHeader = GenerateAuthenticationHeader();
// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>"+
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+
" xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+
authenticationHeader+
"<soap:Body>"+
"<Update xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<entity xsi:type='sdv_loguserinfo'>"+
//"<sdv_logouttime>"+sdv_logouttime+"</sdv_logouttime>"+
"<sdv_loguserinfoid>"+loginInfoGUID+"</sdv_loguserinfoid>"+
"</entity>"+
"</Update>"+
"</soap:Body>"+
"</soap:Envelope>";
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Update");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result
var resultXml = xHReq.responseXML;
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0)
{
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert("Ignore it, It was for tracking the loging infos"+msg);
}
// Display a confirmation message and open the updated contact.
else
{
// alert("done");
}
}
put the above code as it is, if you have created the custom entity with attributes as above.
#3 Finally one plugin need to be registered on ccreate and update of that entity to get the server time
for precreate
#region IPlugin Members
public void Execute(IPluginExecutionContext context)
{
try
{
DynamicEntity entity = getDynamicEntity(context, "sdv_loguserinfo");
entity.Properties.Add(new CrmDateTimeProperty("sdv_logintime", new CrmDateTime(DateTime.Now.ToString())));
}
catch (InvalidPluginExecutionException ipee)
{
throw ipee;
}
catch (Exception e)
{
throw new InvalidPluginExecutionException("Uncaught Exception\r\n" + e.Message + "\r\n" + e.StackTrace);
}
}
#endregion
private static DynamicEntity getDynamicEntity(IPluginExecutionContext context, string name)
{
if (!(context.InputParameters.Properties.Contains("Target") &&
context.InputParameters.Properties["Target"] is DynamicEntity))
{
return null;
}
DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];
if (entity.Name != name)
{
return null;
}
return entity;
}
for preupdate
#region IPlugin Members
public void Execute(IPluginExecutionContext context)
{
try
{
DynamicEntity entity = getDynamicEntity(context, "sdv_loguserinfo");
entity.Properties.Add(new CrmDateTimeProperty("sdv_logouttime", new CrmDateTime(DateTime.Now.ToString())));
}
catch (InvalidPluginExecutionException ipee)
{
throw ipee;
}
catch (Exception e)
{
throw new InvalidPluginExecutionException("Uncaught Exception\r\n" + e.Message + "\r\n" + e.StackTrace);
}
}
#endregion
private static DynamicEntity getDynamicEntity(IPluginExecutionContext context, string name)
{
if (!(context.InputParameters.Properties.Contains("Target") &&
context.InputParameters.Properties["Target"] is DynamicEntity))
{
return null;
}
DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];
if (entity.Name != name)
{
return null;
}
return entity;
}
Register the plugins by using the registration tool and test it.
Have fun.
Sudhanshu
get the full name of the user by using javascript in MS CRM
use the following bunch of code to get it
//get user full name
function getUserFullName(){
//Create the XML that will fetch the required info.
//You can inspect this web service call using a tool called FIDDLER.
var XMLRequest = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>systemuser</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>systemuserid</q1:Attribute>" +
" <q1:Attribute>fullname</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
try{
//Create Http request object
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", XMLRequest.length);
xmlHttpRequest.send(XMLRequest);
//alert("aaa"+xmlHttpRequest.responseText);
//Store the response which would be XML
var Result = xmlHttpRequest.responseXML;
/*
The return is of type "BusinessEntity" if you were using similar code one server side.
Hence we need to select node of type "BusinessEntity"
In our case It should be not more one than one node
*/
var BusinessEntityNodes = Result.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");
// Check If data was retrived
if (BusinessEntityNodes.length != 0){
var BusinessEntityNode = BusinessEntityNodes[0];
var SystemUserId = BusinessEntityNode.selectSingleNode("q1:systemuserid");
var FullName = BusinessEntityNode.selectSingleNode("q1:fullname");
var SystemUserId = (SystemUserId == null) ? null : SystemUserId.text;
var FullName = (FullName == null) ? null : FullName.text;
// alert("FullName"+FullName);
}
return FullName;
} catch (e){
alert(e.message);
}
}
have happy coding
Sudhanshu
//get user full name
function getUserFullName(){
//Create the XML that will fetch the required info.
//You can inspect this web service call using a tool called FIDDLER.
var XMLRequest = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>systemuser</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>systemuserid</q1:Attribute>" +
" <q1:Attribute>fullname</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
try{
//Create Http request object
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", XMLRequest.length);
xmlHttpRequest.send(XMLRequest);
//alert("aaa"+xmlHttpRequest.responseText);
//Store the response which would be XML
var Result = xmlHttpRequest.responseXML;
/*
The return is of type "BusinessEntity" if you were using similar code one server side.
Hence we need to select node of type "BusinessEntity"
In our case It should be not more one than one node
*/
var BusinessEntityNodes = Result.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");
// Check If data was retrived
if (BusinessEntityNodes.length != 0){
var BusinessEntityNode = BusinessEntityNodes[0];
var SystemUserId = BusinessEntityNode.selectSingleNode("q1:systemuserid");
var FullName = BusinessEntityNode.selectSingleNode("q1:fullname");
var SystemUserId = (SystemUserId == null) ? null : SystemUserId.text;
var FullName = (FullName == null) ? null : FullName.text;
// alert("FullName"+FullName);
}
return FullName;
} catch (e){
alert(e.message);
}
}
have happy coding
Sudhanshu
Friday, September 24, 2010
MS CRM 4.0 Attachment Size Limitation
MS CRM stores attachments directly in the database, and there is a 5Mb attachment limit by default with CRM 4.0. This limit is set for good reason, and if you want to increase it, you may want to see if it starts to impact your system performance.
This can be overwritten by doing the following:
Changing the maxRequestLength value in the main CRM web.config file. By default it is 8Mb which doesn't impact the System Setting below. However if you increase the Maximum File size above the maxRequestLength you will have problems.
There are many examples on the web that show something like the following which pretty well removes any size constraints from the web.config.
Save the web.config and restart IIS.
For MS CRM 4.0 that just leaves adjusting the Maximum file size in System Settings with default of 5,120 Kb shown below to something higher but less than the maxRequestLength.
Go to Settings -> administrator -> system settings -> email tab -> Exterem down you will get it.
Sudhanshu
This can be overwritten by doing the following:
Changing the maxRequestLength value in the main CRM web.config file. By default it is 8Mb which doesn't impact the System Setting below. However if you increase the Maximum File size above the maxRequestLength you will have problems.
There are many examples on the web that show something like the following which pretty well removes any size constraints from the web.config.
Save the web.config and restart IIS.
For MS CRM 4.0 that just leaves adjusting the Maximum file size in System Settings with default of 5,120 Kb shown below to something higher but less than the maxRequestLength.
Go to Settings -> administrator -> system settings -> email tab -> Exterem down you will get it.
Sudhanshu
Invoking MSCRM service from any client like Java Client(Sun Microsystem)
Below is the Soap request we execute from SOAP UI tool we have to just pass UId , pwd, domain from left side screen which comes in SOAP UI tool and we will get the response . WE don't need WSE Security required for interpolation among webservices.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://schemas.microsoft.com/crm/2007/WebServices" xmlns:cor="http://schemas.microsoft.com/crm/2007/CoreTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<web:CrmAuthenticationToken>
<cor:AuthenticationType>0</cor:AuthenticationType>
<!--Optional:-->
<cor:OrganizationName>Ath</cor:OrganizationName>
<cor:CallerId>00000000-0000-0000-0000-000000000000</cor:CallerId>
</web:CrmAuthenticationToken>
</soap:Header>
<soap:Body>
<web:RetrieveMultiple>
<!--Optional:-->
<web:query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression">
<q1:EntityName>contact</q1:EntityName>
<q1:ColumnSet xsi:type="q1:ColumnSet">
<q1:Attributes>
<q1:Attribute>fullname</q1:Attribute>
<q1:Attribute>createdon</q1:Attribute>
</q1:Attributes>
</q1:ColumnSet>
<q1:Distinct>false</q1:Distinct>
<q1:Criteria>
<q1:FilterOperator>And</q1:FilterOperator>
<q1:Conditions>
<q1:Condition>
<q1:AttributeName>createdon</q1:AttributeName>
<q1:Operator>ThisWeek</q1:Operator>
</q1:Condition>
</q1:Conditions>
<q1:Conditions>
<q1:Condition>
<q1:AttributeName>fullname</q1:AttributeName>
<q1:Operator>Equal</q1:Operator>
<q1:Values>
<q1:Value xsi:type="xsd:string">Jesper Aaberg</q1:Value>
</q1:Values>
</q1:Condition>
</q1:Conditions>
</q1:Criteria>
<q1:Orders>
<q1:Order>
<q1:AttributeName>fullname</q1:AttributeName>
<q1:OrderType>Descending</q1:OrderType>
</q1:Order>
</q1:Orders>
</web:query>
</web:RetrieveMultiple>
</soap:Body>
</soap:Envelope>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://schemas.microsoft.com/crm/2007/WebServices" xmlns:cor="http://schemas.microsoft.com/crm/2007/CoreTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<web:CrmAuthenticationToken>
<cor:AuthenticationType>0</cor:AuthenticationType>
<!--Optional:-->
<cor:OrganizationName>Ath</cor:OrganizationName>
<cor:CallerId>00000000-0000-0000-0000-000000000000</cor:CallerId>
</web:CrmAuthenticationToken>
</soap:Header>
<soap:Body>
<web:RetrieveMultiple>
<!--Optional:-->
<web:query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression">
<q1:EntityName>contact</q1:EntityName>
<q1:ColumnSet xsi:type="q1:ColumnSet">
<q1:Attributes>
<q1:Attribute>fullname</q1:Attribute>
<q1:Attribute>createdon</q1:Attribute>
</q1:Attributes>
</q1:ColumnSet>
<q1:Distinct>false</q1:Distinct>
<q1:Criteria>
<q1:FilterOperator>And</q1:FilterOperator>
<q1:Conditions>
<q1:Condition>
<q1:AttributeName>createdon</q1:AttributeName>
<q1:Operator>ThisWeek</q1:Operator>
</q1:Condition>
</q1:Conditions>
<q1:Conditions>
<q1:Condition>
<q1:AttributeName>fullname</q1:AttributeName>
<q1:Operator>Equal</q1:Operator>
<q1:Values>
<q1:Value xsi:type="xsd:string">Jesper Aaberg</q1:Value>
</q1:Values>
</q1:Condition>
</q1:Conditions>
</q1:Criteria>
<q1:Orders>
<q1:Order>
<q1:AttributeName>fullname</q1:AttributeName>
<q1:OrderType>Descending</q1:OrderType>
</q1:Order>
</q1:Orders>
</web:query>
</web:RetrieveMultiple>
</soap:Body>
</soap:Envelope>
Retrieve Plug-in/Custom Workflow Activity DLL from Database
Some times we used get some scenarios to register the dll into DB and after wards we will loose the dll and source code.
the following may help to get the dll atleast and some extend the source code also.
1.Retrieve the Plug-in encoded string from the Organization MSCRM database. The plug-ins are stored in the Plugin PluginAssemblyBase table.
SELECT Content FROM PluginAssemblyBase
WHERE PluginAssemblyId = '[Plugin Guid]'
2.Copy and paste the Content string into a text file.
3.Write a simple C# command line application to convert the plug-in string to DLL.
string inputFileName = "Plugin.txt";
string outputFileName = "Plugin.dll";
FileStream fileStream = File.Open(fileName, FileMode.Open);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, buffer.Length);
fileStream.Close();
ASCIIEncoding encoding = new ASCIIEncoding( );
this.buffer = encoding.GetString(buffer);
FileStream fileStream = new FileStream(outputFileName, FileMode.Create);
byte[] buffer = Convert.FromBase64String(this.buffer);
fileStream.Write(buffer, 0, buffer.Length);
fileStream.Close();
4.After you retrieved the DLL from the database, then you may use .Net Reflector to extract the code from this plug-in.
That’s it! Just in case you run into the same situation that you have to retrieve your plug-in or custom workflow activity from MSCRM database, you can follow the steps above. I hope this helps!
Sudhanshu
the following may help to get the dll atleast and some extend the source code also.
1.Retrieve the Plug-in encoded string from the Organization MSCRM database. The plug-ins are stored in the Plugin PluginAssemblyBase table.
SELECT Content FROM PluginAssemblyBase
WHERE PluginAssemblyId = '[Plugin Guid]'
2.Copy and paste the Content string into a text file.
3.Write a simple C# command line application to convert the plug-in string to DLL.
string inputFileName = "Plugin.txt";
string outputFileName = "Plugin.dll";
FileStream fileStream = File.Open(fileName, FileMode.Open);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, buffer.Length);
fileStream.Close();
ASCIIEncoding encoding = new ASCIIEncoding( );
this.buffer = encoding.GetString(buffer);
FileStream fileStream = new FileStream(outputFileName, FileMode.Create);
byte[] buffer = Convert.FromBase64String(this.buffer);
fileStream.Write(buffer, 0, buffer.Length);
fileStream.Close();
4.After you retrieved the DLL from the database, then you may use .Net Reflector to extract the code from this plug-in.
That’s it! Just in case you run into the same situation that you have to retrieve your plug-in or custom workflow activity from MSCRM database, you can follow the steps above. I hope this helps!
Sudhanshu
Thursday, September 23, 2010
CRM4 Set Auto-Numbering for case, quote , order etc
The funda for the auto numbering as follows
# In the Prefix box, enter up to 3 characters, symbols or numbers.
Prefixes are system-wide and are used for all system-generated numbers for the selected record type. If you change the prefix for a record type, it will not change the prefix of numbers that have already been assigned.
# In the Number box, enter the starting number.
If you have not set a numbering format before, the Number box displays 1000. After you set the numbering format and save your settings, this field is set to read-only and you cannot modify it.
# If you are setting auto-numbering formats on any tab except Articles, in the Suffix Length list, select a number
the max value will be 99999 as per the length specified,
but once it will reach 99999 and we will create the next record the value will become the next one 1000000 and it will keep going.
Sudhanshu
# In the Prefix box, enter up to 3 characters, symbols or numbers.
Prefixes are system-wide and are used for all system-generated numbers for the selected record type. If you change the prefix for a record type, it will not change the prefix of numbers that have already been assigned.
# In the Number box, enter the starting number.
If you have not set a numbering format before, the Number box displays 1000. After you set the numbering format and save your settings, this field is set to read-only and you cannot modify it.
# If you are setting auto-numbering formats on any tab except Articles, in the Suffix Length list, select a number
the max value will be 99999 as per the length specified,
but once it will reach 99999 and we will create the next record the value will become the next one 1000000 and it will keep going.
Sudhanshu
Unable to load DLL 'CRMCore.dll' when installing Update Rollup 7
just do as follows
CRMWatson.dll didn't help, but copy CRMCore.dll from C:\Program Files\Microsoft CRM\Server\bin to C:\WINDOWS. Update Rollup 7 installed properly then. CRMCore.dll in C:\WINDOWS was still the UR6 version. Delete this and then install UR12 perfectly fine with the version in the bin directory...
I guess there was an environment variable that did not get set properly, either during the initial upgrade from CRM 3.0 or a subsequent update rollup. It seems rollup 7 fixed the path.
CRMWatson.dll didn't help, but copy CRMCore.dll from C:\Program Files\Microsoft CRM\Server\bin to C:\WINDOWS. Update Rollup 7 installed properly then. CRMCore.dll in C:\WINDOWS was still the UR6 version. Delete this and then install UR12 perfectly fine with the version in the bin directory...
I guess there was an environment variable that did not get set properly, either during the initial upgrade from CRM 3.0 or a subsequent update rollup. It seems rollup 7 fixed the path.
Wednesday, September 22, 2010
Customizing CRM 4.0 print preview form
Here is the most flexible way to customize the Dynamics CRM 4.0 print
preview of any entity:
http://adrian-alexan.blogspot.com/2009/12/customizing-entities-print-preview-form.html
preview of any entity:
http://adrian-alexan.blogspot.com/2009/12/customizing-entities-print-preview-form.html
Cannot add a new user to CRM 4.0
if adding user in crm is not possible means giving error, follow the followings
#1 you should have the privileg to ccreate user.
#2 check whether the user has been created and disabled
#3 finally On the CRM Server check the following in the Registry: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM you should set a registry key AutoGroupManagementOff to value 1 (false).
Sudhanshu
#1 you should have the privileg to ccreate user.
#2 check whether the user has been created and disabled
#3 finally On the CRM Server check the following in the Registry: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM you should set a registry key AutoGroupManagementOff to value 1 (false).
Sudhanshu
Tuesday, September 21, 2010
All system jobs are waiting also workflows
Some times we will face scenarios like this, if we will go to settings -> system jobs and the status.
It happens if the asynch is not perfect.
1st check whether the asynch service is running or not.
If that is running, then check 3 things in DB.
go to the MSCRM_Config DB and the table it "DeploymentProperties".
make sure the following three values are perfect of the "NVarCharColumn" column.
ADRootDomainScheme :http
ADSdkRootDomain :<platformserver>:<port no>
ADWebApplicationRootDomain :<applicationserver>:<port no>
and if the "NVarCharColumn" value of columnaname "AsyncSdkRootDomain" in blank (mostly it happens), make it to :<applicationserver>:<port no>.
Then start the asynch service and test your work.
Sudhanshu
It happens if the asynch is not perfect.
1st check whether the asynch service is running or not.
If that is running, then check 3 things in DB.
go to the MSCRM_Config DB and the table it "DeploymentProperties".
make sure the following three values are perfect of the "NVarCharColumn" column.
ADRootDomainScheme :http
ADSdkRootDomain :<platformserver>:<port no>
ADWebApplicationRootDomain :<applicationserver>:<port no>
and if the "NVarCharColumn" value of columnaname "AsyncSdkRootDomain" in blank (mostly it happens), make it to :<applicationserver>:<port no>.
Then start the asynch service and test your work.
Sudhanshu
While connecting to a specific tenant if Plugin registration tool will give exception
While connecting to a specific tenant if Plugin registration tool will give exception
and at the same time try to access the web services from platform box, if those wo;t work then follow the below
#1 check the three groups(PrivReportingGroup{....},ReportingGroup {...},SQLAccessGroup{...}) with id in {...} from the security -> login of database
#2 make all the three groups to be available in MSCRM_config as well as the tenant_MSCRM tables by giving the following "Roles"
PrivReportingGroup{....} : CRMReaderRole
ReportingGroup {...} : none
SQLAccessGroup{...} : db_owner
#3 if possible restart the app pool in each application and platform servers
this happens sometimes if we are just importing the tenants, so it will have unnecessary groups from the earlier server.
All these things need to be deleted.
Sudhanshu
and at the same time try to access the web services from platform box, if those wo;t work then follow the below
#1 check the three groups(PrivReportingGroup{....},ReportingGroup {...},SQLAccessGroup{...}) with id in {...} from the security -> login of database
#2 make all the three groups to be available in MSCRM_config as well as the tenant_MSCRM tables by giving the following "Roles"
PrivReportingGroup{....} : CRMReaderRole
ReportingGroup {...} : none
SQLAccessGroup{...} : db_owner
#3 if possible restart the app pool in each application and platform servers
this happens sometimes if we are just importing the tenants, so it will have unnecessary groups from the earlier server.
All these things need to be deleted.
Sudhanshu
Cannot generate SSPI context
While accessing tenants , is the exception will come like "Cannot generate SSPI context" followed by the stacktrace
Just make sure the asynch service is running, if its running just rerun it.
next is make sure the sql service and agent are also running.
Sudhanshu
Just make sure the asynch service is running, if its running just rerun it.
next is make sure the sql service and agent are also running.
Sudhanshu
Monday, September 20, 2010
MS CRM5(CRM2011) released for testing
Hi All,
Good news , MS CRM 5 or MS CRM 2011 beta has been released for online.
You can get it from http://crm2011beta.com
Register and enjoy.
Sudhanshu
Good news , MS CRM 5 or MS CRM 2011 beta has been released for online.
You can get it from http://crm2011beta.com
Register and enjoy.
Sudhanshu
Happy Birthday via Workflow using Custom Workflow Activity
The following steps need to be completed:
1. Create and Register the custom workflow activity
2. Create a Workflow that uses the activity to update the custom attribute
Step #1: Creating the Activity
Note: Windows Workflow Foundations (WinWF) must be installed on your machine (it is included with .NET Framework 3.5 and is available as an extension to.NET Framework 3.0). I am using Visual Studio 2005 to build the activity.
Create the Visual Studio project with the following references (CRM assemblies can be found in the GAC of the Platform Server role machine):
•Microsoft.Crm.Sdk
•Microsoft.Crm.SdkTypeProxy
•System
•System.Workflow.Activities
•System.Workflow.ComponentModel
•System.Workflow.Runtime
Copy and paste the code that follows into a new C# class.
using System;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Activities;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
namespace ExampleActivities
{
[PersistOnClose]
[CrmWorkflowActivity("Calculate Next Occurrence", "Examples")]
public partial class CalculateNextOccurrence : SequenceActivity
{
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
DateTime today = DateTime.Today;
DateTime initialDate = this.InitialDate.UserTime;
DateTime upcomingDate = DetermineDateForYear(initialDate, today.Year);
if (upcomingDate.Date <= today)
{
upcomingDate = DetermineDateForYear(initialDate, today.Year + 1);
}
this.UpcomingDate = CrmDateTime.FromUser(upcomingDate);
return ActivityExecutionStatus.Closed;
}
private DateTime DetermineDateForYear(DateTime date, int year)
{
if (date.Month == 2 && date.Day == 29 && !DateTime.IsLeapYear(year))
{
return new DateTime(year, 3, 1);
}
else
{
return new DateTime(year, date.Month, date.Day);
}
}
//Define the dependency properties. CRM requires dependency properties used.
public static DependencyProperty InitialDateProperty = DependencyProperty.Register("InitialDate", typeof(CrmDateTime), typeof(CalculateNextOccurrence));
public static DependencyProperty UpcomingDateProperty = DependencyProperty.Register("UpcomingDate", typeof(CrmDateTime), typeof(CalculateNextOccurrence));
[CrmInput("Initial Date")] //Input Label
[ValidationOption(ValidationOption.Required)]
public CrmDateTime InitialDate
{
get
{
return (CrmDateTime)base.GetValue(InitialDateProperty);
}
set
{
base.SetValue(InitialDateProperty, value);
}
}
[CrmOutput("Upcoming Date")] //Output Label
public CrmDateTime UpcomingDate
{
get
{
return (CrmDateTime)base.GetValue(UpcomingDateProperty);
}
set
{
base.SetValue(UpcomingDateProperty, value);
}
}
}
}
Explanation of the Code
The activity has one input parameter, Initial Date, which is set to the Birthday. The activity’s output parameter, Upcoming Date, can be used to update the Upcoming Birthday field.
The actual date calculation has been placed in a helper method, DetermineDateForYear. If we tried to set the Upcoming Birthday to be February 29th in a year that was not a leap year, an exception will be thrown. To avoid that issue, if February 29th is encountered in a non-leap year, March 1st is used instead.
The Activity’s Execute method utilizes this helper method to determine the date for a given year.
NOTE: CRM 4.0 stores all dates and times in Universal Time (UTC) within CRM 4.0. The date / time is converted to the current user’s time zone when it is retrieved and converted to UTC when it is updated. Any calculations that require a date / time to be at the same moment globally (e.g. an event occurring at 6:00 PM in Los Angeles would occur at 2:00 AM in London) should perform calculations using Universal Time.
Date Only fields (such as Birthday) are stored in the database as DateTime fields, which means that they are subject to Time Zone conversions. Initially, the activity used Universal Time, but this resulted in an incorrect date in certain time zones. To get around this issue, the activity calculates the upcoming birthday using User Time.
Step #2: Register the Assembly
Register the assembly as a Workflow Activity in the CRM deployment. If you want to debug the assembly, place the Symbols file (.pdb file) in the Server\bin\assembly folder of your CRM installation folder.
Option #2 – Step #3: Create the Workflow
This workflow will manage all updates to the Upcoming Birthday field.
1. Create a Workflow for Contact with these triggers:
- On demand
- Record is created
- Record attributes change – should trigger on updates to the Birthday attribute
2. Set the scope of the workflow as appropriate
3. The Workflow should have the following structure:
•Check if Contact.Birthday has a value, then
- Calculate Upcoming Birthday using the Custom activity with Initial Date set to the Birthday attribute
- Update Contact.Upcoming Birthday to be the output parameter
•Otherwise Clear Contact.Upcoming Birthday’s value
How do I do that?
Follow these steps:
1. Create a new Workflow for the Contact entity (click Settings, Workflows, and create a new Workflow). The following Workflow triggers (events or actions that will start the Workflow) should be selected:
•a. On demand – will appear on the Contact form and grid. This button allows the Workflow to be applied manually. If there are existing contacts that have not yet had their Upcoming Birthday set, you can apply the Workflow using this button.
•b. Record is created – Workflow should run when the Contact is initially created.
•c. Record attributes change – Runs when the Birthday attribute is changed.
To set the attribute, click Select and check the Birthday checkbox in the list of attributes.
2. Set the Scope of the Workflow to the appropriate level (does not affect the On Demand trigger). The scope should match the other Workflow (discussed in the original blog post).
a. User: Workflow will only trigger on records with same as owner as the Workflow.
b. Business Unit: Workflow will trigger on records owned by any user in the same Business Unit as the owner of the Workflow.
c. Parent: Child Business Units: Workflow will trigger on records owned by any user in the same Business Unit (and any child Business Units) as the owner of the Workflow.
d. Organization: Workflow will trigger on records owned by any user.
3. Add a Check Condition and configure it.
a. Select Contact from the entity list (first drop-down list).
b. Select Birthday from the attribute list (second drop-down list).
c. Select Contains Data from the operator list (third drop-down list).
4. Add the custom activity to the Check Condition branch.
a. Click Select this row and click Add step
b. Select Examples from the Add Steps menu and click Calculate Next Occurrence.
5. Configure the custom activity with Contact.Birthday as a Dynamic Expression.
a. Click Set Properties to configure the step
b. Select the Initial Date field
c. In the Form Assistant, select Birthday from the attribute list (second drop-down list under Look for).
d. Click the Add button to add it to the list of attributes that will be used in the Dynamic Expression.
e. Click OK to add the Dynamic Expression
f. Save the configuration
6. Add an Update Record step to the Workflow (should follow the Custom Activity step) and configure it.
a. Select the Additional Fields tab
This tab contains attributes that are not shown on the regular form (such as Upcoming Birthday).
b. In the Form Assistant, select Calculate Next Occurrence from the entity list (first drop-down list under Look for).
c. Select Upcoming Date from the attribute list (second drop-down list under Look for).
d. Click Add to add to the list of attributes
e. Click OK to add the Dynamic Expression
f. Save the configuration
7. Add an Otherwise step.
Sometimes adding this step can be a bit tricky. In order to select the item from the menu, you need to click in the row that contains the condition (If Contact:Birthday contains data, then).
8. Add an Update Record step to the Otherwise branch and configure it
a. In the Additional Fields tab, select the Upcoming Birthday field
b. In the Form Assistant, select Clear from the Operator drop-down list.
c. Save the configuration
Your Workflow should look similar to this:
9. Publish the Workflow. Don’t forget to run this Workflow on any contacts that already exist.
1. Create and Register the custom workflow activity
2. Create a Workflow that uses the activity to update the custom attribute
Step #1: Creating the Activity
Note: Windows Workflow Foundations (WinWF) must be installed on your machine (it is included with .NET Framework 3.5 and is available as an extension to.NET Framework 3.0). I am using Visual Studio 2005 to build the activity.
Create the Visual Studio project with the following references (CRM assemblies can be found in the GAC of the Platform Server role machine):
•Microsoft.Crm.Sdk
•Microsoft.Crm.SdkTypeProxy
•System
•System.Workflow.Activities
•System.Workflow.ComponentModel
•System.Workflow.Runtime
Copy and paste the code that follows into a new C# class.
using System;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Activities;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
namespace ExampleActivities
{
[PersistOnClose]
[CrmWorkflowActivity("Calculate Next Occurrence", "Examples")]
public partial class CalculateNextOccurrence : SequenceActivity
{
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
DateTime today = DateTime.Today;
DateTime initialDate = this.InitialDate.UserTime;
DateTime upcomingDate = DetermineDateForYear(initialDate, today.Year);
if (upcomingDate.Date <= today)
{
upcomingDate = DetermineDateForYear(initialDate, today.Year + 1);
}
this.UpcomingDate = CrmDateTime.FromUser(upcomingDate);
return ActivityExecutionStatus.Closed;
}
private DateTime DetermineDateForYear(DateTime date, int year)
{
if (date.Month == 2 && date.Day == 29 && !DateTime.IsLeapYear(year))
{
return new DateTime(year, 3, 1);
}
else
{
return new DateTime(year, date.Month, date.Day);
}
}
//Define the dependency properties. CRM requires dependency properties used.
public static DependencyProperty InitialDateProperty = DependencyProperty.Register("InitialDate", typeof(CrmDateTime), typeof(CalculateNextOccurrence));
public static DependencyProperty UpcomingDateProperty = DependencyProperty.Register("UpcomingDate", typeof(CrmDateTime), typeof(CalculateNextOccurrence));
[CrmInput("Initial Date")] //Input Label
[ValidationOption(ValidationOption.Required)]
public CrmDateTime InitialDate
{
get
{
return (CrmDateTime)base.GetValue(InitialDateProperty);
}
set
{
base.SetValue(InitialDateProperty, value);
}
}
[CrmOutput("Upcoming Date")] //Output Label
public CrmDateTime UpcomingDate
{
get
{
return (CrmDateTime)base.GetValue(UpcomingDateProperty);
}
set
{
base.SetValue(UpcomingDateProperty, value);
}
}
}
}
Explanation of the Code
The activity has one input parameter, Initial Date, which is set to the Birthday. The activity’s output parameter, Upcoming Date, can be used to update the Upcoming Birthday field.
The actual date calculation has been placed in a helper method, DetermineDateForYear. If we tried to set the Upcoming Birthday to be February 29th in a year that was not a leap year, an exception will be thrown. To avoid that issue, if February 29th is encountered in a non-leap year, March 1st is used instead.
The Activity’s Execute method utilizes this helper method to determine the date for a given year.
NOTE: CRM 4.0 stores all dates and times in Universal Time (UTC) within CRM 4.0. The date / time is converted to the current user’s time zone when it is retrieved and converted to UTC when it is updated. Any calculations that require a date / time to be at the same moment globally (e.g. an event occurring at 6:00 PM in Los Angeles would occur at 2:00 AM in London) should perform calculations using Universal Time.
Date Only fields (such as Birthday) are stored in the database as DateTime fields, which means that they are subject to Time Zone conversions. Initially, the activity used Universal Time, but this resulted in an incorrect date in certain time zones. To get around this issue, the activity calculates the upcoming birthday using User Time.
Step #2: Register the Assembly
Register the assembly as a Workflow Activity in the CRM deployment. If you want to debug the assembly, place the Symbols file (.pdb file) in the Server\bin\assembly folder of your CRM installation folder.
Option #2 – Step #3: Create the Workflow
This workflow will manage all updates to the Upcoming Birthday field.
1. Create a Workflow for Contact with these triggers:
- On demand
- Record is created
- Record attributes change – should trigger on updates to the Birthday attribute
2. Set the scope of the workflow as appropriate
3. The Workflow should have the following structure:
•Check if Contact.Birthday has a value, then
- Calculate Upcoming Birthday using the Custom activity with Initial Date set to the Birthday attribute
- Update Contact.Upcoming Birthday to be the output parameter
•Otherwise Clear Contact.Upcoming Birthday’s value
How do I do that?
Follow these steps:
1. Create a new Workflow for the Contact entity (click Settings, Workflows, and create a new Workflow). The following Workflow triggers (events or actions that will start the Workflow) should be selected:
•a. On demand – will appear on the Contact form and grid. This button allows the Workflow to be applied manually. If there are existing contacts that have not yet had their Upcoming Birthday set, you can apply the Workflow using this button.
•b. Record is created – Workflow should run when the Contact is initially created.
•c. Record attributes change – Runs when the Birthday attribute is changed.
To set the attribute, click Select and check the Birthday checkbox in the list of attributes.
2. Set the Scope of the Workflow to the appropriate level (does not affect the On Demand trigger). The scope should match the other Workflow (discussed in the original blog post).
a. User: Workflow will only trigger on records with same as owner as the Workflow.
b. Business Unit: Workflow will trigger on records owned by any user in the same Business Unit as the owner of the Workflow.
c. Parent: Child Business Units: Workflow will trigger on records owned by any user in the same Business Unit (and any child Business Units) as the owner of the Workflow.
d. Organization: Workflow will trigger on records owned by any user.
3. Add a Check Condition and configure it.
a. Select Contact from the entity list (first drop-down list).
b. Select Birthday from the attribute list (second drop-down list).
c. Select Contains Data from the operator list (third drop-down list).
4. Add the custom activity to the Check Condition branch.
a. Click Select this row and click Add step
b. Select Examples from the Add Steps menu and click Calculate Next Occurrence.
5. Configure the custom activity with Contact.Birthday as a Dynamic Expression.
a. Click Set Properties to configure the step
b. Select the Initial Date field
c. In the Form Assistant, select Birthday from the attribute list (second drop-down list under Look for).
d. Click the Add button to add it to the list of attributes that will be used in the Dynamic Expression.
e. Click OK to add the Dynamic Expression
f. Save the configuration
6. Add an Update Record step to the Workflow (should follow the Custom Activity step) and configure it.
a. Select the Additional Fields tab
This tab contains attributes that are not shown on the regular form (such as Upcoming Birthday).
b. In the Form Assistant, select Calculate Next Occurrence from the entity list (first drop-down list under Look for).
c. Select Upcoming Date from the attribute list (second drop-down list under Look for).
d. Click Add to add to the list of attributes
e. Click OK to add the Dynamic Expression
f. Save the configuration
7. Add an Otherwise step.
Sometimes adding this step can be a bit tricky. In order to select the item from the menu, you need to click in the row that contains the condition (If Contact:Birthday contains data, then).
8. Add an Update Record step to the Otherwise branch and configure it
a. In the Additional Fields tab, select the Upcoming Birthday field
b. In the Form Assistant, select Clear from the Operator drop-down list.
c. Save the configuration
Your Workflow should look similar to this:
9. Publish the Workflow. Don’t forget to run this Workflow on any contacts that already exist.
Happy Birthday via Workflow using Client-Side Scripting
Step #1: Create the Attribute
Create a custom datetime attribute called Upcoming Birthday (schema name should be new_upcomingbirthday). The attribute should have Date Only format.
How do I do it?
1. Open the Entity form for the Contact entity (click Settings, Customization, Customize Entities, and select Contact from the grid).
2. Create a new attribute (from the Attributes grid).
3. Set these properties and Save and Close the form:
Display Name: Upcoming Birthday
Name: new_upcomingbirthday
Type: datetime
Format: Date Only
4. Publish the customizations to the Contact entity (under Actions menu, select Publish)
Step #2: Updating the Attribute
Two approaches can be used:
1. Client-Side Scripting – Update the attribute based on changes to the birthday field.
Pros: Quick, simple, very little coding required, no .NET assembly needs to be registered.
Cons: Attribute needs to be visible on the form, relies on JavaScript
2. Custom Workflow Activity – Update the attribute using a .NET assembly
Pros: Attribute is not visible on the form, no possibility of JavaScript issues
Cons: Requires .NET assembly be registered (requires Deployment Manager privileges)
I will be discussing the first option in this blog post. The second option will be discussed in a future blog posting.
Option #1: Client-Side Scripting
1. Add the new Upcoming Birthday field to the form
2. Add the code (shown below) to the onChange event for the Birthday field (be sure to enable the event)
3. Add Upcoming Birthday for the dependencies for the event
Code
var birthdate = crmForm.all.birthdate.DataValue;
if (birthdate == null)
{
crmForm.all.new_upcomingbirthday.DataValue = null;
return;
}
var today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
birthdate.setFullYear(today.getFullYear());
crmForm.all.new_upcomingbirthday.DataValue = birthdate;
How do I do that?
1. Open the Entity form for the Contact entity (click Settings, Customization, Customize Entities, and select Contact from the grid).
2. Open the Form (click Forms and Views and select Form from the grid) and select the Details tab
3. Add Upcoming Birthday to the Personal Information section
4. Change the Properties of the Birthday field (select the field and click Change Properties)
5. Select the onChange event, on the Events tab, and click Edit.
6. Check the Event is enabled checkbox and copy and paste the code (shown above) into the text box.
7. Select the Dependencies tab and add Upcoming Birthday to the list of Dependent fields
Dependencies ensure that fields required by events are not removed from the form – in this case, Upcoming Birthday.
8. Save the changes
9. Publish the customizations to the Contact entity (under Actions menu, select Publish).
Step #3: Send E-mail Workflow
This workflow will send the e-mail on the contact’s birthday.
1. Create a Workflow for Contact with these triggers:
- On demand
- As a child workflow
- Record is created
2. Set the scope of the Workflow as appropriate.
3. The Workflow should have the following structure:
· Wait until Contact.Upcoming Birthday is today
· Send the Contact an e-mail wishing them a happy birthday
· Increment the Contact.Upcoming Birthday by a year
· Call itself as a Child Workflow (creates a loop)
How do I do that?
Follow these steps:
1. Create a new Workflow for the Contact entity (click Settings, Workflows, and create a new Workflow). The following Workflow triggers (events or actions that will start the Workflow) should be selected:
a. On demand – will appear on the Contact form and grid. This button allows the Workflow to be applied manually. If there are existing contacts that have not yet had their Upcoming Birthday set, you can apply the Workflow using this button.
b. As a child workflow – Allows the Workflow to continue to call itself on annual basis using a Start Child Workflow step.
c. Record is created – Workflow should run when the Contact is initially created.
2. Set the Scope of the Workflow to the appropriate level (does not affect the On Demand trigger).
a. User: Workflow will only trigger on records with same as owner as the Workflow.
b. Business Unit: Workflow will trigger on records owned by any user in the same Business Unit as the owner of the Workflow.
c. Parent: Child Business Units: Workflow will trigger on records owned by any user in the same Business Unit (and any child Business Units) as the owner of the Workflow.
d. Organization: Workflow will trigger on records owned by any user.
3. Add a Wait Condition step and configure the step.
a. Select Workflow from the entity list (first drop-down list).
b. Select Timeout from the attribute list (second drop-down list).
c. Select Equals from the operator list (third drop-down list).
d. Select Upcoming Birthday from the Form Assistant
e. Save the configuration
Note: Derik Stenerson has written an article that gives more detailed instructions about the Wait Condition step, and some of its other uses, called “Turning Inaction into Action”.
4. Add a Send E-mail step and configure the step.
a. Select the To field and click the attribute drop-down list in the Form Assistant.
b. Select Contact from the attribute list, click Add, and then click OK.
c. Once you have configured the rest of the Send E-mail form as needed, save the changes.
Note: If the Contact does not have an e-mail address the Workflow will fail. As an extra precaution you may want to add a Check Condition that checks if the Contact’s e-mail address has been set.
5. Add an Update Record step and configure it.
a. In the Additional Fields tab, select the Upcoming Birthday attribute.
b. In the Form Assistant, select 12 from the Months drop-down list
c. Select After from the drop-down list under Months
d. Select Upcoming Birthday from the attribute list (second drop-down list under Look for)
e. Click Add and OK
f. Save the configuration
6. If you have not done so, click the button to save the changes to the triggers.
7. Add a Start Child Workflow step. Using the Lookup control, select the current Workflow (so that Workflow is calling itself). Since I called my Workflow “Recurring Reminders – Send E-mail”, I will select that item from the list.
8. Your Workflow should look something like this:
9. Publish the Workflow. Don’t forget to run this Workflow on existing contacts.
Now new and existing contacts will receive e-mails congratulating them on their birthday.
Create a custom datetime attribute called Upcoming Birthday (schema name should be new_upcomingbirthday). The attribute should have Date Only format.
How do I do it?
1. Open the Entity form for the Contact entity (click Settings, Customization, Customize Entities, and select Contact from the grid).
2. Create a new attribute (from the Attributes grid).
3. Set these properties and Save and Close the form:
Display Name: Upcoming Birthday
Name: new_upcomingbirthday
Type: datetime
Format: Date Only
4. Publish the customizations to the Contact entity (under Actions menu, select Publish)
Step #2: Updating the Attribute
Two approaches can be used:
1. Client-Side Scripting – Update the attribute based on changes to the birthday field.
Pros: Quick, simple, very little coding required, no .NET assembly needs to be registered.
Cons: Attribute needs to be visible on the form, relies on JavaScript
2. Custom Workflow Activity – Update the attribute using a .NET assembly
Pros: Attribute is not visible on the form, no possibility of JavaScript issues
Cons: Requires .NET assembly be registered (requires Deployment Manager privileges)
I will be discussing the first option in this blog post. The second option will be discussed in a future blog posting.
Option #1: Client-Side Scripting
1. Add the new Upcoming Birthday field to the form
2. Add the code (shown below) to the onChange event for the Birthday field (be sure to enable the event)
3. Add Upcoming Birthday for the dependencies for the event
Code
var birthdate = crmForm.all.birthdate.DataValue;
if (birthdate == null)
{
crmForm.all.new_upcomingbirthday.DataValue = null;
return;
}
var today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
birthdate.setFullYear(today.getFullYear());
crmForm.all.new_upcomingbirthday.DataValue = birthdate;
How do I do that?
1. Open the Entity form for the Contact entity (click Settings, Customization, Customize Entities, and select Contact from the grid).
2. Open the Form (click Forms and Views and select Form from the grid) and select the Details tab
3. Add Upcoming Birthday to the Personal Information section
4. Change the Properties of the Birthday field (select the field and click Change Properties)
5. Select the onChange event, on the Events tab, and click Edit.
6. Check the Event is enabled checkbox and copy and paste the code (shown above) into the text box.
7. Select the Dependencies tab and add Upcoming Birthday to the list of Dependent fields
Dependencies ensure that fields required by events are not removed from the form – in this case, Upcoming Birthday.
8. Save the changes
9. Publish the customizations to the Contact entity (under Actions menu, select Publish).
Step #3: Send E-mail Workflow
This workflow will send the e-mail on the contact’s birthday.
1. Create a Workflow for Contact with these triggers:
- On demand
- As a child workflow
- Record is created
2. Set the scope of the Workflow as appropriate.
3. The Workflow should have the following structure:
· Wait until Contact.Upcoming Birthday is today
· Send the Contact an e-mail wishing them a happy birthday
· Increment the Contact.Upcoming Birthday by a year
· Call itself as a Child Workflow (creates a loop)
How do I do that?
Follow these steps:
1. Create a new Workflow for the Contact entity (click Settings, Workflows, and create a new Workflow). The following Workflow triggers (events or actions that will start the Workflow) should be selected:
a. On demand – will appear on the Contact form and grid. This button allows the Workflow to be applied manually. If there are existing contacts that have not yet had their Upcoming Birthday set, you can apply the Workflow using this button.
b. As a child workflow – Allows the Workflow to continue to call itself on annual basis using a Start Child Workflow step.
c. Record is created – Workflow should run when the Contact is initially created.
2. Set the Scope of the Workflow to the appropriate level (does not affect the On Demand trigger).
a. User: Workflow will only trigger on records with same as owner as the Workflow.
b. Business Unit: Workflow will trigger on records owned by any user in the same Business Unit as the owner of the Workflow.
c. Parent: Child Business Units: Workflow will trigger on records owned by any user in the same Business Unit (and any child Business Units) as the owner of the Workflow.
d. Organization: Workflow will trigger on records owned by any user.
3. Add a Wait Condition step and configure the step.
a. Select Workflow from the entity list (first drop-down list).
b. Select Timeout from the attribute list (second drop-down list).
c. Select Equals from the operator list (third drop-down list).
d. Select Upcoming Birthday from the Form Assistant
e. Save the configuration
Note: Derik Stenerson has written an article that gives more detailed instructions about the Wait Condition step, and some of its other uses, called “Turning Inaction into Action”.
4. Add a Send E-mail step and configure the step.
a. Select the To field and click the attribute drop-down list in the Form Assistant.
b. Select Contact from the attribute list, click Add, and then click OK.
c. Once you have configured the rest of the Send E-mail form as needed, save the changes.
Note: If the Contact does not have an e-mail address the Workflow will fail. As an extra precaution you may want to add a Check Condition that checks if the Contact’s e-mail address has been set.
5. Add an Update Record step and configure it.
a. In the Additional Fields tab, select the Upcoming Birthday attribute.
b. In the Form Assistant, select 12 from the Months drop-down list
c. Select After from the drop-down list under Months
d. Select Upcoming Birthday from the attribute list (second drop-down list under Look for)
e. Click Add and OK
f. Save the configuration
6. If you have not done so, click the button to save the changes to the triggers.
7. Add a Start Child Workflow step. Using the Lookup control, select the current Workflow (so that Workflow is calling itself). Since I called my Workflow “Recurring Reminders – Send E-mail”, I will select that item from the list.
8. Your Workflow should look something like this:
9. Publish the Workflow. Don’t forget to run this Workflow on existing contacts.
Now new and existing contacts will receive e-mails congratulating them on their birthday.
Sunday, September 19, 2010
Automatic Workflow for Birthday Reminders
Well, this is not beyond the scope of workflow functionality. You need to think a bit and plan your fuctionality. Since workflow normally waits for a future date so let me give you a simple solution for this.
- Create a custom field at contact named next birthday
- Create two workflows:
#1 Update Next Birthday (This workflow sets the next birthday first time and will not execute thereafter)
Fire this workflow on create of contact/account or on change of birthday field.
If Birthday contains data Then
Update Contact Next Birthday Field by adding 12 Months in Birthday field
2) Birthday Reminder (This workflow sends reminder emails and updates Next Birthday Field after each execution by adding 12 months)
If Next Birthday contains data Then
Wait untill 5 days Before Birthday
Send Email reminder to user - Receptionist
Update Next Birthday = After 12 months of Current birthday
Hope it will work out.
Cheers,
- Create a custom field at contact named next birthday
- Create two workflows:
#1 Update Next Birthday (This workflow sets the next birthday first time and will not execute thereafter)
Fire this workflow on create of contact/account or on change of birthday field.
If Birthday contains data Then
Update Contact Next Birthday Field by adding 12 Months in Birthday field
2) Birthday Reminder (This workflow sends reminder emails and updates Next Birthday Field after each execution by adding 12 months)
If Next Birthday contains data Then
Wait untill 5 days Before Birthday
Send Email reminder to user - Receptionist
Update Next Birthday = After 12 months of Current birthday
Hope it will work out.
Cheers,
Subscribe to:
Posts (Atom)