Dec 13, 2017

Get Form Type in Business Rules

There is no proper way of identify if form is in CREATE or UPDATE. This seems a big missing piece when it comes to implement field functionalities especially for Form Scopes.

Dynamics 365 (Version 8.2 or earlier)

One passive way of doing that is to check if Create On field contains data.

1) Created On - Not Null - Update
2) Created On - Null - Create

Using Create On is particularly sensible since that field is not set with on Load event even by chance.



Dynamics 365 (Version 9)

Then I realise above solution does't work for Version 9. Then found a way to do it slightly differently.

Instead of Created On, we have select a required field to check value to decide if its created or not.

Suppose we need to lock the Department field of Contact after creation.

1) Set Department field as Read-Only in the form.
2) Now do the Business Rule using one of the Required Fields such as First Name.
                                                                               

Nov 20, 2017

Enforce Calculation of Rollup fields

Rollup fields are handy in many scenarios, it takes one hours to update by default. If the business case needs real time updating, we can write plugin/WF to enforce the recalculation based on trigger. In such situation, below generic method can be used.

public static void EnforceCalculationRollup(IOrganizationService OrganizationService, EntityReference record, string rollupField)
{
CalculateRollupFieldRequest rollupRequest = new CalculateRollupFieldRequest { Target = record, FieldName = rollupField };
CalculateRollupFieldResponse response = (CalculateRollupFieldResponse)OrganizationService.Execute(rollupRequest);
Entity entity = response.Entity;
OrganizationService.Update(entity);
}

Hope this helps!

Nov 6, 2017

Use Service Calendar programmatically to check user availability

Service Calendar is a key component used in Field service for scheduling the work. Anyway, Calendar alone can be used for many different usages since its showing the working hours/ availability of a User. Here I am illustrating how to pro-grammatically access the calendar.

First of all, lets see how to set users availability in the calendar. In order to make it simple I am going to set if someone is available in given day or not. (not going into different time slots, which is also possible). Go to any User and go to Work Hours. Then go to New Weekly Schedule as below;


Please set the schedule as you wish in the resulting pane as below; Since I am interested in daily basis, I select 24 hours.


Once save you will see calendar been updated.


Now, this is the code snippet to check the availability of the User through calendar;

public static bool IsUserAvailable(IOrganizationService OrganizationService, Guid UserId)
{
    bool isUserAvailable = false;

    QueryScheduleRequest scheduleRequest = new QueryScheduleRequest
    {
        ResourceId = UserId,
        Start = DateTimeUtility.RetrieveLocalTimeFromUTCTime(OrganizationService, DateTime.Now),
        End = DateTimeUtility.RetrieveLocalTimeFromUTCTime(OrganizationService, DateTime.Now.AddSeconds(5)),
        TimeCodes = new TimeCode[] { TimeCode.Available }
    };
    QueryScheduleResponse scheduleResponse = (QueryScheduleResponse)OrganizationService.Execute(scheduleRequest);
    if (scheduleResponse.TimeInfos.Length > 0)
        isUserAvailable = true;

    return isUserAvailable;
}

You may notice that I am passing the local time in QueryScheduleRequest in above code since I am setting the local time zone in the Calendar also. I am also giving here the methods to be be used in time conversions;

internal static DateTime RetrieveLocalTimeFromUTCTime(IOrganizationService service, DateTime utcTime)
{
    return RetrieveLocalTimeFromUTCTime(utcTime, RetrieveCurrentUsersSettings(service), service);
}

internal static int? RetrieveCurrentUsersSettings(IOrganizationService service)
{
    var currentUserSettings = service.RetrieveMultiple(
        new QueryExpression("usersettings")
        {
            ColumnSet = new ColumnSet("timezonecode"),
            Criteria = new FilterExpression
            {
                Conditions =
                {
                            new ConditionExpression("systemuserid", ConditionOperator.EqualUserId)
                }
            }
        }).Entities[0].ToEntity<Entity>();
    return (int?)currentUserSettings.Attributes["timezonecode"];
}

internal static DateTime RetrieveLocalTimeFromUTCTime(DateTime utcTime, int? timeZoneCode, IOrganizationService service)
{
    if (!timeZoneCode.HasValue)
        return DateTime.Now;
    var request = new LocalTimeFromUtcTimeRequest
    {
        TimeZoneCode = timeZoneCode.Value,
        UtcTime = utcTime.ToUniversalTime()
    };
    var response = (LocalTimeFromUtcTimeResponse)service.Execute(request);
    return response.LocalTime;
}

Hope this helps!

Oct 19, 2017

Programmatically Share, Retrieve Shared Users and UnShare a Dynamics 365 record with a User OR Team

This is just to share some simple code snippets relates to sharing. This works well.

Sharing

using Microsoft.Crm.Sdk.Messages;

public static void ShareRecord(IOrganizationService OrganizationService, string entityName, Guid recordId, Guid UserId)
{
    EntityReference recordRef = new EntityReference(entityName, recordId);
    EntityReference User = new EntityReference(SystemUser.EntityLogicalName, UserId);

    //If its a team, Principal should be supplied with the team
    //EntityReference Team = new EntityReference(Team.EntityLogicalName, teamId);

    GrantAccessRequest grantAccessRequest = new GrantAccessRequest
    {
        PrincipalAccess = new PrincipalAccess
        {
            AccessMask = AccessRights.ReadAccess | AccessRights.WriteAccess | AccessRights.AppendToAccess | AccessRights.,
            Principal = User
            //Principal = Team
        },
        Target = recordRef
    };
    OrganizationService.Execute(grantAccessRequest);
}

Its great that VS intellisense would help you identify which Access Right you can set.


Retrieve user who has been shared with
public static void RetrieveSharedUsers(IOrganizationService OrganizationService, EntityReference entityRef)
{
    var accessRequest = new RetrieveSharedPrincipalsAndAccessRequest
    {
        Target = entityRef
    };
    var accessResponse = (RetrieveSharedPrincipalsAndAccessResponse)OrganizationService.Execute(accessRequest);
    foreach (var principalAccess in accessResponse.PrincipalAccesses)
    {
        // principalAccess.Principal.Id - User Id
    }
}

Revoke the Share (UnShare)
 public static void RevokeShareRecord(IOrganizationService OrganizationService, string TargetEntityName, Guid TargetId, Guid UserId)
 {
    EntityReference target = new EntityReference(TargetEntityName, TargetId);
    EntityReference User = new EntityReference(SystemUser.EntityLogicalName, UserId);

    RevokeAccessRequest revokeAccessRequest = new RevokeAccessRequest
    {
        Revokee = User,
        Target = target
    };
    OrganizationService.Execute(revokeAccessRequest);
}

Oct 11, 2017

Console App to Connect to Dynamics 365

One way of testing complex custom codes for Dynamics 365 is executing them using a Console App. This post explains how to create a Console App that enables you to use early bound classes. This can come in handy when you need to test your complex Linq queries before implementing.

1. Once Create empty Console app we need to install below two NuGet Packages;
  • Microsoft.CrmSdk.CoreAssemblies
  • Microsoft.IdentityModel

2. Now Download and reference below Assemblies
  • Microsoft.IdentityModel.Clients.ActiveDirectory.dll
  • Microsoft.Xrm.Tooling.Connector.dll

3. Now create the early bound classes
For this Open the Command Prompt and go to Bin Folder of Dynamics 365 SDK. Then execute CrmSvcUtil.exe. Check given sample command line used for my scenario;

CrmSvcUtil.exe /url:https://xxxx.crm6.dynamics.com/XRMServices/2011/Organization.svc /out:Xrm.cs /username:xxxx@xxxx.onmicrosoft.com /password:xxxxx/namespace:TestConsoleDynamics365 /serviceContextName:TcServiceContext

4. Add the early bound Classes
Add a Class Library file to solution called Xrm.cs and copy relevant content from generated file in previous step. Your solution will now look like this in terms of file structure.


5. Now Open the Program.cs file and do the code as below. This sample consist of one method that uses Linq query to retrieve account name from Id.

using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Tooling.Connector;
using System.Linq;

namespace TestConsoleDynamics365
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Conncetion to Dynamics 365");
            String connectionString = GetServiceConfiguration();

            Console.WriteLine("Conncetion string : " + connectionString);
            Console.WriteLine("Press Enter to move on..");
            Console.ReadLine();

            CrmServiceClient conn = new Microsoft.Xrm.Tooling.Connector.CrmServiceClient(connectionString);

            IOrganizationService _orgService = (IOrganizationService)conn.OrganizationWebProxyClient != null ? (IOrganizationService)conn.OrganizationWebProxyClient : (IOrganizationService)conn.OrganizationServiceProxy;

            //TEST CODE
            Account acc = RetriveAccountById(_orgService, new Guid("628B3A81-CA93-E711-814F-E0071B662BF1"));
            string AccountName = acc != null ? acc.Name : "Nothing Found";
            Console.WriteLine("Sample Account : " + AccountName);
            Console.ReadLine();
        }

        internal static Account RetriveAccountById(IOrganizationService service, Guid Id)
        {
            Account account = null;
            using (TcServiceContext serviceContext = new TcServiceContext(service))
            {
                account = (from c in serviceContext.CreateQuery<Account>()
                           where c.Id == Id
                           select c).FirstOrDefault();
            }
            return account;
        }

        private static string GetServiceConfiguration()
        {
            return "Url = https://xxxxxx.crm6.dynamics.com; Username=xxxx@xxxxx.onmicrosoft.com; Password=xxxxxx; authtype=Office365";
         }
    }
 }

6. Now Run and see. Here is my result;


Note:
My intention is to give simplest code only, though code can be improved according to best practices which I am not discussing here. Ex: storing connection string in the config file.

Oct 9, 2017

Synchronize Contacts to a Table Using Azure Logic Apps

I have suggested few ways of getting free Azure Subscriptions for Dynamics 365 developers through a post. Click here to read that.

Now though of jumping in to Azure Logic Apps, among many new Dynamics 365 + Azure technologies.

Azure Logic Apps come in handy in integrating Dynamics 365 with many third-party applications. Anyway, most fundamental requirement could be to synchronize an entity data with a SQL database. Below step-by-step guide is to illustrate how we can achieve this in a simple manner.

Let’s open a logic app in Azure. Click New, Select Web + Mobile and select Logic App.


 Now give a meaningful name to the Logic App and Create a Resource group or select existing one.


Then you will come to Logic App Designer with all kinds of Templates. Still I prefer to use a Blank Template for the sake of learning and flexibility. Click Blank Template and select Dynamics 365 as the Connector as below.


Within many triggers, select Dynamics 365 – When record is created or updated as the trigger.



Obviously, you need to provide your Dynamics 365 Organization and Entity interested in synchronizing. In my scenario, it’s Contact.


Now add the Condition which does the trick. In Dynamics 365, we have selected the trigger which tracks both Create and Update operations. You may notice we don’t have such single operation in SQL. Our approach is to check if Created on equals Modified On. If Condition satisfies, it’s a Create or else an Update.


Now add SQL server Insert row action if condition is satisfied and match the table fields with Dynamics 365 fields as below. I have selected only few fields here, but it’s essential to map Id column so Dynamics and SQL table record can be mapped for Update Operation.


Now add SQL server Update row action if condition is NOT satisfied and match the table fields with Dynamics 365 fields as below. You will notice we have extra field called Row id here which is nothing but the Primary Key. This is obviously necessary for Updating.


Now run the logic App and try the operation.

I created Contact record for Mark Wilkinson, and I got below record created through Logic App. App identified this as a Create operation since both Created On and Modified On values are same.


Then I changed the Description field and found it’s correctly updating the record as below.


This is just simple explanation, but this is evident Logic Apps can be used to synchronize Dynamics 365 data with SQL tables easily.

Oct 4, 2017

Validate Next Stage button of Business Process Flow

In Business Process Flows in Dynamics 365, we can proceed to next stage by clicking Next Stage button, if all the required fields in the current stage is filled. This is a straight forward validation. Anyway, we may need to do bit more tricky validations through JavaScript.

Let’s see the approach for adding a client script for this event. Please check below code snippet. As you may notice Stage Name and Direction are giving us most needed values to identify when we need to execute our logic.

function onLoad()
{
    Xrm.Page.data.process.addOnStageChange(OnStageProgress);
}

function OnStageProgress(executionContext)
{
    var stageName = Xrm.Page.data.process.getActiveStage().getName();
    var direction = executionContext.getEventArgs().getDirection();
    alert(stageName);
    alert(direction); //Values are Next OR Previous
}

Consider this scenario for an example. I need to move to next stage which is Complete stage if either of Description or Company Name is filled. Let’s extend the logic for that;

function OnStageProgress(executionContext) {
    var stageName = Xrm.Page.data.process.getActiveStage().getName();
    var direction = executionContext.getEventArgs().getDirection();

    var description = Xrm.Page.getAttribute('description').getValue();
    var company = Xrm.Page.getAttribute('parentcustomerid').getValue();

    if ((direction == 'Next') && (stageName == 'Complete'))
    {
    if ((description == null) && (company == null))
    {
        Xrm.Page.data.process.movePrevious(function () {
        Xrm.Page.ui.setFormNotification('Either Description OR Company is required', 'ERROR');
        });
    }
    }
}

Here we get the error message as expected.


Anyway, we need to add Xrm.Page.ui.clearFormNotification(); in onSave event so notices will be cleared when filling either of fields.

Sep 17, 2017

Importing Duplicate Detection Rules

Since early versions of Dynamics 365, we have been using Duplicate Detection rules and it is a handy feature. Ironically, still we can't import those rules with Solution. In most cases, users configure them in different environment and its not a big deal. Still, it can be error-prone and inconsistent.

So alternative is to consider Duplicate Detection Rule entities as reference data and import them using Configuration Migration Tool coming with SDK. Usually you will find this tool in below location of the SDK;

\SDK\Tools\ConfigurationMigration

Anyway, below are the two points to keep in mind when importing Duplicate Detection rules;

1) Rules should be Unpublished before importing.
2) Need to include both Duplicate Detection Rule and Duplicate Rule Condition in correct Order.


Hope this helps.

Sep 14, 2017

Convert in between Local Time and UTC Time

Though this is not a new subject, thought of sharing a simplified version of Code snippet to be used in converting Local Time and UTC Time as necessary. Base for this code was taken from SDK, but I have simplified and made a Ready Made class, which I though is helpful.

Let's remind few reasons why these conversions are very important to Dynmics 365 implementations;
  • DateTime fields are always kept in DB as UTC, but shown in UI with Users Local Time.
  • DateTime Retrieve with Web Service is always UTC.
  • Plugin/WF Context returns UTC time. 
Below are some of the scenarios you may come across that need conversions in your code.
  • When you need to do a calculation based on Time
  • When you need to pass Data to another System

Here is the class I suggest;
using Microsoft.Xrm.Sdk;
using System;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;

namespace CommonLibrary
{
    internal static class CommonUtility
    {
        internal static DateTime RetrieveLocalTimeFromUTCTime(IOrganizationService service, DateTime utcTime)
        {
            return RetrieveLocalTimeFromUTCTime(utcTime, RetrieveCurrentUsersSettings(service), service);
        }

        internal static DateTime RetrieveUTCTimeFromLocalTime(IOrganizationService service, DateTime localTime)
        {
            return RetrieveUTCTimeFromLocalTime(localTime, RetrieveCurrentUsersSettings(service), service);
        }

        internal static int? RetrieveCurrentUsersSettings(IOrganizationService service)
        {
            var currentUserSettings = service.RetrieveMultiple(
                new QueryExpression("usersettings")
                {
                    ColumnSet = new ColumnSet("timezonecode"),
                    Criteria = new FilterExpression
                    {
                        Conditions =
                        {
                    new ConditionExpression("systemuserid", ConditionOperator.EqualUserId)
                        }
                    }
                }).Entities[0].ToEntity<Entity>();
            return (int?)currentUserSettings.Attributes["timezonecode"];
        }

        internal static DateTime RetrieveLocalTimeFromUTCTime(DateTime utcTime, int? timeZoneCode, IOrganizationService service)
        {
            if (!timeZoneCode.HasValue)
                return DateTime.Now;
            var request = new LocalTimeFromUtcTimeRequest
            {
                TimeZoneCode = timeZoneCode.Value,
                UtcTime = utcTime.ToUniversalTime()
            };
            var response = (LocalTimeFromUtcTimeResponse)service.Execute(request);
            return response.LocalTime;
        }

        internal static DateTime RetrieveUTCTimeFromLocalTime(DateTime localTime, int? timeZoneCode, IOrganizationService service)
        {
            if (!timeZoneCode.HasValue)
                return DateTime.Now;
            var request = new UtcTimeFromLocalTimeRequest
            {
                TimeZoneCode = timeZoneCode.Value,
                LocalTime = localTime
            };
            var response = (UtcTimeFromLocalTimeResponse)service.Execute(request);
            return response.UtcTime;
        }
    }
}

Suppose we need to Retrieve Local time from UTC time (dateTime could be a field coming from Plug-in context);

DateTime dateTimeLocal = CommonUtility.RetrieveLocalTimeFromUTCTime(service, dateTime);

Sep 11, 2017

Azure Subscriptions for Dynamics 365 Developers

Now it’s time to jump in to Azure and find out what’s available for Dynamics Developers. First thing first, how to get azure subscription. I mean FREE subscription to play around and see.

Option 1
Get the general Azure Trial worth $200.

Check: https://azure.microsoft.com/en-au/free/

Only issue with this option is it last only 30 days, you must complete all your drama in 30 days!

Option 2
Register for Dev essentials program, which gives $300 worth credit for one year, which is good.

Check: https://www.visualstudio.com/dev-essentials/


Warning
What so ever, one thing you may need to know is these free subscriptions are NOT per account, BUT per person. Though you try with different email addresses Microsoft will nail you down with Mobile OR/AND Credit Card details. In fact, you can use one of the above two choices.
Sadly, registration form just through a generic error (i.e. We cannot proceed with signup due to an issue with your account. Please contact billing support) and you may not be able to understand why you can’t register.


Option 3
Now come the thrilling part. As Dynamics Developers we always work with Dynamics 365 trials. Though they expire in 30 days, we are ALLOWED to create as many instances as we want.

Check: https://www.microsoft.com/en-us/dynamics/free-crm-trial.aspx

Actually, once you create Dyanmics 365 instance, relevant Admin user get free subscription automatically assigned to that account! Isn’t it fun?

Problem solved! Though it is painful to create Dynamics 365 trials again and again, at least we know that we can get Azure subscriptions as we wish regardless of how many we had in the past.

Feel privileged to be a Dynamics 365 Developer!

(Thanks Sam and Proveen for the help in this regards)

Aug 29, 2017

Retrieve Multiple - FetchXML with Alias

This is not a complex thing, but though of posting the code snippet since I found it important to explain a bit. When we need to retrieve data in related records few levels away from our current context, best method is using the FetchXML and use ReiveMultiple method. This will avoid us using many server calls to reach our entity.

Reason for writing this post is to explain one point. When we do complex FetchXMLs we definitely need to use Alias for linked entities. So reading the data in the code can be little different.

Check my example; I have an entity called job. Job got a lookup field to Project. Project got a lookup to User. Suppose we need to read the Mobile Number of the user (3 levels ahead) while we have only Job Id in hand, we use FetchXML as below;

internal static EntityCollection RetriveProjectAdminMobileByJobId(IOrganizationService service, Guid Id)
{
var fetchXml = string.Format(@"<fetch mapping='logical' output-format='xml-platform' version='1.0' distinct='false'>  
               <entity name='new_job'>
               <filter type='and'>
               <condition attribute='new_jobid' operator='eq' value='{0}' />
               </filter>
                   <link-entity name='new_project' to='new_jobprojectid' from='new_projectid' alias='PROJ' link-type='outer' visible='false'>
                       <link-entity name='systemuser' to='new_projectadmin' from='systemuserid' link-type='outer' alias ='PROJADMIN'>
                            <attribute name='mobilephone' />
                        </link-entity>
                   </link-entity>
               </entity>
               </fetch>", Id);
            return service.RetrieveMultiple(new FetchExpression(fetchXml));

Please notice, how I have used Aliases meaningfully. Now what we need to keep in mind is resulting fields would come with that Alias. In fact, mobile phone field is like PROJADMIN.mobilephone. Now check how I read it in C#;

var entityCollection = RetriveProjectAdminMobileByJobId(service, workOrderRef.Id);

if (entityCollection == null || entityCollection.Entities == null || entityCollection.Entities.Count <= 0)
    return;

if (entityCollection.Entities[0].Attributes.Contains("PROJADMIN.mobilephone"))
    string AdminMobile = (string)((AliasedValue)entityCollection.Entities[0].Attributes["PROJADMIN.mobilephone"]).Value;

I advice always try the FetchXmls before using. Best tool to do so is FetchXM Tester of XrmToolBox.


There is a one pitfall. I have seen some tools which are not returning the correct field names as expected. For example, I have tested same FetxhXML in DataSet creating tool in SSRS report authering extensions in VS2012 that returned like PROJADMIN-mobilephone, which is WRONG.

Aug 16, 2017

Code snippet to add Lookup filter

This is just a simple JavaScript to filter lookup based on existing field value in the form.

In this scenario, we will assume we needs to set Payment Method to a lookup field (i.e. new_paymentmethodid). Its user-friendly if we can filter the lookup values to show only the payments of contact (i.e. new_contactid). This code snippet will work for this.

PaymentMethodFilter = function () {
    Xrm.Page.getControl("new_paymentmethodid").addPreSearch(addPaymentMethodFilter);
}

addPaymentMethodFilter = function () {
    var contact = Xrm.Page.getAttribute('new_contactid');
    if ((contact == null) || (contact.getValue() == null) || (contact.getValue()[0] == undefined)) return;
    var paymentMethodFilter = "<filter type='and'><condition attribute='new_paymentowner' uiname='" + contact.getValue()[0].name + "' operator='eq' uitype='contact' value='" + contact.getValue()[0].id + "' /></filter>";
    Xrm.Page.getControl("new_paymentmethodid").addCustomFilter(paymentMethodFilter, "new_paymentmethod");
}

Now call PaymentMethodFilter method for onChange of new_contactid field.

Jul 10, 2017

Call WFs and Actions from Plug-ins

When developing server-side custom functionalities for Dynamics 365, our initial though is whether jump in to plug-ins, WFs or Actions. We always need to check the pros and cons of them depending on the scenario.

By the meantime, separating the functionalities among them and using them harmoniously would add more value in terms of flexibility. Below code snippets could be helpful in such an approach.

Call a WF from a Plug-in;

ExecuteWorkflowRequest request = new ExecuteWorkflowRequest()
{
  WorkflowId = new Guid("019813bc-104b-4dc9-93d5-54d93d79908e"), //WF Id
  EntityId = Id
};
ExecuteWorkflowResponse executeWorkflowResponse = (ExecuteWorkflowResponse)service.Execute(request);

Call an Action from a Plug-in;

OrganizationRequest req = new OrganizationRequest("new_profitcalculator");
req["Amount"] = amount; //Parameter
req["Target"] = new EntityReference(new_office.EntityLogicalName, Id);
OrganizationResponse response = service.Execute(req);

May 23, 2017

Web API - Retrieve records in JavaScript

It is high time to use WEB API for JavaScript calls, though no one is recommending one particular library for that. In fact, underlying argument has two sides;

1) Using a Library - can be tricky since Microsoft can change WEB API and no guarantee new version of Library will be out
2) Without a Library - Coding sequential lines will not manageable in long run

When take both in to account we decided we will have our own light library (easily updated as necessary).

In Order to do this, we found a cool tool that create JS codes for WEB API. This is simply a Dynamics Solution which needs to be deployed first as any other solution. Then you will see the button that launches the tool;


So we generated record Retrieve operation and made below small library for Retrieval of single record in general manner. (We plan to extend this to other CRUD operations).

We made same method to be used to below scenarios;

1) Retrieve record by Id
2) Retrieve record by Criteria

So this is the simple Library. I hope this is manageable than a heavily customized and heavy one.

function RetrieveEntityById(id, entityName, fieldsArry, filterStr) {
    var RetrieveStr = RetrieveStringBuilder(id, entityName, fieldsArry, filterStr);
    return RetrieveEntityByIdWebApi(RetrieveStr);
}

function IdBracketRemover(id) {
    return (id.indexOf('{')>-1) ? id.substring(1, id.length - 1) : id;
}

function RetrieveStringBuilder(id, entityName, fieldsArry, filterStr) {
    var Str;

    if (id != null)
    { Str = entityName + '(' + IdBracketRemover(id) + ')' + '?$select='; }
    else
    { Str = entityName + '?$select='; }

    for (i = 0; i < fieldsArry.length; i++)
    { Str = Str + fieldsArry[i] + ','; }
    Str = Str.substring(0, Str.length - 1);
    if (filterStr != null)
        Str = Str + '&$filter=' + filterStr;
    return Str;
}

function RetrieveEntityByIdWebApi(RetriveString) {
    var result;
    var req = new XMLHttpRequest();
    req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/" + RetriveString, false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                result = JSON.parse(this.response);
            } else {
                Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
    return result;
}

Please find the ways of calling the method for two different scenarios as mentioned. Check how parameters become null in different scenarios.

// Query basded on Id
// string : "/customeraddresses(1A170E1D-91AE-4965-8631-0FB9270504D7)"
var fieldsArry = ['city', 'country'];
var Id = '{1A170E1D-91AE-4965-8631-0FB9270504D7}';
var AddressEnt = RetrieveEntityById(Id, 'customeraddresses', fieldsArry, null);
if (AddressEnt != null) {
    alert(AddressEnt["city"]);
}


// Query basded on condition (Parent ID and Prefred = true
// string : "/customeraddresses?$filter=_parentid_value eq 19139FC0-DC44-4E88-A793-924F1F90B08F 
//          and  smsmt_ispreferred eq true"
var fieldsArry = ['city', 'country'];
var ParentId = '{19139FC0-DC44-4E88-A793-924F1F90B08F}';
var filterStr = 'new_ispreferred eq true and  _parentid_value eq ' + IdBracketRemover(Id);
var AddressEnt = RetrieveEntityById(null, 'customeraddresses', fieldsArry, filterStr);
if ((AddressEnt != null) && (AddressEnt.value[0] != null)) {
    alert(AddressEnt.value[0].city);
}

For the moment, this seems to be catering the need. If I develop the other operations, I will post them. Anyway, it is the CRM Rest Builder generated this code for me, what I have done is making little changes with parameters to create correct string to be passed.

Special thanks to my colleague Biplab Singha for guiding to this approach.

Post Note;
Noticed assigning a lookup value after retrieving through this could be little tricky. Please find below code snippet of assigning lookup value (coming from entity called new_shareholders and lookup field called new_shname) to primarycontact;


var shEnt = RetrieveEntityById(null, 'new_shareholders', fieldsArry, filterStr);
if ((shEnt != null) && (shEnt.value[0] != null)) {
    var constLookup = new Array();
    constLookup[0] = new Object();
    constLookup[0].id = shEnt.value[0]['_new_shname_value'];
    constLookup[0].name = shEnt.value[0]['_new_shname_value@OData.Community.Display.V1.FormattedValue'];
    constLookup[0].entityType = shEnt.value[0]['_new_shname_value@Microsoft.Dynamics.CRM.lookuplogicalname'];
    Xrm.Page.getAttribute('primarycontact').setValue(constLookup);
}

Mar 20, 2017

Track Underage contact based on birthday

Dynamics 365 doesn’t give a functionality to identify under age contacts though it captures the birthday by default. Here is a simple code snippet to switch a flag based on age. We can now simply distinguish under age (<18) contact easily through a view.

SetUnderAgeFlag = function() {
  var birthDate = Xrm.Page.getAttribute('birthdate').getValue();
  if (birthDate  != null)
  {
      var today = new Date();
      var nowyear = today.getFullYear();
      var nowmonth = today.getMonth();
      var nowday = today.getDate();             

      var birthyear = birthDate.getFullYear();
      var birthmonth = birthDate.getMonth();
      var birthday = birthDate.getDate();

      var age = nowyear - birthyear;
      var age_month = nowmonth - birthmonth;
      var age_day = nowday - birthday;

      if ( (age < 18) || ((age==18) && (age_month<0)) || ((age==18) && (age_month==0) && (age_day <0)) )
      {         
         Xrm.Page.getAttribute('new_under18flag').setValue(true);
      }
      else
      {
         Xrm.Page.getAttribute('new_under18flag').setValue(false);
      } 
      Xrm.Page.getAttribute('new_under18flag').setSubmitMode('always');  
  }   
}

Jan 4, 2017

Calling third party Web service from Dynamics CRM online plug-in

Just thought of sharing this important code snippet. Please have a closer look at Binding Configuration part which is the essence of the exercise.

public class PostUpdateTransaction : IPlugin
{
  public void Execute(IServiceProvider serviceProvider)
  {
    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    IOrganizationService service = (IOrganizationService)serviceFactory.CreateOrganizationService(context.UserId);
    ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

    if (context.Depth > 1)
    {
        return;
    }

    try
    {
        BasicHttpBinding myBinding = new BasicHttpBinding();
        myBinding.Name = "BasicHttpBinding_Service";
        myBinding.Security.Mode = BasicHttpSecurityMode.Transport;
        myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
        myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
        myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

        EndpointAddress endPointAddress = new EndpointAddress(@"https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.asmx");
        ClassLibrary1.XXXXX.XXXClient serviceClient = new ClassLibrary1.XXXXX.XXXClient(myBinding, endPointAddress);
        string xmlRequest = @"XXX";

        string result = serviceClient.<Method>(xmlRequest);
        XmlDocument resultXML = new XmlDocument();
        resultXML.LoadXml(result);

     }
     catch (Exception ex)
     {
        throw new InvalidPluginExecutionException(ex.Message);
     }
     finally
     {

     }
  }
}