D365: Add business day(s) to specific date considering weekends and public holidays

Recently, we got a requirement to add business days/working days to specific date considering weekends and public holidays defined in CRM system and get the next working day.

Below is the workdays and working hours defined in the CRM system:

Fridays and Saturdays are considered as week off.

Below are the list of holidays defined in the CRM system which are observed:

Given a specific date, we were supposed to return the next working day considering the weekends and public holidays defined as above.

Below is the block of code which can be used to meet the requirement:

/// <summary>
/// Get Next working date 
/// </summary>
/// <param name="service">Organisation Service</param>
/// <param name="serviceCalenderId">GUID of service calendar</param>
/// <param name="specificDate">Date to which days need to be added</param>
/// <param name="workingDaysToAdd">Number of days to be added</param>
/// <returns>Next Working Date</returns>
public static DateTime GetNextWorkingDay(IOrganizationService service, Guid serviceCalenderId, DateTime specificDate, int workingDaysToAdd)
{
	var currentDate = specificDate;
	var businessClosures = GetBusinessClosureCalendarRules(service, serviceCalenderId);

	// Calculate the working days by taking out the weekends(Fridays and Saturdays)
	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 public holidays
	for (var i = currentDate; i <= date; i = i.AddDays(1))
	{
		if (i.DayOfWeek == DayOfWeek.Friday || i.DayOfWeek == DayOfWeek.Saturday)
			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);
                                while (!IsWorkingDayOfWeek(date))
                                {
                                        date = date.AddDays(1);
                                }
				if (date.DayOfWeek == DayOfWeek.Friday)
				{
					date = date.AddDays(2);
                                        while (!IsWorkingDayOfWeek(date))
                                        {
                                                date = date.AddDays(1);
                                        }
					i = i.AddDays(1);
				}
				else if (date.DayOfWeek == DayOfWeek.Saturday)
				{
					date = date.AddDays(1);
                                        while (!IsWorkingDayOfWeek(date))
                                        {
                                                date = date.AddDays(1);
                                        }
				}
			}
		}
	}
	return date;
}

private static IEnumerable<Entity> GetBusinessClosureCalendarRules(IOrganizationService service, Guid serviceCalendarId)
{
	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, serviceCalendarId.ToString()));

	// Get Calendar
	var businessClosureCalendar = service.RetrieveMultiple(query).Entities[0];
	var holidaySchedule = service.Retrieve(businessClosureCalendar.GetAttributeValue<EntityReference>("holidayschedulecalendarid").LogicalName, businessClosureCalendar.GetAttributeValue<EntityReference>("holidayschedulecalendarid").Id, new ColumnSet(true));

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

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

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);
	}
}

Hope it helps !!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.