Friday, March 27, 2015

Get local DateTime from UTC DateTime in CRM

Hello All,
Few days back I was writing a CRM 2013 plugin for an integration project in CRM. Requirement involved passage of some DateTime fields from CRM to another system. The other system was expecting the local date and time values for its DateTime fields in a string format. In my plugin code, I was pulling some DateTime fields from different entities using LINQ and OrganizationServiceContext. As we all know that CRM SDK web services always return the DateTime fields in UTC format only, I was retrieving it using LINQ (directly into the REST request object) converting the UTC date time into a string format (because the REST request object had string data type). Now since I have the string UTC DateTime value, I needed the same to be converted into the local time of the user under which the plugin is executed.

Below are the simple steps for converting any DateTime fields in CRM from UTC to user’s local time:
  1. Retrieve the “timezonecode” from user’s settings for the user under whose context the plugin is executed.
  2. Use the “timezonecode” as well as the UTC DateTime you want to convert to create LocalTimeFromUtcTimeRequest request.
  3. Pass the request object created in Step 3 above to “Execute” method of CRM Service.
  4. Response object of type LocalTimeFromUtcTimeResponse will contain the local time within its “LocalTime” property.

Okay! I hear you, enough of the stories and theories. Let’s dive directly inside the code below to perform the desired action above.

Below two functions will help you convert the UTC DateTime to Local DateTime. I have added code comments as much as I can to make it quite self-explanatory. Still if you need any help feel free to drop your comment below and I will try my best to address it.

In Order to perform vice-a-versa operation use UtcTimeFromLocalTimeRequest/UtcTimeFromLocalTimeResponse instead of LocalTimeFromUtcTimeRequest/LocalTimeFromUtcTimeResponse with some simple substitutions.

/// <summary>
/// Convert UTC DateTime to Local DateTime
/// </summary>
/// <param name="utcDateTime">DateTime in UTC Format</param>
/// <param name="service">CRM Organization Service</param>
/// <returns>Local DateTime</returns>
public DateTime ConvertUtcDateToLocal(DateTime utcDateTime, IOrganizationService service)
{
   // Get the TimezoneCode for the user
   int? _timeZoneCode = RetrieveCurrentUsersTimezone(_serviceProxy);

   // Some quick error checking
   if (_timeZoneCode != -1 && !_timeZoneCode.HasValue)
       throw new InvalidPluginExecutionException("Retrieve TimeZoneCode Failed!");

   // Below line is required only if you have a DateTime value as a String.
   // Uncomment below line if required
   //DateTime utcCreatedOnDate = DateTime.SpecifyKind(Convert.ToDateTime(stringUtcDateTime), DateTimeKind.Utc);

   // Create the LocalTimeFromUtcTimeRequest object using TimeZoneCode
   var request = new LocalTimeFromUtcTimeRequest
   {
       TimeZoneCode = _timeZoneCode.Value,
       UtcTime = utcDateTime
   };

   // Execute the above request created and get the Response back
   var responseLocalDateTime = (LocalTimeFromUtcTimeResponse)_serviceProxy.Execute(request);

   // Some null checking for the response object
   if (null != responseLocalDateTime)
       throw new InvalidPluginExecutionException("LocalTimeFromUtcTimeRequest Failed!");

   // Retrieve LocalTime from the Response
   DateTime localDateTimeValue = responseLocalDateTime.LocalTime;

   // Return Local DateTime value
   return localDateTimeValue;
}

/// <summary>
/// Retreive Current User's Timezonecode
/// </summary>
/// <param name="service">OrganizationService</param>
/// <returns>TimeZoneCode or -1</returns>
public int RetrieveCurrentUsersTimezone(IOrganizationService service)
{
   // Create RetrieveMultiple Request for UserSettings
   var currentUserSettings = service.RetrieveMultiple(
       new QueryExpression("usersettings")
       {
           ColumnSet = new ColumnSet("localeid", "timezonecode"),
           Criteria = new FilterExpression
           {
               Conditions =
{
new ConditionExpression("systemuserid", ConditionOperator.EqualUserId)
}
           }
       }).Entities[0].ToEntity<Entity>();

   // Some (old school) null and error checking
   if (null != currentUserSettings && currentUserSettings.Attributes.Contains("timezonecode"))
   {
       // Convert timezonecode into Integer type
       return Convert.ToInt32(currentUserSettings.Attributes["timezonecode"]);
   }
   else
       return -1;
}

Hope you find this post helpful. Feel free to post your comments/suggestions.

Thank You! ☺

Monday, August 4, 2014

Generate Posts in Social Pane for Migrated Records

Hello,
Few days back we faced very interesting issue where the Posts (as shown in below sample screenshot) were NOT getting generated for the account records which were migrated from previous version of CRM 4.0/2011 to CRM 2013. We verified that Posts were configured as expected for the account entity under "Settings -> Post Configuration" but still they were not getting generated however they were working perfectly for any new account records created directly in CRM 2013.
After spending multiple cups of coffee and lots of papers balls in the trash (actually it took just 20 minutes. Hahaha..!J) to figure out the problem. What I did was directly opened the CRM database and started looking for the reference tables for generating Posts. I found that below six tables were regarding Posts entity and all stuffs you see in CRM for Posts are handled through these tables (shown in below image)
From the initial symptoms of the issue which showed that Posts don’t want to get generated for Existing Migrated Records but they were happy enough to be in for newly created records in CRM 2013. This immediately kicked me to think that there must be some Mr. X (in form of some table) which is stopping the Posts to take birth. I quickly analyzed all tables affected after you create a new CRM account record in 2013 and for those which were migrated back from the previous version of CRM. This result of the analysis for the affected tables made my whole lot of hard efforts (J) fruitful.
Solution:
Above result clearly pointed out that the records for Exiting Migrated Records were missing in “PostBase” and “PostRegardingBase” (since we didn’t have any comments/follow/likes other relevant tables were empty for both types of records). Looking into the structure for both the above tables, I quickly found that “PostBase” have a foreign key column for “PostRegardingBase” and Bang On!!! We found Mr. X JJJ
Mr. X = PostRegardingBase
I quickly generated a small script for Mr. X and inserted a reference record for one of my Existing Migrated account record and tried making some legitimate changes in relation to that account which ideally should generate a Post and Guess what…
Eureka!!! Posts started getting generated for all the new eligible actions done against the Existing Migrated Account Record.
So basically in our CRM 2013 system somehow the records were missing in “PostRegardingBase” for all migrated records for the entities and we just have to “Insert” those records into this table to start Posts taking into effect for migrated records. Below table will give you mapping for the data in columns for PostRegardingBase table. Below table shows values as per Account entity record but you can change it as per your needs for other entity.
Column Name
Data to be Inserted
PostRegardingId
NewID()  “New Guid”
RegardingObjectOwningBusinessUnit
BusinessUnitId for the account record.
RegardingObjectId
AccountId (Guid) for the account record.
RegardingObjectOwnerId
OwnerId for the account.
RegardingObjectTypeCodeForSharing
ObjectTypeCode for the Account i.e. 1
RegardingObjectOwnerIdType
ObjectTypeCode for the OwnerId type. For example User or Team.
RegardingObjectIdName
Account Name – Basically name field value of the record.
RegardingObjectTypeCode
ObjectTypeCode for the Account i.e. 1
RegardingObjectIdYomiName
NULL
LatestManualPostModifiedOn
NULL
LatestAutoPostModifiedOn
GetUTCDate() – Basically Current UTC Timestamp

Hope this post helps you! Feel free to provide your valuable comments/suggestions.
Thank You. Enjoy! J
Important Note:
  • Posts will start getting generated from ongoing basis.
  • Recommendation is to take care of this table during Data Migration Planning Phase itself so that along with all migrated records, their reference records will also be pushed to PostRegardingBase table.
  • Direct database operation is not recommended by Microsoft so please do this at your own risk with all necessary backups taken and after testing in Development environment.

Friday, June 20, 2014

Custom/Conditional Duplicate Detection on Account

Hi All,
It’s been a while for me sharing some good stuffs of CRM through my blog so now here I am. J
Recently in my organization we received a very good request from users where they needed a Duplicate Detection on Accounts and we thought that it’s a very simple task for us since now CRM 2013 Service Pack 1 have brought back our old and very desirable OOB duplicate detection screen which we are used to see in previous CRM versions. However in our requirement gathering meeting with users we came across a special request from them which said that they need conditional Duplicate Detection on Accounts which means that only certain type of accounts should be detected for duplication whereas other type should be allowed to be duplicated.
While doing some research over the feasibility of this we came across this blog by Andrii (Thanks Andrii!) which pointed us to go back and check out CRM 2013 SDK. In SDK there was already a custom solution provided by Microsoft for implementing the Duplicate Detection functionality (Details can be found in the link above) which we can use it easily and modify it to embed the conditions on which our users want Duplicate Detection to be fired. It was a happy day that we found this ready-made solution provided by Microsoft but happiness was short lived when we read through the “Read Me” file provided which said that the provided solution was only for Contacts and Lead entity. So we had to build the solution for Accounts entity using the files provided for Contacts and Lead.
Solution
Here you can download the source code which is built for implementing the same SDK solution but for “Accounts” entity. The download is a zip of all the web resources (No CRM Solution) which you will need to implement the functionality.
I have made some below changes which was not part of the original solution:
  • Added “Account Number” column in Duplicate Detection Pop-up screen. For that below files were modified.
    • ShowDuplicateAccounts.html (HTML file)
    • ShowDuplicateAccounts.js (JS file)
  • Added logic in “SDK.DuplicateDetection.js” file to open the record from Duplicate Detection screen in a new Pop-up window instead of it getting opened in the same window. The changes done were:
    • Added a new method “OpenNewWindow” which will be called whenever user clicks on any record to get it open in a new Pop-up or window.
  • (Optional - Not included in the code) If you have same type of requirement of conditional Duplicate Detection then you can add your checking condition in the beginning of “onSave” method of “ShowDuplicateAccounts.js” file (Step 3 Below).

Implementation steps are already available in the link provided above as well as in SDK in “ReadMe.doc” on path “SDK2013\SampleCode\JS\DuplicateDetection”. However I will still reiterate the same here for easy reference.
Implementation Steps
  1. Import all the web resources provided in the download with the same “Name” as shown in below screenshot however your prefix may differ (here it is “new_”).
    • The reason for the “Name” to be specific is that those same paths are referred in other files.
  1. Open Account entity form and add “SDK.DuplicateDetection.js” JS web resource library.
  2. Add event handler for “OnSave” event referencing the above library calling the function named “SDK.Sample.DuplicateDetection.onSave”
  3. Check “Pass execution context as first parameter” checkbox.
  4. Add below parameters in other parameters textbox. (Make sure you change the prefix below if different)
false, "account",  "accountid",  ["name", "accountnumber", "emailaddress1"],
"new_/duplicateDetection/ShowDuplicateAccounts.html",
"dialogHeight: 250px; dialogWidth: 600px; resizable: yes; status: no;"



Result: OnSave event of Account having a potential duplicate record.

Hope you find this post helpful. Feel free to post your valuable comments or suggestions. 

Thank You !!! J