/ Development  

Unlock hidden superpowers; How business calendars turbocharge your "Process Automation"

Hi there “Process Automation” fans,

Welcome to a new installment of “Process Automation” tips.

Let me start with an honest statement; I have never used “Business Calendars” in any of my projects…UNTIL NOW! I was also the one always telling we could do without them. That’s indeed true, but with larger business projects, you want to use them calculating business working days, including all the extra free days within a country you craft the solution. And indeed…The world shifts when it gets to public free days, and there isn’t a single rule applicable to all! So, you want to be in control with indeed the power of “Business Calendars”…

OPA 24.3 has an enhancement feature for the ‘Business Calendar’ type of document; Read about it here


Let’s get right into it…

Where to start when your VM of OPA is already operational? Well, jump into your project and directly create a new type of document within the configs folder of the project:

bc_001

Yes, you see it correctly…We start with the exceptional days (or public holidays) that we connect to the real ‘Business Calendar’. I do a first implementation like this:

bc_002

You see also all the public holidays are behind us! It’s now sweating till Christmas! 💦 😅 💦

Save it and move to the next ‘Business Calendar’ implementation:

bc_003

In the ‘Calendar Exceptions’ tab, you select the exceptions document; Make sure to also set the correct time-zone settings in the final tab:

bc_004

Do a publication…

Now what to do? Indeed, a valid question with a solid answer continuing your read…

The first thing you want to do in your project is adding a new runtime reference from the context menu of your project; Nicely saved in the runtimerefs folder. It’s of type ‘Web Service Interface’:

bc_005

You see multiple ‘BusinessCalendar’ method sets; You want “the one” with the red arrow! The other one is legacy from the enhanced version!


Services exploration

Time to explore some service calls (which I cleaned from namespace declarations for readability). First a grip on our crafted calendar:

1
2
3
4
5
<SOAP:Envelope>
<SOAP:Body>
<getCalendars />
</SOAP:Body>
</SOAP:Envelope>

With a response of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<data>
<getCalendarsResponse>
<getCalendars>
<Calendars>
<Calendar>
<Id>08002786-6f6a-a1f0-921a-bf54a7709858</Id>
<Name>bc_working_days</Name>
<Description>bc_working_days</Description>
<QName>bc_working_days</QName>
</Calendar>
</Calendars>
</getCalendars>
</getCalendarsResponse>
</data>

Next is calculating a start-date based on an end-date.

1
2
3
4
5
6
7
8
9
10
<SOAP:Envelope>
<SOAP:Body>
<calculateStartDate>
<calendarName>bc_working_days</calendarName>
<endDate>2025-06-17T11:00:00.0</endDate>
<hours>1</hours>
<minutes>1</minutes>
</calculateStartDate>
</SOAP:Body>
</SOAP:Envelope>

Format for date can also be yyyy/MM/dd…Just that you know!

The response for this is:

1
2
3
4
5
<data>
<calculateStartDateResponse>
<calculateStartDate>2025-06-17T09:59:00.0</calculateStartDate>
</calculateStartDateResponse>
</data>

Is this correct? Yes! As 2025-06-17 is a Tuesday and the time is exactly 1 hour and 1 minute less!

Now watch this table with the same hours/minutes subtraction:

Input Output Explanation
2025-06-16T11:00:00.0 2025-06-16T09:59:00.0 As expected for a Monday morning!
2025-06-16T09:00:00.0 2025-06-16T07:59:00.0 Again, expected
2025-06-16T08:00:00.0 2025-06-13T14:59:00.0 Interesting, as that’s a Friday; 1 minute before 15.00 u. with respect of the week-end!
2025-06-13T08:00:00.0 2025-06-12T14:59:00.0 That’s consistent!

Ok, why 15.00 as my business days end at 17.00? Does the service call respect my weekdays configuration? Yes it does as when my Friday end at 18.00 I get a return value of ...T15:59:00.0…So, why a difference of 2 hours!? Well, that’s a time-zone thing where our response is a worldwide UTC/GMT time when I’m in a CEST zone 2 hours later! Let’s not dive into this now…I’ll leave it with you. For me, it still works as expected!

Let’s move on to another service call:

1
2
3
4
5
6
7
8
9
10
<SOAP:Envelope>
<SOAP:Body>
<calculateStartDateInBusinessDays>
<calendarName>bc_working_days</calendarName>
<endDate>2025-06-17T11:00:00.0</endDate>
<nrOfBusinessDays>1</nrOfBusinessDays>
<moveToEndOfPreviousDay>false</moveToEndOfPreviousDay>
</calculateStartDateInBusinessDays>
</SOAP:Body>
</SOAP:Envelope>

The response of the call gives the start of the previous day:

1
2
3
4
5
<data>
<calculateStartDateInBusinessDaysResponse>
<calculateStartDateInBusinessDays>2025-06-16T07:00:00.0</calculateStartDateInBusinessDays>
</calculateStartDateInBusinessDaysResponse>
</data>

With <moveToEndOfPreviousDay>true</moveToEndOfPreviousDay> it’ll give 2025-06-13T15:00:00.0!! Watch closely as that’s BEFORE the weekend where the 17th is on a Tuesday! So, it’s respecting the configured business days!

How about the other way around? Well, calculateEndDate and calculateEndDateInBusinessDays works the same…Only calculating forward! Let’s focus on the “What else?” question…

1
2
3
4
5
6
7
8
<SOAP:Envelope>
<SOAP:Body>
<isWorkingTime>
<calendarName>bc_working_days</calendarName>
<date>2025-06-17T11:00:00.0</date>
</isWorkingTime>
</SOAP:Body>
</SOAP:Envelope>

With an interesting result which is FALSE (for a Tuesday, June 17th 2025 11.00)!? That’s weird…Looks like it’s time for a remote debugging session on a fascinating class com.cordys.businesscalendar.BusinessCalendarRequestHandler within OPA library %CORDYS_HOME%/components/bpmengine/bpmengine.jar

Only, just before I start my R&D remote debug session I directly see the problem (and that my friends, is eXperience!):

bc_006

That’s #BUG #SUPPORT

Let’s give it another shot with this SOAP message:

1
2
3
4
5
6
7
8
<SOAP:Envelope>
<SOAP:Body>
<isWorkingTime>
<calendarName>bc_working_days</calendarName>
<dateTime>2025-06-17T11:00:00.0</dateTime><!--NOT <date/>-->
</isWorkingTime>
</SOAP:Body>
</SOAP:Envelope>

Now we nicely get a response of TRUE and 2025-06-17T6:00:00.0 is FALSE (not a weekend, but outside business hours)!

What else?

What about this one:

1
2
3
4
5
6
7
8
9
<SOAP:Envelope>
<SOAP:Body>
<calculateAvailableBusinessHours>
<calendarName>bc_working_days</calendarName>
<startDate>2025-06-17T11:00:00.0</startDate>
<endDate>2025-06-18T11:00:00.0</endDate>
</calculateAvailableBusinessHours>
</SOAP:Body>
</SOAP:Envelope>

The response is 28800000!?…Yes, that’s milliseconds! Where 28800000/1000 is 28800 seconds, and 28800/60/60 is 8 hours! Exactly 1 “working” day! 🥳

Now for this one:

1
2
3
4
5
6
7
8
<SOAP:Envelope>
<SOAP:Body>
<calculateEarliestBusinessDate>
<calendarName>bc_working_days</calendarName>
<date>2025-06-17T11:00:00.0</date>
</calculateEarliestBusinessDate>
</SOAP:Body>
</SOAP:Envelope>
Input Output Explanation
2025-06-17T11:00:00.0 2025-06-17T11:00:00.0 That’s the same output as we’re in the middle of a working day already!
2025-06-17T02:00:00.0 2025-06-17T07:00:00.0 Agree, as our working day will start at 07.00
2025-06-15T02:00:00.0 2025-06-16T07:00:00.0 Agree again, as the 15th is a Sunday in the weekend. 16th 7.00 is Monday morning

So, it all works as expected! NICEEEE stuff…

The opposite for this service call is calculateLatestBusinessDate…One for your own to figure out!

What else? Well, for the service calls we leave it for now…Time to dive into a new section of using BCs.


Use a Business Calendar (in BPMs)

First make sure you have a new BPM available; Just a simple one-activity BPM (saved in the bpms folder of the project) like this with a selection of your business calendar:

bc_007

Now attach a ‘User Interface’/‘Entity Layout’ to the activity (from the activity context menu) and have a look at the ‘Duration’ tab using a BC to calculate a due time for the task:

bc_008

So, starting this process will land a task in the inbox for the current user with a due date of seven business(!) days later.

You can also apply this same setting for a ‘Delay’ construct:

bc_009

That’s the same for a ‘Time-out’ construct:

bc_010

These examples are for BPMs…That’s nice, but we are more interested in terms of entities, lifecycles, deadlines, and due-date properties!


Use BCs in entity modelling-land

Create yourself a ‘Case’ entity with a case_name (as TEXT) and case_completion_date (as DATE) property and generate the rest. Add the ‘Lifecycle’ BB and have first glimpse on the business calendar settings for a lifecycle:

bc_011

You can also scope down further with a same setting on ‘Activity’ level:

bc_012

Can we do more?…Yes, please! 😎

Add a ‘Deadline’ building block to the entity with the name dl_for_completion and do an implementation like this:

bc_013

Now for the great trick which is setting the ‘Completion date’ to a valid date considering our business day! Aha…Now it gets interesting as that’s (advanced) business logic! 🤔

With “advanced” I mean BPM logic beyond what is out-of-the-box possible with a simple ‘Rule’ BB!

Start with the creation of a ‘Rule’ BB (of type ‘Event - Application UI’) on the entity with name e_opc_set_completion_date and implemented like this:

bc_014

I have version 25.2 with the use of client-side action events and multi-action rules

So, when the completion date is empty (you don’t want to set it again!) and the lifecycle has an instance state, we’ll set the completion date property to now…Do a deployment and check it out! Works like a charm, but that’s not the point. You can even do things like this:

  • now + 1
  • today + 7
  • today + duration(0, 0 , 7) which is [year, month, day]!
  • today + duration("P7D") read about this XML duration format here

The only disadvantage is that it’s not respecting our business calendar days AND that’s where the fun starts with a BPM and our previous explained BC webservices knowledge!

So, today is the 20th of June 2025 (a Friday) which makes the next 7 business days land on 1st of July 2025 (a Tuesday) where I see just 7 days down the road:

bc_015


A BPM implementation calculation date respecting business days

Move back into the ‘Rule’ BB and change the action to start a process/BPM (saved in the bpms folder of the project!):

bc_016

I move the global condition of the ‘Rule’ BB to an action specific condition; Why? Because otherwise the BPM does not trigger; it will only fire when changing one of the properties in the condition. I know it has to do with the “advanced” setting “Start the process only when the specified condition changes from false to true”, but it behaves very weird. Only true is also not working; that’s why I use
(item.Lifecycle.InstanceStatus=="New" && item.Properties.case_completion_date==null) || true
This behaves the same but smells different…Weird OPA things!? 🙃 Now it always starts independent of that weird checkmark. Ohwwwww…That global condition is now set the true…Has it something to do with it?…I continue my life.

Do a first quick deployment to see if the BPM instance (from the PIM perspective) is visible!

This BPM requires some steps…It needs to calculate the business days (let’s say 7 for now) from today (when the BPM starts) and it needs to update our ‘Case’ entity instance with a valid completion date!

For this we miss one puzzle piece which is the ‘Web service’ BB on the entity with the exposure of the ‘Update’ operation:

bc_017

Do a quick publication! AND because of this BB, we also require a new service container of type ‘Application Server’ from the ‘System Resource Manager’ to support this…I leave this task for you; Give a comment if you don’t have a clue.

We continue with the (by default) long-lived BPM implementation like this:

bc_018

With this consolidated view for the messagemap:

bc_019

I found this one via my ChatGPT friend:
java.time.Instant.ofEpochMilli(number(instance:instanceProperties/instance:startTime/text()))
See more of these examples here

The BC service call ‘calculateEndDateInBusinessDays’ eventually looks like this resulting in 2025-07-01T15:00:00.0:

1
2
3
4
5
6
7
8
9
10
<SOAP:Envelope>
<SOAP:Body>
<calculateEndDateInBusinessDays>
<calendarName>bc_working_days</calendarName>
<startDate>2025-06-20T14:26:44.719Z</startDate><!--The extra [Z]one in the end in fine!-->
<nrOfBusinessDays>7</nrOfBusinessDays>
<moveToStartOfNextDay>false</moveToStartOfNextDay>
</calculateEndDateInBusinessDays>
</SOAP:Body>
</SOAP:Envelope>

FYI an interesting observation; A long-lived BPM triggering a client-side ‘Rule’ BB!? That doesn’t make up as client-side has an “on-the-fly update mindset”, so a short-lived BPM would be a better choice. Does it still update runtime on the fly which is the mindset of client-side? Well, NOPE! BUT we can simply update this execution mode in the properties of the BPM…Also, a task for yourself (incl. the deployment of the change)!

It’s time for weekend my friends…We learned our lessons, and we shared some great tricks again…


A great “DONE” where we finally found our use-cases implementing the ‘Business Calendar’ type of document. As I review the post, it’s also a rather simple concept that just needs to snap. Where (as for me), you first need put with your nose onto the facts to see the advantages. Well, the facts are clear now and the business calendars will get back more often at other projects from now on (if requested!). Have a great weekend!

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The Process Automation guy”?