Celebrating International Women’s Day
August 14, 2024
10 technological advances
10 Technological Advances of the Last 10 Years
December 17, 2024

Creating Business Closures with the Dynamics 365 API

Posted by TrueNorth

By Alex Radice, Senior Solution Developer, TrueNorth IT

The Service Management side to Dynamics 365 includes features such as calculating the times when cases must be closed by to avoid breaching the organisation’s Service Level Agreement. Measuring resolution time requires the system to know which days count as “business days” so it maintains a list of Business Closures which are non-working days:

Business Closures are also used for scheduling, work hours and within other areas of Dynamics 365. Normally a system administrator has to manually enter the list of Business Closures. One of our clients wanted us to automate this process based up on the gov.uk JSON API. Our basic design for this was:

  1. An Azure function triggered by a schedule trigger to run at midnight on the first day of each month.
  2. The Azure function retrieves the bank holiday from https://www.gov.uk/bank-holidays.json, then calls the Dynamics 365 Web API to check whether each bank holidays is already present in dynamics 365, and if not then creates it.

 

So far, so simple. The Web API mostly wraps CRUD (Create, Read, Update & Delete) operations against the underlying database data of the Dynamics 365 system (its list of clients, contacts, opportunities, incidents, etc), plus some custom operations which carry out functions across multiple tables like promoting a lead to a contact. There’s a table called “calendar” which contains the names of all calendars and another called “calendarrules” which contains specific dates on those calendars so I expect that these are going to hold the data I need. Even better, the “organization” table which contains a single row holding organisation-wide settings has a column “businessclosurecalendarid” which specifies the ID of the calendar containing the system business closures. All I need to do is retrieve all the “calendarrule” rows which have the matching ID.

First hitch: The standard “RetrieveMultiple” API doesn’t work for “calendarrule” rows

Fortunately this is a problem which lots of people have already encountered and they have written blog posts about it (like this one); you can retrieve the parent “calendar” row and its child “calendarrule” rows in a single operation.

private IEnumerable<Entity> GetBusinessClosureCalendarRules(IExecutionContext context, IOrganizationService service)
{
Entity organisationEntity = service.Retrieve("organization", context.OrganizationId, new ColumnSet("businessclosurecalendarid"));

QueryExpression calendarQuery = new QueryExpression("calendar");
calendarQuery.ColumnSet = new ColumnSet(true);
calendarQuery.Criteria = new FilterExpression();
calendarQuery.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.Equal, organisationEntity["businessclosurecalendarid"].ToString()));
Entity businessClosureCalendar = service.RetrieveMultiple(calendarQuery).Entities[0];

return businessClosureCalendar.GetAttributeValue<EntityCollection>("calendarrules").Entities;
}

As my Azure Function uses Node rather than C# I want to achieve this using the REST API rather than the SOAP API:

const calendar = await new DynamicsWebApi().retrieveRequest(
{
select: ['calendarid'],
expand: [{
property: 'calendar_calendar_rules',
select: ['calendarruleid' ,' endtime', 'starttime']
} as DynamicsWebApi.Expand],
collection: 'calendars',
key: calendarId,
includeAnnotations: "OData.Community.Display.V1.FormattedValue"
} as RetrieveRequest)

This works fine and I receive a JSON array looking something like this:

{
"@odata.context": "https://d365cedev1.crm11.dynamics.com/api/data/v9.2/$metadata#calendars(calendarid,calendar_calendar_rules(calendarruleid,endtime,starttime))/$entity",
"@odata.etag": "W/\"1939718231\"",
"calendarid": "8aa279b4-9d26-e811-811d-5065f38a29d1",
"_businessunitid_value@Microsoft.Dynamics.CRM.associatednavigationproperty": "businessunitid",
"[email protected]": "businessunit",
"_businessunitid_value": "c75c81ae-9d26-e811-811d-5065f38a29d1",
"calendar_calendar_rules": [
{
"@odata.etag": "W/\"1939718279\"",
"calendarruleid": "305b110b-3414-ef11-9f89-000d3a0d1d75",
"endtime": null,
"[email protected]": "26/12/2024 00:00",
"starttime": "2024-12-26T00:00:00Z"
},
{
"@odata.etag": "W/\"1939718278\"",
"calendarruleid": "3d80ec84-3814-ef11-9f89-000d3a0d1d75",
"endtime": null,
"[email protected]": "25/12/2024 00:00",
"starttime": "2024-12-25T00:00:00Z"
},
{
"@odata.etag": "W/\"1939718291\"",
"calendarruleid": "4ac8e48a-3814-ef11-9f89-000d3a0d1d75",
"endtime": null,
"[email protected]": "26/05/2025 00:00",
"starttime": "2025-05-26T00:00:00Z"
}
]
}

Second hitch: The REST “Create” API doesn’t work for CalendarRule entries

I can retrieve the calendarrule entities via their parent calendar as shown above, but the aim of this story is to also add new calendarrules. Expecting that a calendarrule is an entity like any other, I try to create one using the REST API:

"The 'Create' method does not support entities of type 'calendarrule'. MessageProcessorCache returned MessageProcessor.Empty. "

Oh dear. It turns out that calendarrule entities are not entities like any other: they are a kind of “fake” entity which dynamics uses to represent the detail of a calendar.

I can use the REST Create API to create a new calendar with its child calendarrule objects (see this link), but there is then no way to switch the dynamics organisation to use my new calendar for its Business Closures.

Next I tried using a plugin that I triggered from a Custom Action that updated a calendar along with all its child calendarrules. This worked…. sort of. It created calendarrules which were visible on the Business Closures screen, but when any dynamics operation ran which needed to make use of Business Closures ran, it crashed out with an internal error. The calendarrules that I had added were Business Closure-ish enough to appear on the Business Closures screen, not quite Business Closure-ish enough to work in the calculations.

During this last step I did get the clue that set me on the trail of the eventual solution though. I noticed that the REST URL that was returning the error ended with msdyn_BusinessClosures. Looking this up in the XRM Toolbox Metadata Explorer plugin I saw that this is a built-in table called… “Business Closure”:

I decided to use the “Dataverse REST Explorer” plugin for XRM Toolbox to see if there were any dynamics actions related to this table. I found this!

Which I call from my Node backend like this:

await new DynamicsWebApi().executeUnboundAction('msdyn_BusinessClosureSave', {
Name: '2024 Christmas Day',
Start: '2024-12-25T00:00:00.000Z',
End: '2024-12-26T00:00:00.000Z'
});

You don’t even need to specify the calendarId; dynamics looks that up itself (there’s only one Business Closures calendar for the organisation).

This is rather poorly documented at present:

Hopefully this blog post will help with that!

Get our Latest Articles in your Inbox

Enjoyed this article? Sign up for our email newsletter and get real-world information on all things Microsoft, cloud and tech. Your information will be shared with MailChimp but no one else, and you can unsubscribe with one click at any time

Sign-Up to Our Newsletter: