Dynamics 365





Monday, May 28, 2018

MS CRM get break duration time from business calendar


innerCalendar entitycollection  will have 2 entities
1 is business hours 
2nd one is break 

//with entity[1].attributes["duration"] will get break duration.


//temp code
                Entity BCalendar = GetCalendar(calendarGuid);

                EntityCollection calendarRules = BCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules);
                var firstRulePattern = calendarRules[0].GetAttributeValue<string>(CalendarColumnNames.Pattern);

                Guid innerCalendarId = calendarRules[0].GetAttributeValue<EntityReference>(CalendarColumnNames.InnerCalendarId).Id;
                Entity innerCalendar = service.Retrieve(CalendarColumnNames.EntityName, innerCalendarId, new ColumnSet(true));
                EntityCollection innnerCalendarRule = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules);
                var duration = 0;
                var offset = 0;
                var BreakDuration = 0;
                BreakDuration = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules).Entities[1].GetAttributeValue<int>(CalendarColumnNames.Duration);
                duration = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules).Entities[0].GetAttributeValue<int>(CalendarColumnNames.Duration);
                offset = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules).Entities[0].GetAttributeValue<int>(CalendarColumnNames.Offset);
                // temp code

Calculate business hours using Business calendar - complex methods


using System;
using Microsoft.Xrm.Sdk;
using Vrp.Crm.Base.Handler;
using Vrp.Crm.Base.Tracing;
using Vrp.Crm.Base.Logging;
using dyn.Common.Handler;
using dyn.CaseManagement.Common;
using dyn.Base.Scheduling;
using dyn.CaseManagement.Business.Objects;
using PluginStage = dyn.Base.Handler.PluginStage;
using Microsoft.Crm.Sdk.Messages;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk.Query;

namespace dyn.CaseManagement
{
    public class IncidentResolutionsProcessorPluginExecutor : PluginExecutor
    {
        public IncidentResolutionsProcessorPluginExecutor(IServiceProvider serviceProvider, IPluginExecutionContext pluginExecutionContext, ITracingService crmTracing, string userId, IOrganizationService service, Tracer tracer)
            : base(serviceProvider, pluginExecutionContext, crmTracing, userId, service, tracer)
        {
        }

        public override void Execute()
        {
            try
            {
                var context = pluginExecutionContext;
                tracer.AddInfo(string.Format("{0} - {1}", CaseManagementConstants.Module, context.MessageName));

                Entity targetEntity = PluginHelper.GetEntityFromContext(context);

                if (context.Stage == PluginStage.PreOperation)
                {
                    if (targetEntity == null)
                    {
                        return;
                    }

                    CaseHandler caseHandler = new CaseHandler(service, userId, CaseManagementConstants.Module);

                    Guid incidentId = Guid.Empty;

                    if (targetEntity.Attributes.Contains(IncidentResolutionEntityAttributeNames.RegardingIncidentIdFieldName))
                    {
                        EntityReference entityReference = targetEntity.Attributes[IncidentResolutionEntityAttributeNames.RegardingIncidentIdFieldName] as EntityReference;
                        if (entityReference != null && entityReference.LogicalName == IncidentEntityAttributeNames.EntityName)
                        {
                            incidentId = entityReference.Id;
                        }
                    }

                    if (incidentId != Guid.Empty)
                    {
                        Entity caseEntity = new Entity
                        {
                            Id = incidentId,
                            LogicalName = IncidentEntityAttributeNames.EntityName
                        };

                        if (caseEntity != null)
                        {
                            //get Case Configuration Case resolution Duration 24x7 or business hours

                            bool isDurationTypeBusinessHours =  GetCaseProcessConfigResolutionDuration(caseHandler, caseEntity.Id.ToString());
                         
                         
                            //Validate
                            if (ValidateCaseResolution(caseHandler, incidentId.ToString()))
                            {
                                string subject = String.Empty;
                                string description = String.Empty;

                                //Get resolution Subject
                                if (
                                    targetEntity.Attributes.Contains(
                                        IncidentResolutionEntityAttributeNames.SubjectFieldName))
                                {
                                    subject =
                                        targetEntity.Attributes[IncidentResolutionEntityAttributeNames.SubjectFieldName]
                                            .ToString();
                                }

                                //Get resolution Description
                                if (targetEntity.Attributes.Contains(
                                    IncidentResolutionEntityAttributeNames.DescriptionFieldName)
                                    &&
                                    targetEntity.Attributes[IncidentResolutionEntityAttributeNames.DescriptionFieldName] !=
                                    null)
                                {
                                    description =
                                        targetEntity.Attributes[
                                            IncidentResolutionEntityAttributeNames.DescriptionFieldName].ToString();
                                }

                                //Set Resolution Subject on Case Entity
                                if (
                                    caseEntity.Attributes.Contains(
                                        IncidentEntityAttributeNames.ResolutionSubjectFieldName))
                                {
                                    caseEntity.Attributes.Remove(IncidentEntityAttributeNames.ResolutionSubjectFieldName);
                                }

                                //Subject is mandatory field so no need to check for empty or null
                                caseEntity.Attributes.Add(IncidentEntityAttributeNames.ResolutionSubjectFieldName,
                                    subject);

                                //Set Resolution description on Case Entity
                                if (
                                    caseEntity.Attributes.Contains(
                                        IncidentEntityAttributeNames.ResolutionDescriptionFieldName))
                                {
                                    caseEntity.Attributes.Remove(
                                        IncidentEntityAttributeNames.ResolutionDescriptionFieldName);
                                }

                                caseEntity.Attributes.Add(IncidentEntityAttributeNames.ResolutionDescriptionFieldName,
                                    description);

                                if (
                                    caseEntity.Attributes.Contains(
                                        IncidentEntityAttributeNames.ActualResolutionTimeFieldName))
                                {
                                    caseEntity.Attributes.Remove(
                                        IncidentEntityAttributeNames.ActualResolutionTimeFieldName);
                                }

                                caseEntity.Attributes.Add(IncidentEntityAttributeNames.ActualResolutionTimeFieldName,
                                    DateTime.Now);
                                //var resolutionDuration =CrmTypesHelper.GetIntValue(targetEntity, IncidentResolutionEntityAttributeNames.TimeSpentFieldName);
                                //caseEntity.Attributes.Add(IncidentEntityAttributeNames.ResolutionDurationFieldName, resolutionDuration + " Minutes");
                                TimeSpan ts;
                                if (isDurationTypeBusinessHours)
                                {
                                    ts = CalculateCaseresolutionDuration(caseHandler, incidentId.ToString());

                                }
                                else
                                {
                                    ts = DateTime.UtcNow - GetCaseCreatedOnDateTime(caseHandler, incidentId.ToString());
                                }
                                var resolutionDuration = string.Format("{0:%d} days, {0:%h} hours, {0:%m} minutes, {0:%s} seconds", ts);

                                caseEntity.Attributes.Add(IncidentEntityAttributeNames.ResolutionDurationFieldName, resolutionDuration);

                                caseHandler.Update(caseEntity);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                ExceptionHelper.LogException(e, userId, CaseManagementConstants.Module, "Error occured", service);
                tracer.AddError("Error occured in Case Management, Change Category Task Generator Plugin" + e.Message);
                throw;
            }
        }

        public DateTime GetCaseCreatedOnDateTime(CaseHandler caseHandler, string incidentId)
        {
            var caseEntity = caseHandler.GetCaseById(incidentId, new[]
            {
                IncidentEntityAttributeNames.CreatedOnFieldName
            });

            var result = CrmTypesHelper.GetDateTimeValue(caseEntity, IncidentEntityAttributeNames.CreatedOnFieldName);

            return result;
        }
        public bool GetCaseProcessConfigResolutionDuration(CaseHandler caseHandler, string incidentId)
        {
            TimeManager TimeManager = new TimeManager(service, tracer, userId, CaseManagementConstants.Module);
            bool result = false;
            try
            {
                var caseEntity = caseHandler.GetCaseById(incidentId, new[]
                {
                IncidentEntityAttributeNames.ProcessConfigurationIdFieldName,IncidentEntityAttributeNames.OwnerIdFieldName,IncidentEntityAttributeNames.Entitlement
            });

                var ConfigID = CrmTypesHelper.GetEntityReferenceId(caseEntity, IncidentEntityAttributeNames.ProcessConfigurationIdFieldName);
               
               
                ProcessTaskConfigurationHandler caseProcessHandler = new ProcessTaskConfigurationHandler(service, userId, CaseManagementConstants.Module);

                Entity caseProcessConfig = caseProcessHandler.GetCaseProcessConfigurationById(ConfigID);
                if (caseProcessConfig.Attributes.Contains(CaseProcessConfigurationEntityAttributeNames.CaseResolutionDuration))
                {
                    if (((OptionSetValue)caseProcessConfig[CaseProcessConfigurationEntityAttributeNames.CaseResolutionDuration]).Value == 2)
                    {
                        result = true; // Business Hours
                    }
                }
               
            }
            catch (Exception e)
            {
                ExceptionHelper.LogException(e, userId, CaseManagementConstants.Module, "Error occured", service);
                tracer.AddError("Error occured in Case Management, GetCaseProcessConfigResolutionDuration:" + e.Message);
                throw;
            }
            return result;
        }
        public TimeSpan CalculateCaseresolutionDuration(CaseHandler caseHandler, string incidentId)
        {
            TimeSpan BusinessHoursDuration;
            var workingDays = new List<DayOfWeek>();
            try
            {
                TimeManager TimeManager = new TimeManager(service, tracer, userId, CaseManagementConstants.Module);
                var caseEntity = caseHandler.GetCaseById(incidentId, new[]
                {
                IncidentEntityAttributeNames.ProcessConfigurationIdFieldName,IncidentEntityAttributeNames.OwnerIdFieldName,IncidentEntityAttributeNames.Entitlement
            });

                var ConfigID = CrmTypesHelper.GetEntityReferenceId(caseEntity, IncidentEntityAttributeNames.ProcessConfigurationIdFieldName);
                var OwnerID = CrmTypesHelper.GetEntityReferenceId(caseEntity, IncidentEntityAttributeNames.OwnerIdFieldName);
                Guid OwnerGuid = new Guid(OwnerID);
                Guid ConfigGUID = new Guid(ConfigID);

                SLAConfigObject slaConfig = new SLAConfigObject(service, userId, CaseManagementConstants.Module, tracer);

                SLAObject sla = slaConfig.GetCaseProcessConfigurationSLAbyConfigId(ConfigGUID, Guid.Empty, OwnerGuid);

                var entitlementGuid = CrmTypesHelper.GetEntityReferenceId(caseEntity,
                       IncidentEntityAttributeNames.Entitlement);

                Guid calendarGuid = TimeManager.GetCalendarFromSLA(sla.SLAId);

                Entity BCalendar = GetCalendar(calendarGuid);

                EntityCollection calendarRules = BCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules);
                var firstRulePattern = calendarRules[0].GetAttributeValue<string>(CalendarColumnNames.Pattern);

                Guid innerCalendarId = calendarRules[0].GetAttributeValue<EntityReference>(CalendarColumnNames.InnerCalendarId).Id;
                Entity innerCalendar = service.Retrieve(CalendarColumnNames.EntityName, innerCalendarId, new ColumnSet(true));
                EntityCollection innnerCalendarRule = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules);
                var duration = 0;
                var offset = 0;

                duration = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules).Entities[0].GetAttributeValue<int>(CalendarColumnNames.Duration);
                offset = innerCalendar.GetAttributeValue<EntityCollection>(CalendarColumnNames.CalendarRules).Entities[0].GetAttributeValue<int>(CalendarColumnNames.Offset);

                DateTime CaseCreateTime = GetCaseCreatedOnDateTime(caseHandler, incidentId.ToString());
                var ResolutionDate = DateTime.UtcNow;
                List<TimeSlot> timeSlots = TimeManager.GetAvailableTimeSlotsForCalendar(calendarGuid, CaseCreateTime, DateTime.UtcNow);
                double totalSpanSeconds = 0;
                foreach (var slot in timeSlots)
                {
                    double slotSpan = 0;
                    if (CaseCreateTime > slot.End)
                        slotSpan = 0;
                    else if (CaseCreateTime > slot.Start && ResolutionDate < slot.End)
                        slotSpan = (ResolutionDate - CaseCreateTime).TotalSeconds;
                    else if (CaseCreateTime > slot.Start && CaseCreateTime < slot.End && ResolutionDate > slot.End)
                        slotSpan = (slot.End - CaseCreateTime).TotalSeconds;
                    else if (ResolutionDate > slot.Start && ResolutionDate < slot.End)
                        slotSpan = (ResolutionDate - slot.Start).TotalSeconds;
                    else
                        slotSpan = (slot.End - slot.Start).TotalSeconds;

                    totalSpanSeconds += slotSpan;
                }

                TimeSpan TotalDuration = DateTime.UtcNow - CaseCreateTime;

                workingDays = GetPatternDays(firstRulePattern);

                List<Day> workingHours = BuildWorkingHoursFromPattern(workingDays, offset, offset + duration);

                var timeBetween = new Duration(CaseCreateTime, DateTime.UtcNow);
                timeBetween.RemoveNonWorkingMinutes(workingHours);
                double responseTime = Math.Round(TimeSpan.FromMinutes(timeBetween.GetTotalMinutes()).TotalHours, 2);

                BusinessHoursDuration = new TimeSpan(0, 0, Convert.ToInt32(totalSpanSeconds));
                var resolutionDuration = string.Format("{0:%d} days, {0:%h} hours, {0:%m} minutes, {0:%s} seconds", BusinessHoursDuration);
               
            }

            catch (Exception e)
            {
                ExceptionHelper.LogException(e, userId, CaseManagementConstants.Module, "Error occured", service);
                tracer.AddError("Error occured in Case Management, CalculateCaseresolutionDuration:" + e.Message);
                throw;
            }
            return BusinessHoursDuration;
        }
        private Entity GetCalendar(Guid calendarId)
        {
            ColumnSet cols = new ColumnSet(true);

            return service.Retrieve(CalendarColumnNames.EntityName, calendarId, cols);
        }
        internal static List<DayOfWeek> GetPatternDays(string pattern)
        {
            var days = new List<DayOfWeek>();

            string split = Array.Find(pattern.Split(';'), s => s.Contains("BYDAY"));
            int index = split.IndexOf("BYDAY=");
            string[] daysStr = split.ToString().Remove(index, 6).Split(',');
           
            if (daysStr.Length > 0)
            {
                var dayMappings = BuildDayMapping();

                foreach (var day in daysStr)
                {
                    if (dayMappings.ContainsKey(day))
                        days.Add(dayMappings[day]);
                }
            }

            return days.Count > 0 ? days : null;
        }
        static Dictionary<string, DayOfWeek> BuildDayMapping()
        {
            Dictionary<string, DayOfWeek> dayMapping = new Dictionary<string, DayOfWeek>(7);
            dayMapping.Add("MO", DayOfWeek.Monday);
            dayMapping.Add("TU", DayOfWeek.Tuesday);
            dayMapping.Add("WE", DayOfWeek.Wednesday);
            dayMapping.Add("TH", DayOfWeek.Thursday);
            dayMapping.Add("FR", DayOfWeek.Friday);
            dayMapping.Add("SA", DayOfWeek.Saturday);
            dayMapping.Add("SU", DayOfWeek.Sunday);

            return dayMapping;
        }
        internal static List<Day> BuildWorkingHoursFromPattern(List<DayOfWeek> days, int start, int end)
        {
            var workingDays = new List<Day>(7);
            var weekDays = BuildWeekDays();

            foreach (var day in days)
                workingDays.Add(new Day(day, start, end));

            foreach (var weekDay in weekDays)
                if (!days.Contains(weekDay))
                    // Add an empty Day with no minutes.
                    workingDays.Add(new Day(weekDay));

            return workingDays;
        }
        static List<DayOfWeek> BuildWeekDays()
        {
            return new List<DayOfWeek>() {
            DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday,
            DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday };
        }
        public bool ValidateCaseResolution(CaseHandler caseHandler, string incidentId)
        {
            bool result = false;

            if (caseHandler != null)
            {
                var caseEntityCheck = caseHandler.GetCaseById(incidentId, new[]
                {
                    IncidentEntityAttributeNames.SubCaseTypeCodeFieldName,
                    IncidentEntityAttributeNames.CustomerSatisfactionFieldName,
                    IncidentEntityAttributeNames.RootCauseFieldName,
                    IncidentEntityAttributeNames.RootCauseDescriptionFieldName,
                    IncidentEntityAttributeNames.InFavorOfFieldName,
                    IncidentEntityAttributeNames.ResolutionSubjectFieldName
                   
                });

                var caseSubType = CrmTypesHelper.GetOptionSetValue(caseEntityCheck,
                    IncidentEntityAttributeNames.SubCaseTypeCodeFieldName);
           
                if (caseSubType != 1) // Not equal to Customer Information Update
                {
                    var resolutionSubject = CrmTypesHelper.GetStringValue(caseEntityCheck,
                        IncidentEntityAttributeNames.ResolutionSubjectFieldName);
                    if (resolutionSubject == CaseManagementConstants.SubjectAutoResolved)
                        return true;

                    var errMsg = "";
                    var satisfaction = CrmTypesHelper.GetOptionSetValue(caseEntityCheck,
                        IncidentEntityAttributeNames.CustomerSatisfactionFieldName);
                    if (satisfaction == 0)
                        errMsg = errMsg + @"Please select customer Satisfaction. \n";

                    var rootCause = CrmTypesHelper.GetEntityReferenceName(caseEntityCheck,
                        IncidentEntityAttributeNames.RootCauseFieldName);
                    if (string.IsNullOrWhiteSpace(rootCause))
                        errMsg = errMsg + @"Please select Root Cause. \n";

                    var rootCauseDesc = CrmTypesHelper.GetStringValue(caseEntityCheck,
                        IncidentEntityAttributeNames.RootCauseDescriptionFieldName);
                    if (string.IsNullOrWhiteSpace(rootCauseDesc))
                        errMsg = errMsg + @"Please select Root Cause Description. \n";

                    var inFavorOf = CrmTypesHelper.GetOptionSetValue(caseEntityCheck,
                        IncidentEntityAttributeNames.InFavorOfFieldName);
                    if (inFavorOf == 0)
                        errMsg = errMsg + @"Please select In Favor Of. \n";

                    if (!string.IsNullOrWhiteSpace(errMsg))
                        throw new InvalidPluginExecutionException(errMsg);
                    else
                    {
                        result = true;
                    }
                }
            }

            return result;
        }
    }
    //Murthy BEGIN - CALCULATE DUration
    sealed class CalendarColumnNames
    {
        public const string EntityName = "calendar";
        public const string HolidayScheduleCalendar = "holidayschedulecalendarid";
        public const string CalendarRules = "calendarrules";
        public const string CalendarId = "calendarid";
        public const string InnerCalendarId = "innercalendarid";
        public const string Pattern = "pattern";
        public const string Duration = "duration";
        public const string Offset = "offset";
        //
     
    }
    public sealed class Minute
    {
        public int Index;

        /// <summary>
        /// Default constructor for <see cref="Minute"/>.
        /// </summary>
        /// <param name="index">The minute's offset from midnight in minutes
        /// e.g. a <see cref="Index"/> of 60 would represent the minute at 01:00am for a <see cref="Day"/>.</param>
        public Minute(int index)
        {
            Index = index;
        }
    }
    public sealed class Day
    {
        public DayOfWeek DayOfWeek;
        public int DayOfMonth;
        public int Month;
        public int Year;
        public List<Minute> Minutes;
        public TimeSpan WorkingHours;

        public Day(DayOfWeek dayOfWeek)
        {
            DayOfWeek = dayOfWeek;
            Minutes = new List<Minute>();
        }
        public Day(DayOfWeek dayOfWeek, int start = 0, int end = 1440)
        {
            DayOfWeek = dayOfWeek;
            SetMinutes(start, end);
        }
        public Day(DateTime dateTime, int start = 0, int end = 1440)
        {
            DayOfWeek = dateTime.DayOfWeek;
            DayOfMonth = dateTime.Day;
            Month = dateTime.Month;
            Year = dateTime.Year;

            SetMinutes(start, end);
        }
        public Day(DayOfWeek dayOfWeek, int day, int month, int year)
        {
            DayOfWeek = dayOfWeek;
            DayOfMonth = day;
            Month = month;
            Year = year;
            SetMinutes();
        }

        /// <summary>
        /// Initialises the minutes in a day, either using an offset for start and end
        /// or setting all 1440 minutes (default).
        /// </summary>
        /// <param name="start">Offset from midnight in minutes.</param>
        /// <param name="end">Offset from midnight in minutes.</param>
        void SetMinutes(int start = 0, int end = 1440)
        {
            var minutes = new List<Minute>(1440);
            for (int i = start; i < end; i++)
                minutes.Add(new Minute(i));
            Minutes = minutes;
        }
        public bool IsSameDate(DateTime dt)
        {
            bool isSameDate = false;
            DateTime thisDay = new DateTime(this.Year, this.Month, this.DayOfMonth);

            if (dt != null)
                isSameDate = dt.Date == thisDay.Date;

            return isSameDate;
        }
    }
    public sealed class Duration
    {
        public List<Day> Days;
        public DateTime Start;
        public DateTime End;

        public Duration(List<Day> days)
        {
            Days = days;
        }
        public Duration(DateTime start, DateTime end)
        {
            Start = start;
            End = end;
            SetDays();
        }

        /// <summary>
        /// Get sum total of all minutes of all days within the <see cref="Duration"/>.
        /// </summary>
        /// <returns></returns>
        public int GetTotalMinutes()
        {
            int count = 0;

            foreach (var day in Days)
                count += day.Minutes.Count;

            return count;
        }

        /// <summary>
        /// Initialises <see cref="Days"/>.
        /// Begins with <see cref="Start"/>, ends with <see cref="End"/> and initialises all days between the two.
        /// </summary>
        void SetDays()
        {
            var daysInBetween = (End - Start).Days;
            Days = new List<Day>(daysInBetween);

            var dayOfWeek = Convert.ToInt16(Start.DayOfWeek);
            Days.Add(new Day(Start, Convert.ToInt16(Start.TimeOfDay.TotalMinutes)));

            for (int i = 0; i < daysInBetween; i++)
            {
                var nextDate = Start.AddDays(i + 1);

                if (nextDate.Date != End.Date)
                    Days.Add(new Day(nextDate.DayOfWeek, nextDate.Day, nextDate.Month, nextDate.Year));
            }

            Days.Add(new Day(End, 0, Convert.ToInt16(End.TimeOfDay.TotalMinutes)));
        }

        /// <summary>
        /// Sets <see cref="Start"/> equal to the smaller value of <see cref="Start"/> and <see cref="End"/>.
        /// </summary>
        /// <param name="inverted">Outputs true if <see cref="Start"/> and <see cref="End"/> has to be swapped, otherwise false.</param>
        //void SetStartDate(out bool inverted)
        //{
        //    inverted = false;

        //    if (Start > End)
        //    {
        //        Helpers.Common.Swap(ref Start, ref End);
        //        inverted = true;
        //    }
        //}

        /// <summary>
        /// Removes all minutes from the <see cref="Duration"/> which are not included in <paramref name="businessHours"/> parameter
        /// i.e. are not considered 'working' minutes.
        /// </summary>
        /// <param name="businessHours">Representation of minutes in days which are working minutes e.g. 09:00 - 17:00.</param>
        public void RemoveNonWorkingMinutes(List<Day> businessHours)
        {
            var startMinutes = Start.TimeOfDay.TotalMinutes;
            var endMinutes = End.TimeOfDay.TotalMinutes;

            if (Start.Date == End.Date && Days.Count == 2)
                Days.RemoveAt(1);

            foreach (var day in Days)
            {
               
                   var businessDay = businessHours.Find(x => x.DayOfWeek == day.DayOfWeek);
                var minutesToRemove = new List<Minute>();

                foreach (var minute in day.Minutes)
                {
                    if (businessDay.Minutes.FindAll(m => m.Index == minute.Index).Count <= 0)
                        minutesToRemove.Add(minute);

                    if (day.IsSameDate(Start))
                        if (minute.Index < startMinutes)
                            minutesToRemove.Add(minute);


                        else if (day.IsSameDate(End))
                            if (minute.Index >= endMinutes)
                                minutesToRemove.Add(minute);
                }

                foreach (var minuteToRemove in minutesToRemove)
                    try
                    {
                        day.Minutes.Remove(minuteToRemove);
                    }
                    catch { continue; } // Lazy. Expecting IndexOutOfBounds..
            }
        }
    }
    //Murthy END - CALCULATE DUration
}

Calculate Duration between two times using business calendar in MS CRM Dynamics

Calculate Duration between two times using business calendar in MS CRM Dynamics

Guid calendarGuid = TimeManager.GetCalendarFromSLA(sla.SLAId);

                var ResolutionDate = DateTime.UtcNow;
                List<TimeSlot> timeSlots = TimeManager.GetAvailableTimeSlotsForCalendar(calendarGuid, CaseCreateTime, DateTime.UtcNow);
                double TotalSpanSeconds = 0;
                double Totalhours = 0;
                double TotalBusinesshours = 0;
                double Totaldays = 0;
                double TotalMinutes = 0;
                int TotalDaysTillDay = timeSlots.Count / 2;
                foreach (var slot in timeSlots)
                {
                    double slotSpan = 0;
                    double hours = 0;
                    if (CaseCreateTime > slot.End)
                        slotSpan = 0;
                    else if (CaseCreateTime > slot.Start && ResolutionDate < slot.End)
                    {
                        slotSpan = (ResolutionDate - CaseCreateTime).TotalSeconds;
                        hours = (ResolutionDate - CaseCreateTime).TotalHours;
                    }
                    else if (CaseCreateTime > slot.Start && CaseCreateTime < slot.End && ResolutionDate > slot.End)
                    {
                        slotSpan = (slot.End - CaseCreateTime).TotalSeconds;
                        hours = (slot.End - CaseCreateTime).TotalHours;
                    }
                    else if (ResolutionDate > slot.Start && ResolutionDate < slot.End)
                    {
                        slotSpan = (ResolutionDate - slot.Start).TotalSeconds;
                        hours = (ResolutionDate - slot.Start).TotalHours;
                    }
                    else
                    {
                        slotSpan = (slot.End - slot.Start).TotalSeconds;
                        hours = (slot.End - slot.Start).TotalHours;
                    }

                    TotalSpanSeconds += slotSpan;
                    Totalhours += hours;

                }
                TotalBusinesshours = Totalhours;

                double reminingHours = Totalhours % 8;
                Totaldays = Totalhours / 8;             
                TotalMinutes = reminingHours * 60;

                BusinessHoursDuration = new TimeSpan(Convert.ToInt32(Totaldays), Convert.ToInt32(reminingHours), Convert.ToInt32(TotalMinutes), 0);


                var resolutionDuration = string.Format("{0:%d} days, {0:%h} hours, {0:%m} minutes, {0:%s} seconds", BusinessHoursDuration);




//GetAvailableTimeSlotsForCalendar


public List<TimeSlot> GetAvailableTimeSlotsForCalendar(Guid calendarId, DateTime startTime, DateTime endTime)
        {
            try
            {
                //Convert time to UTC because calendar info returned by the system is in UTC
                if (startTime.Kind == DateTimeKind.Local)
                    startTime = startTime.ToUniversalTime();
                if (endTime.Kind == DateTimeKind.Local)
                    endTime = endTime.ToUniversalTime();

             
                TimeInfo[] timeInfos = GetTimeInfoForCalendar(calendarId, startTime, endTime);
           
                List<TimeSlot> timeSlots = GetAvailableTimeSlots(timeInfos);
                return timeSlots;

            }
            catch (Exception ex)
            {
               
                throw;
            }
        }


// to get time slot

internal Entity GetCalendarFromSLA(Guid slaId)
        {
            try
            {
                return Retrieve(SLAColumnNames.EntityName, slaId, new string[] { SLAColumnNames.SLAId, SLAColumnNames.BusinessHours });
            }
            catch (Exception ex)
            {
                ExceptionHelper.LogException(ex, user, BaseConstants.BaseModuleName, "Error getting calendar for SLA:" + slaId.ToString(), service);
                throw;
            }
        }

Calculate Case Resolution time based on business calendar SLA


Calculate Case Resolution time based on business calendar SLA

//CaseHandler caseHandler  // this caseHandler to retrieve the case related fields like case create time
// you can replace it by CaseEntity

public TimeSpan CalculateCaseresolutionDuration(CaseHandler caseHandler, string incidentId, DateTime CaseCreateTime)
        {
            TimeSpan BusinessHoursDuration;
         
            try
            {
                TimeManager TimeManager = new TimeManager(service, tracer, userId, CaseManagementConstants.Module);
                var caseEntity = caseHandler.GetCaseById(incidentId, new[]
                {
//pass case attributes which are reqired               
IncidentEntityAttributeNames.ProcessConfigurationIdFieldName,IncidentEntityAttributeNames.OwnerIdFieldName,IncidentEntityAttributeNames.Entitlement
            });

                var ConfigID = CrmTypesHelper.GetEntityReferenceId(caseEntity, IncidentEntityAttributeNames.ProcessConfigurationIdFieldName);
                var OwnerID = CrmTypesHelper.GetEntityReferenceId(caseEntity, IncidentEntityAttributeNames.OwnerIdFieldName);
                Guid OwnerGuid = new Guid(OwnerID);
                Guid ConfigGUID = new Guid(ConfigID);

                SLAConfigObject slaConfig = new SLAConfigObject(service, userId, CaseManagementConstants.Module, tracer);

                SLAObject sla = slaConfig.GetCaseProcessConfigurationSLAbyConfigId(ConfigGUID, Guid.Empty, OwnerGuid);
                           

                Guid calendarGuid = TimeManager.GetCalendarFromSLA(sla.SLAId);

                var ResolutionDate = DateTime.UtcNow;
                List<TimeSlot> timeSlots = TimeManager.GetAvailableTimeSlotsForCalendar(calendarGuid, CaseCreateTime, DateTime.UtcNow);
                double TotalSpanSeconds = 0;
                double Totalhours = 0;
                double TotalBusinesshours = 0;
                double Totaldays = 0;
                double TotalMinutes = 0;
                int TotalDaysTillDay = timeSlots.Count / 2;
                foreach (var slot in timeSlots)
                {
                    double slotSpan = 0;
                    double hours = 0;
                    if (CaseCreateTime > slot.End)
                        slotSpan = 0;
                    else if (CaseCreateTime > slot.Start && ResolutionDate < slot.End)
                    {
                        slotSpan = (ResolutionDate - CaseCreateTime).TotalSeconds;
                        hours = (ResolutionDate - CaseCreateTime).TotalHours;
                    }
                    else if (CaseCreateTime > slot.Start && CaseCreateTime < slot.End && ResolutionDate > slot.End)
                    {
                        slotSpan = (slot.End - CaseCreateTime).TotalSeconds;
                        hours = (slot.End - CaseCreateTime).TotalHours;
                    }
                    else if (ResolutionDate > slot.Start && ResolutionDate < slot.End)
                    {
                        slotSpan = (ResolutionDate - slot.Start).TotalSeconds;
                        hours = (ResolutionDate - slot.Start).TotalHours;
                    }
                    else
                    {
                        slotSpan = (slot.End - slot.Start).TotalSeconds;
                        hours = (slot.End - slot.Start).TotalHours;
                    }

                    TotalSpanSeconds += slotSpan;
                    Totalhours += hours;

                }
                TotalBusinesshours = Totalhours;

                double reminingHours = Totalhours % 8;
                Totaldays = Totalhours / 8;             
                TotalMinutes = reminingHours * 60;

                BusinessHoursDuration = new TimeSpan(Convert.ToInt32(Totaldays), Convert.ToInt32(reminingHours), Convert.ToInt32(TotalMinutes), 0);


                var resolutionDuration = string.Format("{0:%d} days, {0:%h} hours, {0:%m} minutes, {0:%s} seconds", BusinessHoursDuration);
            }

            catch (Exception e)
            {
                ExceptionHelper.LogException(e, userId, CaseManagementConstants.Module, "Error occured", service);
                tracer.AddError("Error occured in Case Management, CalculateCaseresolutionDuration:" + e.Message);
                throw;
            }
            return BusinessHoursDuration;
        }