When extending Dynamics 365 using plugins and workflows, requirements often involve interacting with SLAs. This could mean retrieving:

  • Daily working hours for example Monday - Friday 09:00 - 17:00
  • Scheduled breaks in the working day such as lunch time
  • Business-wide holidays such as bank holidays

Let's walk through the code in your plugin/custom workflow

First, knowing the ID of the SLA you want to get, retrieve the SLA using service.Retrieve()

var sla = service.Retrieve("sla", ID, new ColumnSet(new string[] { "businesshoursid" });

A reference to the Business Hours associated with the SLA is stored in the "businesshoursid" field. To retrieve it's ID, use GetAttributeValue()

var businessHoursId = sla.GetAttributeValue<EntityReference>("businesshoursid").Id;

Now that we have the ID, we can retrieve the Business Hours record itself. Notice that the schema name of the Business Hours entity is calendar:

var calendar = service.Retrieve("calendar", businessHoursId, new ColumnSet(true));

Calendars (Business Hours) can have multiple rules, depending on how they've been setup in CRM. If your Calendar has been setup to schedule each day the same in CRM, then you'll have fewer calendar rules. However, if each working day is scheduled differently, then you will have many calendar rules.

Retrieve your calendar rules by calling GetAttributeValue() against your Calendar's pattern attribute:

var firstRulePattern = calendarRules[0].GetAttributeValue<string>("pattern");

The pattern is returned as a string as shown by the // comment in my code. You can simply split it by comma delimiters after the substring BYDAY= to establish which days the pattern applies to.

Now we're ready to query the inner calendar rules. These give us values duration and offset which allow us to work out how much time in the day is working, and how much is out-of-hours.

// Get the ID of the inner calendar
var innerCalendarId = calendarRules[0].GetAttributeValue<EntityReference>("innercalendarid").Id;

// Retrieve the inner calendar with all of its columns
var innerCalendar = service.Retrieve("calendar", innerCalendarId, new ColumnSet(true);

// Get the first inner calendar rule
var innnerCalendarRule = innerCalendar.GetAttributeValue<EntityCollection>("calendarrules").Entities.FirstOrDefault();

We're interested in looking at our innerCalendarRule variable's duration and offset. For example, if the offset is 540, then the rule begins at 09:00am in the morning, because offset is in minutes and 540 / 60 = 9. Therefore if the duration is 480, then the rule ends at 17:00 (5pm) because 480 / 60 = 8 and 9(am) + 8 = 17(:00).

You can now go even deeper to query your inner calendar rule's inner calendar rules. These rules will have a subcode attribute which can be used to determine whether the time outlined by the inner calendar rule relates to working time or out-of-hours time.

That's it

Calendar rule inception can be quite complicated to figure out but it is possible. Hopefully this post has helped get you a step closer to fulfilling your Dynamics 365 requirement.

If you still need to know more, I'd highly suggest you step through your code in a console application or integration test. This will allow you to read and interact with the data returned from your queries in real time. To help with this, remember to use: