Monday 23 June 2014

Dynamics CRM: How to get Business Closure Dates in Plugin


using System;
using System.Linq;
using HayerCrmPackage.Plugins.Entities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace HayerCrmPackage.Plugins
{
    public class PreCaseCreate : Plugin
    {
        public PreCaseCreate()
            : base(typeof(PreCaseCreate))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "incident", new Action<LocalPluginContext>(ExecutePreCaseCreate)));            
        }

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

            var pluginContext = localContext.PluginExecutionContext;
            var service = localContext.OrganizationService;
            var context = new OrganizationServiceContext(service);

            // Get Organization Business Closure Calendar Id
            var organization = service.Retrieve("organization", context.OrganizationId, new ColumnSet("businessclosurecalendarid"));

            var query = new QueryExpression("calendar")
            {
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression()
            };

            // Add condition to get Get Calander where CalanderId is equal to Organization's businessclosurecalendarid
            query.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.Equal, organization["businessclosurecalendarid"].ToString()));

            // Get Calendar
            var businessClosureCalendar = service.RetrieveMultiple(query).Entities[0];

            // Get the Calendar rules
            IEnumerable<Entity> calanderRules = businessClosureCalendar != null ? businessClosureCalendar.GetAttributeValue<EntityCollection>("calendarrules").Entities : null;
        }
    }
}



To know how to add working days by calculating business closure dates and weekends, please check my blog Here.

Happy Coding

P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)

Please check my other (non-CRM) blog here: Programming Blogs

Friday 20 June 2014

Dynamics CRM: How to Add working days by calculating business closure and weekends


Recently I received a requirement to set the Incident Auto Closure date to 'CreatedOn' + 7 working days. To add working days to 'CreatedOn' date, we need to exclude all the weekend and business closures. I came up with the following solution to achieve the required outcome.
Add the following Class to your project to calculate working day and business closures.

using System;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

namespace HayerCrmPackage.Plugins
{
    public class AddWorkingDaysClass
    {
        /// 
        /// Add working days to a specific date by calculating Business Closure and weekends
        /// 
        public static DateTime AddWorkingAndBusinessClosureDays(DateTime specificDate, int workingDaysToAdd, 
            IOrganizationService service, IPluginExecutionContext context)
        {
            var currentDate = specificDate;
            var businessClosures = GetBusinessClosureCalendarRules(context, service);

            // Calculate the working days by taking out the weekends
            int completeWeeks = workingDaysToAdd / 5;
            DateTime date = specificDate.AddDays(completeWeeks * 7);
            workingDaysToAdd = workingDaysToAdd % 5;
            for (int i = 0; i < workingDaysToAdd; i++)
            {
                date = date.AddDays(1);
                while (!IsWorkingDayOfWeek(date))
                {
                    date = date.AddDays(1);
                }
            }

            // Calculate the working days by taking out Business Closures
            for (var i = currentDate; i <= date; i = i.AddDays(1))
            {
                if (i.DayOfWeek == DayOfWeek.Saturday || i.DayOfWeek == DayOfWeek.Sunday)
                    continue;

                foreach (var closure in businessClosures)
                {
                    var startDate = (DateTime)closure["effectiveintervalstart"];
                    var endDate = (DateTime)closure["effectiveintervalend"];
                    var range = new DateRange(startDate, endDate);

                    if (range.Includes(i))
                    {
                        date = date.AddDays(1);

                        if (date.DayOfWeek == DayOfWeek.Saturday)
                        {
                            date = date.AddDays(2);
                            i = i.AddDays(1);
                        }
                        else if (date.DayOfWeek == DayOfWeek.Sunday)
                        {
                            date = date.AddDays(1);
                        }
                    }
                }
            }
            return date;
        }

        /// 
        /// Get Business Closure Calendar Rules
        /// 
        private static IEnumerable<Entity> GetBusinessClosureCalendarRules(IPluginExecutionContext context,
            IOrganizationService service)
        {
            // Get Organization Business Closure Calendar Id
            var organization = service.Retrieve("organization", context.OrganizationId, new ColumnSet("businessclosurecalendarid"));

            var query = new QueryExpression("calendar")
            {
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression()
            };

            // Add condition to get Get Calander where CalanderId is equal to Organization's businessclosurecalendarid
            query.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.Equal, organization["businessclosurecalendarid"].ToString()));

            // Get Calendar
            var businessClosureCalendar = service.RetrieveMultiple(query).Entities[0];

            // Return the Calendar rules
            return businessClosureCalendar != null ? businessClosureCalendar.GetAttributeValue<EntityCollection>("calendarrules").Entities : null;
        }

        private static bool IsWorkingDayOfWeek(DateTime date)
        {
            var day = date.DayOfWeek;
            return day != DayOfWeek.Saturday && day != DayOfWeek.Sunday;
        }

        public interface IRange<T>
        {
            T Start { get; }
            T End { get; }
            bool Includes(T value);
        }

        public class DateRange : IRange<DateTime>
        {
            public DateRange(DateTime start, DateTime end)
            {
                Start = start;
                End = end;
            }

            public DateTime Start { get; private set; }
            public DateTime End { get; private set; }

            public bool Includes(DateTime value)
            {
                return (Start <= value) && (value < End);
            }
        }
    }
}



To consume above class in your plugin, use the following code:
using System;
using System.Linq;
using HayerCrmPackage.Plugins.Entities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace HayerCrmPackage.Plugins
{
    public class PreCaseCreate : Plugin
    {
        public PreCaseCreate()
            : base(typeof(PreCaseCreate))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "incident", new Action<LocalPluginContext>(ExecutePreCaseCreate)));            
        }

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

            var pluginContext = localContext.PluginExecutionContext;
            var service = localContext.OrganizationService;
            var context = new OrganizationServiceContext(service);

            var targetEntity = (pluginContext.InputParameters != null && pluginContext.InputParameters.Contains("Target"))
            ? (Entity)pluginContext.InputParameters["Target"]
            : null;

            // Converting Entity to Incident
            var incident = (targetEntity != null && targetEntity.LogicalName == Incident.EntityLogicalName) ? targetEntity.ToEntity<Incident>() : null;
            
            // If you want to update the number of working days to add, change the value of 2nd parameter below
            incident.new_AutoClosureDate = AddWorkingDaysClass.AddWorkingAndBusinessClosureDays(DateTime.Now, 7, service,
                        pluginContext);
        }
    }
}

Happy Coding

P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)

Ref:
 1. Adding Working Week Days to a Specific Date
 2. Check if Date Time is in Date Range
Please check my other (non-CRM) blog here: Programming Blogs

Thursday 19 June 2014

CRM 2013: Timer button is currently disabled

Service pack 1 of Dynamics CRM 2013 came up with an exciting feature of SLA Timer. Timers can be used to track service level agreement KPI to ensure customer commitments are met. Today I was trying to get my hands on it but after installing Service Pack 1, I discovered that Timer option was greyed out on form editor. It was saying:

"This button is currently disabled".

"You may not have selected the item that works with this feature. If you don not have permissions to use this feature, correct your system."


 Solution: 

Go to Settings > Administration > Install Product Updates

You will get the following screen.

Install the updated by clicking on 'Update' at the bottom of the screen.

it will install the updated and in the mean time you can watch the video on installation screen to know more about this brilliant feature. Once updated you will get the confirmation screen. 

Open case form in form editor. You will see that 'Timer' button is enabled not and it is ready to use. 



P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)
Please check my other (non-CRM) blog here: Programming Blogs