Sunday, 28 August 2016

Using money fields in plugins in Dynamics CRM

Hi all,

I had faced some issue finding the correct way to update the money type fields in Dynamics CRM and I write this post so that you do not have to face the same issue in your development journey. As you would know when we create a field in Dynamics CRM of the "Currency" type it creates internally another field with schema name  ' "schema name of the original currency field"_base'. Basically in this _base field the currency value is stored in the base currency of the system and in the original field the value is stored in the user's chosen currency or organizational set currency. Hence, each record in Dynamics CRM has a Currency lookup field with schema name "transactioncurrency" to the transactioncurrency entity that contains the details of the currency when this record was created. Among other details this entity also contains the exchange rate between this currency and the base currency of the system.

So my requirement was to update the total fees field in opportunity when the "fees" field in one of the related Opportunity product entities was updated. Due to some security related requirement, we were not using roll-up field and needed to achieve the functionality using a plugin.

 decimal totalDealProductFees = 0;
 var dealProductsQuery = new QueryExpression("opportunityproduct");
 dealProductsQuery.ColumnSet = new ColumnSet(new string[] { "contoso_fees_base" });
 dealProductsQuery.Criteria.AddCondition("opportunityid", ConditionOperator.Equal, dealEntityRef.Id);
 EntityCollection dealProductsQueryResults = orgService.RetrieveMultiple(dealProductsQuery);                              
 if (dealProductsQueryResults != null && dealProductsQueryResults.Entities != null && dealProductsQueryResults.Entities.Count > 0)
 {
tracingService.Trace("The number of deal product records obtained is: " + dealProductsQueryResults.Entities.Count);
        foreach (var dealProduct in dealProductsQueryResults.Entities)
        {                
          var feesDealProduct = dealProduct.GetAttributeValue<Money>("contoso_fees_base");
                if (feesDealProduct != null)
                    {
                           totalDealProductFees += feesDealProduct .Value;
                    }                                                    
        }
 }


 var exchangeRate = GetCurrencyExchangeRate(orgService, dealEntity.Id);
 dealEntity["contoso_totalfees"] = new Money(totalDealProductFees * exchangeRate);
 dealEntity["contoso_totalfees_base"] = new Money(totalDealProductFees);
 orgService.Update(dealEntity);


private static decimal GetCurrencyExchangeRate(IOrganizationService service, Guid dealId)
 {
     Guid CurrencyId = Guid.Empty;
     decimal oppExchangeRate = 0;
         
     if (dealId != Guid.Empty)
        {
          Entity oppEntity = service.Retrieve("opportunity", dealId, new ColumnSet("transactioncurrencyid"));
         CurrencyId = oppEntity.GetAttributeValue<EntityReference>("transactioncurrencyid").Id;
        }
           
     if (CurrencyId != Guid.Empty)
        {
          Entity currencyEntity = service.Retrieve("transactioncurrency", CurrencyId, new ColumnSet("exchangerate"));
          oppExchangeRate = currencyEntity.GetAttributeValue<decimal>("exchangerate");
        }
           
     return oppExchangeRate;
 }


The GetCurrencyExchangeRate method gets the currency exchange rate by obtaining the relevant fields from the opportunity entity and the transactioncurrency entities as discussed in the theory above.

As you will observe the currency fields are assigned their value by assigning to them a Money object, which has a constructor that takes a decimal value. We assign the actual computed value to the base field in the deal (opportunity) record and the value that is shown to the user is updated with the the actual value multiplied by the required exchange rate as obtained by the method discussed above.

Thus by following the above approach we can ensure that the currency values are in sync as required by the platform.

Hope this helps and feel free to leave your comments.
Thanks and Happy Dynamics!

Monday, 15 August 2016

Unit testing plugins in Dynamics CRM using Visual studio unit testing framework

Hi all,

In this post I will discuss the code that you can use to unit test your Dynamics CRM plugins using Visual studio unit testing framework.
You would first need to create the fake libraries for all the CRM SDK libraries as well as mscorlib and system DLLs. Make sure that the version matches with the version of the DLLs being used in the code. I have CRM 2015 SDK fakes and mscorlib/system DLL fakes here.

You can define a helper class with the following helper methods:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Fakes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Fakes;

namespace Contoso.CRM.Unittests
{
    public class HelperMethodsContainer
    {
        /// <summary>
        /// Moles the plugin variables.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="pluginContext">The plugin context.</param>
        /// <param name="organizationService">The organization service.</param>
        /// <param name="stageNumber">The stage number.</param>
        /// <param name="messageName">Name of the message.</param>
        public static void MolePluginVariables(
            StubIServiceProvider serviceProvider,
            StubIPluginExecutionContext pluginContext,
            StubIOrganizationService organizationService,
            int stageNumber,
            string messageName)
        {
            var serviceFactory = new StubIOrganizationServiceFactory();
            var tracingService = new StubITracingService();
            if (serviceProvider != null)
            {
                serviceProvider.GetServiceType = type =>
                {
                    if (type == typeof(IPluginExecutionContext))
                    {
                        return pluginContext;
                    }
                    else if (type == typeof(ITracingService))
                    {
                        return tracingService;
                    }
                    else if (type == typeof(IOrganizationServiceFactory))
                    {
                        return serviceFactory;
                    }

                    return null;
                };
            }

            pluginContext.DepthGet = () => 1;
            pluginContext.UserIdGet = () => new Guid();
            pluginContext.MessageNameGet = () => messageName;
            pluginContext.StageGet = () => stageNumber;
            pluginContext.InitiatingUserIdGet = () => new Guid();
            pluginContext.CorrelationIdGet = () => new Guid();
            pluginContext.PrimaryEntityIdGet = Guid.NewGuid;
            pluginContext.IsInTransactionGet = () => true;
            serviceFactory.CreateOrganizationServiceNullableOfGuid = t1 => organizationService;
            tracingService.TraceStringObjectArray = Trace;
        }

        /// <summary>
        /// Sets the mole for the Entity
        /// </summary>
        /// <param name="entityName">The LogicalName of the entity</param>
        /// <param name="attributeValues">The attributes of the entity</param>
        /// <param name="context">Object of type SIPluginExecutionContext</param>
        /// <returns>Object of type Entity</returns>
        public static Entity MoleEntity(string entityName, Dictionary<string, object> attributeValues, StubIPluginExecutionContext context)
        {
            var entity = new Entity(entityName);
            entity.Attributes = new AttributeCollection();

            if (attributeValues != null)
            {
                foreach (string key in attributeValues.Keys)
                {
                    entity.Attributes.Add(key, attributeValues[key]);
                }
            }

            if (context != null)
            {
                context.PrimaryEntityNameGet = () => entityName;
            }

            entity.Id = Guid.NewGuid();

            return entity;
        }

/// <summary>
        /// Moles the PluginContext.InputParameters
        /// </summary>
        /// <param name="context">the mole of the IPluginExecutionContext</param>
        /// <param name="inputParameterCollection">Object of type System.Dictionary</param>
        public static void MolePluginInputParameters(StubIPluginExecutionContext context, Dictionary<string, object> inputParameterCollection)
        {
            if (inputParameterCollection != null)
            {
                var parameterCollection = new ParameterCollection();
                foreach (var key in inputParameterCollection.Keys)
                {
                    parameterCollection.Add(key, inputParameterCollection[key]);
                }

                if (context != null)
                {
                    context.InputParametersGet = () => parameterCollection;
                }
            }
        }
   }
}

The first method "moles" the basic plugin variables which include the plugin depth, userID, message, stage, etc. The initial values can be anything unless they are required in the test logic.

The second method is used to mole the primary entity for the plugin (the entity which triggers the plugin)

The third method is used to set the input parameters for the plugin. This includes the "Target" parameter for a create/update plugin, etc.

In order to perform the actual test, we need to use the above methods to mole the basic plugin inputs. After that we also need to provide the implementations for the fake methods that will be invoked from the plugin. This is the standard way in which fakes framework works, where we provide the delegate that imitates what the function is expected to return.

Here is an example of a test class:

using System;
using System.Collections.Generic;
using System.Fakes;
using ContosoCRM.Plugins;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Fakes;
using Microsoft.Xrm.Sdk.Query;

namespace ContosoCRM.Unittests
{
    [TestClass]
    public class PostEmailCreateTests
    {
        [TestMethod]
        public void PostEmailCreateCreateTaskWhenConditionsMeet()
        {
            var postEmailCreatePlugin = new PostEmailCreate();
            var serviceProvider = new StubIServiceProvider();
            var pluginContext = new StubIPluginExecutionContext();
            var organizationService = new StubIOrganizationService();

            // Mole the basic Plugin objects
            HelperMethodsContainer.MolePluginVariables(serviceProvider, pluginContext, organizationService, 40, "Create");
            var subjectField = "Alpha beta " + "test suffix";          
            var entityAttributes = new Dictionary<string, object>();
            entityAttributes.Add("subject", subjectField);
            entityAttributes.Add("regardingobjectid", new EntityReference("opportunity", Guid.NewGuid()));
            entityAttributes.Add("description", "Hello world!");  
            Entity appointmentEntity = HelperMethodsContainer.MoleEntity("email", entityAttributes, pluginContext);

            // set the entity in the parameter collection
            var parameterAttributes = new Dictionary<string, object>();
            parameterAttributes.Add("Target", appointmentEntity);
            HelperMethodsContainer.MolePluginInputParameters(pluginContext, parameterAttributes);

            organizationService.CreateEntity = entity => Guid.NewGuid();
            organizationService.RetrieveStringGuidColumnSet = (s, guid, arg3) =>
            {
                var dealEntity = new Entity("opportunity");
                dealEntity.Id = Guid.NewGuid();
                dealEntity.Attributes.Add("name", "Alpha oppty");
                dealEntity.Attributes.Add("ownerid", new EntityReference("systemuser", Guid.NewGuid()));
                return dealEntity;
            };

            organizationService.RetrieveMultipleQueryBase = delegate(QueryBase query)
            {
                var entityCollection = new EntityCollection();
                Entity entity;
                if (query is QueryExpression && (query as QueryExpression).EntityName == "contoso_configuration")
                {
                    entity = new Entity("contoso_configuration")
                    {
                        Attributes = new AttributeCollection
                        {
                            { "contoso_name", "Alpha" },
                            { "contoso_value", "4"}
                        }
                    };
                    entityCollection.Entities.Add(entity);
                }
                if (query is QueryExpression && (query as QueryExpression).EntityName == "contoso_activitycategory")
                {
                    entity = new Entity("contoso_activitycategory")
                    {
                        Attributes = new AttributeCollection
                        {
                            { "contoso_name", "Validation" }                          
                        }
                    };
                    entityCollection.Entities.Add(entity);
                }

                return entityCollection;
            };
            postEmailCreatePlugin.Execute(serviceProvider);
        }
   }
}


The above unit test is for a plugin called "PostEmailCreate" which creates a task record whenever an email record is created in Dynamics CRM based on some conditions (like the subject of the email and its regarding object).

In the above it can be seen that delegates are provided to imitate the results of a retrieve organization service call ( organizationService.RetrieveStringGuidColumnSet ), a retrieve multiple organization service call ( organizationService.RetrieveMultipleQueryBase  ) and a create organization request (organizationService.CreateEntity). It can also be seen that different values are being returned based on some conditions (like which entity triggered the call, etc.) in the retrieve calls.

I hope with the above you get a general idea on how to write unit tests for your plugins that you write in Dynamics CRM. Let me know in the comments if you would like to know more about unit testing your plugins in Dynamics CRM.

Happy Dynamics CRM!!

Retrieve master entity record values using C#

Hi all,

In continuation of my previous post here: http://musingsofasoftwareenthusiast.blogspot.in/2016/08/retrieve-data-from-configuration-record.html, here I will discuss how we can retrieve any record with the specified column set and specified search criteria by creating a generic method. In particular, this will be extremely useful to retrieve the values from "Master" entity record. A "Master" entity is a entity whose records are less likely to be created/modified/deleted when the Dynamics CRM system is being used. Also, master records will have a unique value like its name by which we can identify the record.

Here is the generic method:

/// <summary>
        /// Retrieves the entity by unique values.
        /// </summary>
        /// <param name="entityName">Name of the entity.</param>
        /// <param name="queryColumns">The query columns.</param>
        /// <param name="requiredColumns">The required columns.</param>
        /// <param name="service">The service.</param>
        /// <returns>The entity as per the provided parameters</returns>
        public static Entity RetrieveEntityByUniqueValues(string entityName, Dictionary<string, object> queryColumns, List<string> requiredColumns, IOrganizationService service)
        {
            if (!string.IsNullOrWhiteSpace(entityName) && queryColumns != null && requiredColumns != null &&
                service != null)
            {
                var query = new QueryExpression(entityName);
                query.ColumnSet = new ColumnSet();
                foreach (var requiredColumn in requiredColumns)
                {
                    query.ColumnSet.AddColumn(requiredColumn);
                }

                foreach (var queryColumn in queryColumns)
                {
                    query.Criteria.AddCondition(queryColumn.Key, ConditionOperator.Equal, queryColumn.Value);
                }

                var recordsCollection = service.RetrieveMultiple(query);
                if (recordsCollection != null && recordsCollection.Entities != null &&
                    recordsCollection.Entities.Count > 0)
                {
                    return recordsCollection.Entities[0];
                }
            }

            return null;
        }

Here is how the method is called:

// get the validation activity category record
var validationActivityCategoryRecord =
Common.RetrieveEntityByUniqueValues(
"contoso_activitycategory",
new Dictionary<string, object>()
        {
           {"contoso_name", "Validation"}
        },
        new List<string>()
        {
           "contoso_name"
        },
        orgService);

Here is a walk through of the parameters that the method expects and how it works:

The first parameter is the name of entity that is expected.

The second parameter is a dictionary of the columns that will be part of the conditional expression in the query expression. As can be seen from the code above it is doing an equality check with the value provided. In the invocation above, I am passing the primary field of this entity (contoso_name) and providing the expected value as "Validation".

The third parameter is the list of columns that are expected from the entity being retrieved. Sometimes, we might not need any value as such, say when we are assigning the retrieved value to an entity reference field, so we can give an empty list (or the field that is part of the condition expression).

The last parameter is the organization service which is required to perform the platform operation.

I hope the above snippets help you to build up your Dynamics CRM Utility library!
Happy Dynamics CRMing!


Retrieve data from configuration record using C# in Dynamics CRM

Hi guys,

In continuation of my previous blog here: http://musingsofasoftwareenthusiast.blogspot.in/2016/08/retrieve-data-from-configuration.html, here I will discuss (and give the code) to retrieve data from configuration record using C#. In particular, I would keep this code in a Common.cs file, so that the methods in this file can be accessed by all plugins/workflow assemblies.

/// <summary>
        /// Get the value of the configuration record corresponding to the key
        /// </summary>
        /// <param name="configurationKeyName">configuration Key Name</param>
        /// <param name="service">IOrganization service</param>
        /// <returns>The value of the configuration record for the supplied key</returns>
        public static string RetrieveConfigurationData(string configurationKeyName, IOrganizationService service)
        {
            Entity configurationEntity = null;
            EntityCollection configurationEntityCollection = null;          
                QueryExpression qE = new QueryExpression("contoso_configuration");
                qE.ColumnSet = new ColumnSet("contoso_value");
                qE.Criteria.AddCondition(new ConditionExpression("contoso_name", ConditionOperator.Equal, configurationKeyName));
                if (service != null)
                {
                    configurationEntityCollection = service.RetrieveMultiple(qE);
                }

                if (configurationEntityCollection != null && configurationEntityCollection.Entities != null && configurationEntityCollection.Entities.Count > 0)
                {
                    configurationEntity = configurationEntityCollection.Entities[0];
                }
                else
                {
                    throw new InvalidPluginExecutionException("There is no configuration record with the name: " + configurationKeyName);
                }
           
            string configurationEntityValue = string.Empty;
            if (configurationEntity != null)
            {
                configurationEntityValue = configurationEntity.GetAttributeValue<string>("contoso_value");
            }

            return configurationEntityValue;
        }

I have used late binding here, so that names of the entity and fields will be as per your organization.

Also, note that unlike the Javascript example in my earlier post here we are using the all small case schema name to refer to the names of the entity and the fields.

Let me know if this helps by giving your feedback!
Happy Dynamics CRMing!

Retrieve data from configuration records in Dynamics CRM using JavaScript

In most of my projects, we have a configuration record in Dynamics CRM from where we retrieve configuration values. Here is a code snippet that will retrieve the configuration data based on the name supplied.

var common = common || {};

/*
Purpose : Get the configuration record value whose name has been provided
*/
common.getValueFromConfigurationRecordBasedOnName = function(configurationRecordName, isAsync) {
    if (configurationRecordName) {
        console.log("The configuration record name is: " + configurationRecordName);
        var oDataSet = "contoso_configuration";
        var options = "$select=contoso_Value&$filter=contoso_name eq '" + configurationRecordName + "'";
        var configurationDataRecordValue;
        if (isAsync == null) {
            isAsync = false;
        }
        SDK.REST.retrieveMultipleRecords(oDataSet, options, function (response) {
            configurationDataRecordValue = common.getValueFromConfigurationRecordBasedOnNameHelper(response);
        }, SDK.REST.errorCallback, function () {
            console.log("Retrieve multiple call for retrieving configuration record data completed");
        }, isAsync);
        return configurationDataRecordValue;
    } else {
        console.log("The configuration record name has not been provided.");
        return null;
    }
}

/*
Purpose : Helper function to process the response to get the configuration record value whose name has been provided
*/
common.getValueFromConfigurationRecordBasedOnNameHelper = function(response) {
    if (response) {
        console.log("The number of responses retrieved is: " + response.length);
        if (response.length > 0) {
            return response[0].contoso_Value;
        } else {
            console.log("There is no configuration data record with the given name");
        }
    } else {
        console.log("There is null response retrieved as part of the ODATA call.");
    }

    return null;
}

Here I have used the SDK.REST JavaScript library (a JS file) (find it here: https://msdn.microsoft.com/en-us/library/gg334427(v=crm.7).aspx#BKMK_SDKREST ) which also comes with the SDK. This helps avoid writing code to trigger the AJAX request while making the ODATA query. I have added an additional parameter called "isAsync" that mentions whether the request needs to be asynchronous or not by modifying the method in the SDK.REST file itself. This is required as in some cases we need to retrieve the data in a synchronous manner to drive the correct behavior.

This is the updated method declaration:

retrieveMultipleRecords: function (type, options, successCallback, errorCallback, OnComplete, isAsync) {
        ///<summary>
        /// Sends an asynchronous request to retrieve records.
        ///</summary>
        ///<param name="type" type="String">
        /// The Schema Name of the Entity type record to retrieve.
        /// For an Account record, use "Account"
        ///</param>
        ///<param name="options" type="String">
        /// A String representing the OData System Query Options to control the data returned
        ///</param>
        ///<param name="successCallback" type="Function">
        /// The function that will be passed through and be called for each page of records returned.
        /// Each page is 50 records. If you expect that more than one page of records will be returned,
        /// this function should loop through the results and push the records into an array outside of the function.
        /// Use the OnComplete event handler to know when all the records have been processed.
        /// </param>
        ///<param name="errorCallback" type="Function">
        /// The function that will be passed through and be called by a failed response.
        /// This function must accept an Error object as a parameter.
        /// </param>
        ///<param name="OnComplete" type="Function">
        /// The function that will be called when all the requested records have been returned.
        /// No parameters are passed to this function.
        /// </param>
        ///<param name="isAsync" type="Boolean">
        /// Whether the operation should be asynchronously performed or not
        /// </param>
        this._stringParameterCheck(type, "SDK.REST.retrieveMultipleRecords requires the type parameter is a string.");
        if (options != null)
            this._stringParameterCheck(options, "SDK.REST.retrieveMultipleRecords requires the options parameter is a string.");
        this._callbackParameterCheck(successCallback, "SDK.REST.retrieveMultipleRecords requires the successCallback parameter is a function.");
        this._callbackParameterCheck(errorCallback, "SDK.REST.retrieveMultipleRecords requires the errorCallback parameter is a function.");
        this._callbackParameterCheck(OnComplete, "SDK.REST.retrieveMultipleRecords requires the OnComplete parameter is a function.");

        var optionsString;
        if (options != null) {
            if (options.charAt(0) != "?") {
                optionsString = "?" + options;
            }
            else { optionsString = options; }
        }
        var req = new XMLHttpRequest();
        req.open("GET", this._ODataPath() + type + "Set" + optionsString, isAsync);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.onreadystatechange = function () {
            if (this.readyState == 4 /* complete */) {
                req.onreadystatechange = null;
                if (this.status == 200) {
                    var returned = JSON.parse(this.responseText, SDK.REST._dateReviver).d;
                    successCallback(returned.results);
                    if (returned.__next != null) {
                        var queryOptions = returned.__next.substring((SDK.REST._ODataPath() + type + "Set").length);
                        SDK.REST.retrieveMultipleRecords(type, queryOptions, successCallback, errorCallback, OnComplete);
                    }
                    else { OnComplete(); }
                }
                else {
                    errorCallback(SDK.REST._errorHandler(this));
                }
            }
        };
        req.send();
    }

I have highlighted the changes made to the original retrieve multiple method above wherever the isAsync parameter is used.

Here is how to call the original retrieve configuration method:

var incrementValue = common.getValueFromConfigurationRecordBasedOnName("increment", false);



Additionally, there is also this method that will convert the configuration data value into an array (comma character in the configuration data value acts as the delimiter in the array)

/*
Purpose     : Retrieve configuration data and convert coma seprated string into an array after trimming the white spaces at the begining and end.
*/
common.getConfigurationDataConvertedToArray = function (configName) {
    var configRecord = common.getValueFromConfigurationRecordBasedOnName(configName, false);
    var configRecordArray = [];
    if (configRecord) {
        console.log("getConfigurationDataConvertedToArray() : Value of configuration record is " + configRecord);
        configRecordArray = configRecord.split(",");
        console.log("The number of items in the record: " + configRecordArray.length);
        for (var i = 0; i < configRecordArray.length; i++) {
            if (configRecordArray[i]) {
                configRecordArray[i] = configRecordArray[i].trim();
            }
        }
    } else {
        console.log("getConfigurationDataConvertedToArray() : configuration data is null.");
    }
    return configRecordArray;
}


Let me know if the above code snippets help you. Happy Dynamics CRMing!!!

Develop a custom workflow activity in Dynamics CRM

Hi all,

In today's post I will walk through a scenario where we will develop a custom workflow activity for some requirement in Dynamics CRM.

To create a custom workflow activity (similar to plugins) we need to inherit from the abstract class "CodeActivity". The code below will show the process in which to obtain the other important properties like the organization service and the tracing service from the context.


namespace Contoso.CRM.Workflows
{
    using System;
    using System.Activities;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Messages;
    using Microsoft.Xrm.Sdk.Query;
    using Microsoft.Xrm.Sdk.Workflow;
     
    /// <summary>
    /// The post campaign create workflow activity
    /// </summary>
    public sealed class PostCampaignCreateActivity : CodeActivity
    {
        /// <summary>
        /// Executes the workflow activity.
        /// </summary>
        /// <param name="executionContext">The execution context.</param>
        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the tracing service
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();

            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }

            tracingService.Trace(
                "Entered PostCampaignCreateActivity.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId,
                executionContext.WorkflowInstanceId);

            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            tracingService.Trace(
                "PostCampaignCreateActivity.Execute(), Correlation Id: {0}, Initiating User: {1}",
                context.CorrelationId,
                context.InitiatingUserId);

            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService orgService = serviceFactory.CreateOrganizationService(context.UserId);
           
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    var campaignEntity = (Entity)context.InputParameters["Target"];
                    if (campaignEntity != null)
                    {
                        // your logic comes here
   }
}
    }
}

The above workflow activity is for the post create of a campaign record as can be inferred from its name (the first highlight). In the second highlight I have shown how we will obtain the organization service from the service factory that we obtain from the context. Once we have these properties we can start with all the messages that the Dynamics CRM platform gives us!

Also, for those who are not aware. After registering the workflow assembly which contains all the custom workflow activity classes (like above) using the plugin registration tool (or the CRM developer toolkit), we still need to configure an out of the box workflow.
The conditions when this OOTB workflow will fire will act as the trigger for this custom workflow activity. This is required as unlike plugins we do not have steps or filtering attributes that can be specified while registering the workflow activity. Also note that images are not supported by workflows, so any requirement having to do with previous values in updates, need to be achieved through plugins.

Let me know your comments in the comments section. Happy Dynamics CRMing!!




Friday, 12 August 2016

Check whether a date field has valid data in MS CRM

The date type unlike other object types is not equal to null when it is not populated in C#.
Say, you are writing a plugin (in Dynamics CRM) and you expect the date field to be filled and do some operation on it (like add some days) and then use this value as attribute value in some other entity during create.

The problem is that the minimum value of date field gets populated when there is no date value in the date variable. The default minimum date in .NET is 1/1/0001 while CRM accepts a minimum date equal to 1/1/1900. So when we try to create the new record with a non-existent date value from the other entity, an exception is thrown by the platform stating the above fact.

So the way to check whether the date field contains a valid value in your C# code is:

// value is not provided in the email
if (emailStartDate == DateTime.MinValue)
{
  // do something
}
else
{
 // valid value!!
}

Hope this helps!
Happy Dynamics CRMing...

How to populate email record details from email template using JavaScript in Dynamics CRM

Recently, I had the requirement to open the email form on the click of a custom button on the opportunity form. The email form that opened needed to be pre-populated with the subject and body from an email template. There were some other values in the email record that needed to be populated from the opportunity record from which the email form was being opened. There was the option to make the SOAP request InstantiateTemplateRequest. 
However, as I had to embed the current opportunity record URL in the email message body (it cannot be added in the email template), along with some other values from the opportunity record in the email body, so I could not use the SOAP request. 

I decided to use JavaScript to populate the values in the email body (using my own placeholders).

Here is the email template:

Subject: This email with regards to the opportunity %OPPORTUNITY_TITLE%
Body:
Please click %OPPORTUNITY_URL% to view the opportunity.
Thanks,
Alpha business team


Here is the logic on click of the custom button on the opportunity form that opens the email form by reading the values from the above email template, replacing the placeholder values and opening the email form.

// get the email template details for the email template (Template): Body, Description, Subject, Title
    var type = "Template";
    var options = "$select=Body,Description,Subject,Title&$filter=Title eq '" + EmailTemplateTitle + "'";
    SDK.REST.retrieveMultipleRecords(type, options, function(successResponse) {
        if (successResponse != null && successResponse.length > 0) {
            var emailTemplateResponse = successResponse[0];
            if (emailTemplateResponse) {
                var emailBody = emailTemplateResponse.Body;
                var emailDesc = emailTemplateResponse.Description;
                var emailSubject = emailTemplateResponse.Subject;
                var emailTitle = emailTemplateResponse.Title;                      

                // populate the deal ID in the subject
                emailSubject = common.getCDataValue(emailSubject);              
                var dealReferenceAttr = Xrm.Page.getAttribute("new_dealnumber");
                if (dealReferenceAttr) {
                    var dealReferenceValue = dealReferenceAttr.getValue();
                    if (dealReferenceValue) {
                        emailSubject = emailSubject.replace("%OPPORTUNITY_TITLE%", dealReferenceValue);
                    } else {
                        emailSubject = emailSubject.replace("%OPPORTUNITY_TITLE%", "");
                    }
                }
             
                // populate the values in the deal body
                emailBody = common.getCDataValue(emailBody);                            
                var presentDealRecordId = Xrm.Page.data.entity.getId();
                var dealRecordUrl = "";
                if (presentDealRecordId) {
                    // remove the preceeding and trailing {} from the record ID
                    presentDealRecordId = presentDealRecordId.replace("{", "");
                    presentDealRecordId = presentDealRecordId.replace("}", "");
                    // returns value in this format: http(s)://server/org
                    var baseUrl = Xrm.Page.context.getClientUrl();
                    dealRecordUrl = baseUrl + "/main.aspx?etn=opportunity&pagetype=entityrecord&id=%7b" + presentDealRecordId + "%7d";
                    console.log("Deal record URL: " + dealRecordUrl);
                }
                emailBody = emailBody.replace("%OPPORTUNITY_URL%", "<a href =\"" + dealRecordUrl + "\"> Click here</a>");

                // populate the email record
                var parameters = {};
                console.log("The email subject is: " + emailSubject);
                parameters["subject"] = emailSubject;
                console.log("The email body is: " + emailBody);
                sessionStorage.setItem("dealEmailBody", emailBody);

                var dealLeaderAttr = Xrm.Page.getAttribute("ownerid");
                if (dealLeaderAttr) {
                    var dealLeader = dealLeaderAttr.getValue();
                    if (dealLeader) {
                        parameters["parameter_dealleaderid"] = dealLeader[0].id;
                        parameters["parameter_dealleadername"] = dealLeader[0].name;
                        parameters["parameter_dealleadertype"] = dealLeader[0].entityType;
                    }
                }
             
                parameters["parameter_regardingid"] = presentDealRecordId;
                parameters["parameter_regardingname"] = Xrm.Page.getAttribute("name").getValue();
                parameters["parameter_regardingtype"] = "opportunity";

                // open the email entity form
                Xrm.Utility.openEntityForm("email", null, parameters);
            }
        } else {
            console.log("No response received");
        }
        }, SDK.REST.errorCallback,
        function() { console.log("Retrieve Multiple call completed"); },
        );




I have highlighted the interesting parts in the code above. The first highlight indicates where you put the title of the email template to fetch the details.
The second is the portion where we extract the email subject/body from the XML (like) manner in which it is stored in the email template. The method definition is below where I get the value from within the CDATA section in the XML.

common.getCDataValue = function(text) {
    if (text) {
        var cDataStr = "CDATA[";
        var cDataStart = text.indexOf(cDataStr);
        if (cDataStart >= 0) {
            var cDataValue = text.substring(cDataStart + cDataStr.length);
            if (cDataValue) {
                var indexOfClosingBracket = cDataValue.indexOf("]]");
                if (indexOfClosingBracket >= 0) {
                    cDataValue = cDataValue.substring(0, indexOfClosingBracket);
                    return cDataValue;
                }
            }
        }
    }

    return null;
}



The third part shows storing the email body in HTML session storage. This is how the value is transmitted to the main email form. The other way to store as a form parameter has not been used as the email body can be rather large.

The fourth item shows how the values are stored in parameters to be passed to the email form (which has these parameters defined)

The below shows snippet from a method bound on the load of the email form that gathers the values from the parameters/sessionStorage and populates them in the email form:

// Get the Value of the Regarding through the Customer Parameters
            var param = Xrm.Page.context.getQueryStringParameters();
var dealLeaderId = param["parameter_dealleaderid"];
                var dealLeaderType = param["parameter_dealleadertype"];
                var dealLeaderName = param["parameter_dealleadername"];
                if (dealLeaderId && dealLeaderType && dealLeaderName) {
                    Xrm.Page.getAttribute("to").setValue([{ id: dealLeaderId, name: dealLeaderName, entityType: dealLeaderType }]);
                }

var dealNotificationEmailMessage = sessionStorage.getItem("dealEmailBody");
                if (dealNotificationEmailMessage) {
                    Xrm.Page.getAttribute("description").setValue(dealNotificationEmailMessage);
}


Hence, you get the email form popped out with all the relevant details from the email template and opportunity form!


Let me know your thoughts by commenting below. Happy Dynamics!!

Wednesday, 10 August 2016

SSRS report in Dynamics CRM: Including required and optional attendees for appointment

Hi guys,
Here is another article for today where I want to highlight my learnings while creating an SSRS report in Dynamics CRM on the appointment entity. In particular, I want to show how we can fetch the required and optional attendees, those who are mentioned in the appointment record but are not readily obtainable from the underlying appointment table in SQL.

The report required the list of these attendees in a comma separated format as shown below:






The main issue was that required attendees and optional attendees are not directly present in the appointment table. The attribute type says it is a party list type. The fields are unique in that these lookups allow multi item selection and that too records of different types (users/contacts/companies, etc.). A bit of internet search and some figuring out later I understood that the required details are present in the activity party table. Each row in activity party has the corresponding activity id and details like the participation type mask, which indicates whether the party is required/optional (it has values for other types of parties as well like organizer, etc.) and the party object type code, which indicates the type of party (whether it is user/contact/account, etc.). The partyid would eventually be the ID of the contact/user record that is part of the activity. We need to obtain the contact full name from the contact table using this ID as the contact ID. Here is the query that I had used to get all the contacts in the required attendee list:

declare @requiredList nvarchar(max);

-- partyobjecttypecode 2 denotes contact and participationtypemask 5 denotes required attendee
 select @requiredList = coalesce(@requiredList + ', ', '') + fullname from filteredcontact where contactid in
(
select partyid from filteredactivityparty        
where partyobjecttypecode = 2
and participationtypemask = 5
and activityid = @appointmentId
)

The coalesce function aggregates the full name of the contacts and separates them with a comma.
In a similar manner, I aggregated the full name of all the required users, optional contacts and optional users. 

The whole SQL query used is pasted below:

declare @appointmentId nvarchar(40); 

-- Obtain the ID for the present appointment record in Dynamics CRM using the below code
select @appointmentId = activityid from filteredappointment as CRMAF_filteredappointment;

declare @requiredList nvarchar(max);

-- partyobjecttypecode 2 denotes contact and participationtypemask 5 denotes required attendee
select @requiredList = coalesce(@requiredList + ', ', '') + fullname from filteredcontact where contactid in
(
select partyid from filteredactivityparty        
where partyobjecttypecode = 2
and participationtypemask = 5
and activityid = @appointmentId
)

-- partyobjecttypecode 8 denotes user and participationtypemask 5 denotes required attendee
select @requiredList = coalesce(@requiredList + ', ', '') + fullname from FilteredSystemUser where systemuserid in
(
select partyid from filteredactivityparty
where partyobjecttypecode = 8
and participationtypemask = 5
and activityid = @appointmentId
)

declare @optionalList nvarchar(max);

-- partyobjecttypecode 2 denotes contact and participationtypemask 6 denotes optional attendee
select @optionalList = coalesce(@optionalList + ', ', '') + fullname from filteredcontact where contactid in
(
select partyid from filteredactivityparty
where partyobjecttypecode = 2
and participationtypemask = 6
and activityid = @appointmentId
)

-- partyobjecttypecode 8 denotes user and participationtypemask 6 denotes optional attendee
select @optionalList = coalesce(@optionalList + ', ', '') + fullname from FilteredSystemUser where systemuserid in
(
select partyid from filteredactivityparty
where partyobjecttypecode = 8
and participationtypemask = 6
and activityid = @appointmentId
)


select @requiredList as [Required attendees], @optionalList as [Optional attendees], ownerid, actualstart, scheduledstart, subject, location, description from FilteredAppointment
where activityid = @appointmentId

The last select query uses the results of the previous operations to return the required details for the report (for the present appointment record). They are then mapped to the fields in the SSRS reports which are then mapped to the report UI.


I would love to hear from you if you have a better way of achieving the above functionality. Thanks and happy SSRS reporting!!

Writing a plugin on post opportunity win in Dynamics CRM

Hi guys,
Here is a new post that tries to highlight how to write a Dynamics CRM plugin on win of an opportunity. Yes, I had some issues while developing the plugin and I want to share my learnings so that you know better.

The scenario that this plugin looks to solve is not important as much as to understand how to get the relevant details (read: opportunity details for the opportunity that was won) from the plugin is.

So the code:

public class PostDealWin: Plugin
    {     

        public PostDealWin()
            : base(typeof(PostDealWin))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Win", "opportunity", new Action<LocalPluginContext>(ExecutePostDealWin)));           
        }

        protected void ExecutePostDealWin(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");
            }

            var tracingService = localContext.TracingService;
            var orgService = localContext.OrganizationService;
            var context = localContext.PluginExecutionContext;
            if (context.InputParameters.Contains("OpportunityClose") && context.InputParameters["OpportunityClose"] is Entity)
            {
                var opportunityCloseEntity = (Entity)context.InputParameters["OpportunityClose"];

                if (opportunityCloseEntity != null && opportunityCloseEntity.Attributes.Contains("opportunityid") &&
                    opportunityCloseEntity.Attributes["opportunityid"] != null)
                {
                    var dealEntityRef = (EntityReference) opportunityCloseEntity.Attributes["opportunityid"];
                    if (dealEntityRef != null)
                    {                                               
                            try
                            {
                                // your magic comes here
                            }
                            catch (Exception ex)
                            {
                                tracingService.Trace("An exception occurred while trying to execute the post deal win plugin. Details: " + ex.Message);
                                throw;
                            }                       
                    }                   
                }
            }
        }      
    }       



The lines highlighted above are of interest since these are the places where the code differs from the typical post update plugin.
So behind the hoods when the opportunity is won there is a “OpportunityClose” entity (it is an activity entity - refer here: https://msdn.microsoft.com/en-us/library/gg334301.aspx) that is passed to the plugin. It has a reference to the original opportunity that is available in the opportunityid field. I am storing the opportunity reference entity in the local variable dealEntityRef for use later. I then perform a retrieve to get some details related to the opportunity entity using the ID from this opportunity reference.

I have put the comment where you would want to do all your magic after the opportunity is won!

I hope this helps someone! Happy Dynamics CRMing!




Generic method to compare values: less code to achieve more!

I recently had a requirement to verify whether a value has updated by comparing with the present value and the earlier value. In the context of Dynamics CRM, I needed to compare the value of an attribute in the target entity with the value of the same attribute from the preimage entity in a post update plugin on a custom entity.
I had to write a method which will compare the values of two object types and return true or false. However, the caveat is that these values are reference types (and I assumed they do not have any IComparer implementation and are sealed) and so the comparisons for each type will be different (need to use different properties). The obvious way was to write a method for each type, which would be difficult to maintain as there needs to be a method for each of the object types supported by the Dynamics CRM SDK. So I used an alternate approach where I wrote a single method (with the help of C# generics) that would cater to all the different object types.
Here goes the method:


private bool IsValueUpdated <T>(T presentValue, T oldValue, Func<T, T, bool> comparer) where T: class
        {
            if (presentValue == null)
            {
                // if the present value is null, it means it was not updated in the update plugin
                return false;
            }

            if (oldValue == null)
            {
                // if the old value is null, it means the old value was not populated but there is a new value (applicable during creation)
                return true;
            }

            return comparer(presentValue, oldValue);
        }


The above comments might be Dynamics CRM specific (but the idea is to help you understand the approach used and can be applicable to any .NET scenario as well).
The method is mostly easy to understand. However, I would like to point out the third parameter in the method the “comparer” Func. Funcs are a feature of C# that help us to encapsulate methods (a delegate type). We can pass our custom comparer method in the Func using a lambda expression as follows:

  var isTypeUpdated = this.IsValueUpdated(typePresentValue, typeOldValue,
                        (presentValue, oldValue) => presentValue.Value != oldValue.Value);


                    var isCompanyUpdated = this.IsValueUpdated(companyPresentValue, companyOldValue,
                        (presentValue, oldValue) => presentValue.Id != oldValue.Id);


The highlighted portion shows 2 different method invocations where we are passing the lambda expression for the comparer. If you note carefully, the types are automatically inferred by the C# compiler, so we do not need to explicitly state that the first invocation of the generic method is for the OptionsetValue type, while the second is for the EntityReference type (Dynamics CRM object types of interest for my case).
We can also create Func variables and store the lambda expressions as local variables which we can pass in case we require another method which has the same comparer implementation or we do not want to pass the lambdas during the method invocation.


Please let me know your views on the above approach and you are most welcome to share your ideas as well. Thanks in advance and have a great time with C#/Dynamics CRM!