Workflow automation focuses on what people are doing, but also concerns when they do it. Handling dates can be tricky, especially when computers are involved, because of the details and exceptions. This article addresses public holidays, which are the tricky part of a business days calculation workflow.

Vacation request approval

Despite its apparent simplicity, a vacation request business process can illustrate many of workflow automation’s tricky details. Calculating business days is no exception.

Vacation request trigger form

A vacation request starts a simple management approval process, and includes vacation start and end dates. The approval decision typically needs to consider two questions.

  1. Is there a reason why the employee should not be away at that time?
  2. Does the employee have enough days left from their annual vacation allowance?

The answer to the first question depends on the requested vacation dates, but the second question depends on the number of business days between those two dates. The default approach, perhaps, is for the approver to look at the calendar and count the days, but they could make an error if they forget to account for public holidays, for example.

Approval task showing business days

A nicer solution would be to automate the calculation, so that the approval task would already show the number of vacation days requested – the number of business days between the vacation start and end dates (inclusive).

Business days calculation workflow

In Signavio Workflow Accelerator, you can automate the business days calculation workflow by adding a JavaScript action (a BPMN script task) before the approval step.

Vacation request business days calculation workflow

The JavaScript action calculates the number of business days between the ‘Start date’ and ‘End date’ entered on the trigger form, and sets the value of a new ‘Vacation days’ field to this number of business days. This calculated number of ‘Vacation days’ appears on the ‘Approve vacation request’ task’s form.

const moment = require('moment')
moment.locale(moment.locale(), {
  holidays: ['2017-10-03', '2017-10-31', '2017-12-25', '2017-12-26'],
  holidayFormat: 'YYYY-MM-DD'
})
vacationDays = moment(startDate).businessDiff(moment(endDate).add(1, 'd'))

This script uses the moment.js JavaScript library with the moment-business-days plug-in’s ‘businessDiff’ function to calculate the number of business days between two dates. The catch, however, is that the script has to provide a list of public ‘holidays’ dates. This example only has dates from now until the end of 2017, so in practice you’d have to add several years’ dates, and update the script every year.

Not only do you need new dates for each year, but the dates depend on where you are. The example above lists public holidays in Berlin. In the Netherlands, for example, there aren’t any public holidays in October, and if Christmas day is on a Saturday then there aren’t any in December either. By contrast, France has six public holidays in the second half of the year (seven in Réunion).

The next improvement is to replace the fixed list of dates with dynamic data. Fortunately, the Internet has that covered.

External public holidays data

Instead of using a fixed list of holidays, the script can fetch a list of public holidays from a web service. For Germany, you can query the Feiertage Webservice API for the public holidays for a particular year and a specific German federal state.

For example, http://feiertage.jarmedia.de/api/?jahr=2017&nur_land=BE serves public holidays in 2017 for Berlin, in JSON format. Here’s a longer version of the script that fetches the dates from the web service, before doing the calculation.

const moment = require('moment')
const url = (year) => `http://feiertage.jarmedia.de/api/?jahr=${year}&nur_land=BE`
const setHolidays = (holidays) => {
  const dates = _.values(holidays).map((holiday) => holiday.datum)
  moment.locale(moment.locale(), {
    holidays: dates,
    holidayFormat: 'YYYY-MM-DD'
  })
}
const calculateBusinessDays = (startDate, endDate) => {
  moment(startDate).businessDiff(moment(endDate).add(1, 'd'))
}
request(url(moment(startDate).format('YYYY')), (error, response, holidays) => {
  setHolidays(JSON.parse(holidays))
  console.log(moment(endDate).add(1, 'd'))
  vacationDays = calculateBusinessDays(startDate, endDate)
})

This version script has a limitation: it assumes that the start date and end date are in the same year, and only fetches that year’s public holidays. Even if you assume that people don’t request a vacation that’s more than twelve months long, a vacation from December to January still requires public holiday dates for two consecutive years.

You can fix this either by fetching and adding public holiday dates for both years, or by doing the whole calculation twice – from the start date until 31 December, and from 1 January the next year until the end date. This is left as an exercise for the reader.

Simple things are easy, hard things are possible

Vacation requests and a business days calculation workflow provide a good test of the principle that tools should make simple things easy, and hard things possible. Some tools fail by making simple problems easy to solve, while being too limited to be able to solve the problem as soon as it becomes complicated. Other tools fail by having the flexibility to solve any problem, but are difficult to use for even simple problems.

Signavio Workflow Accelerator makes it straightforward to automate simple workflows, without writing code, but still make it possible to solve harder problems, such as business days calculations and other problems that require dynamic data from external web services.

If you want to experience Signavio Workflow Accelerator’s user-friendly workflows and forms for yourself, or try writing script tasks in JavaScript, then register for a free 30-day trial today.

Published on: June 15th 2017 - Last modified: February 23rd, 2018