Rule Engine
About the rule engine
Every appointment that is assigned to a resource that is set to sync with their Exchange calendar, will eventually end up in that person's Outlook calendar. Understandably, a more fine-grained filtering system is desired to allow the planners to control which appointments should be synchronized to the calendars of the resources. For example, temporary appointments or placeholders have no added value and should therefore not appear in the calendars.
As such, the rule engine in the Exchange connector introduced in Dime.Scheduler 2022.2.0 provides a framework for customers to define their own requirements, that is to be able to change them without having to recompile the system. The first iteration of the rule engine is to be configured in the "Rules" section of the application settings file of the service and the web app (appsettings.json).
Example
The (simplified) sample below showcases how this might be put into effect. You will find there are two rules which need to be fulfilled before an appointment can be sent to Dime.Scheduler:
- The category name (the background of the appointment in the planning board) must be “SERVICE” and a certain field on the task needs to be set to true. In this case, a free boolean field is used to indicate whether to synchronize the record to Exchange.
- The time marker (the line below the subject of the appointment in the planning board) name must be “IN PROCESS” and a certain field on the task needs to be set to true. In this case, a free boolean field is used to indicate whether to synchronize the record to Exchange.
"Rules": [
{
"Name": "Filter indicators",
"Rules": [
{
"Name": "FilterCategory",
"ErrorMessage": "Cannot send this category to Outlook.",
"Expression": "appointment.Category <> NULL AND appointment.Category.Name == \"SERVICE\" AND appointment.Task <> NULL AND appointment.Task.FreeBit1 == true"
},
{
"Name": "FilterTimeMarker",
"ErrorMessage": "Cannot send this time marker to Outlook.",
"Expression": "appointment.TimeMarker <> NULL AND appointment.TimeMarker.Name == \"IN PROCESS\" AND appointment.Task <> NULL AND appointment.Task.FreeBit1 == true"
}
]
}
]
Assume the planner schedules the following appointments in the planning board:
Appointment | Category | Time Marker | Task Free Boolean 1 |
---|---|---|---|
Appointment #1 | SERVICE | IN PROCESS | TRUE |
Appointment #2 | SERVICE | FINISHED | TRUE |
Appointment #3 | PREVENTIVE MAINTENANCE | IN PROCESS | TRUE |
Appointment #4 | SERVICE | IN PROCESS | FALSE |
In the Exchange connector, the rule engine will evaluate that only appointment #1 should be sent to Exchange because it’s the only one that fulfills both requirements: the category name is “SERVICE”, the time marker name is “IN PROCESS” and the free boolean field is “TRUE”.
Obviously, this is just a simple example; it is up to the customer to define what the rule must be. Through a special syntax that the rule engine can understand, powerful queries can be constructed. If the requirements change, the customer can update the rules in the file and they will be put into effect immediately. It is also worth mentioning that through this syntax, customers have access to the entire data graph: filters can be created based on data of the appointment, the planning lines, the service orders, customer data, etc.
Constructing rules
It is possible to have multiple conditions for one given rule, as you can deduct from the example above. It is up to you to define the filter, but splitting up a rule into multiple expressions makes it easier to diagnose potential problems. Since you can name a condition, you'll quickly be able to determine why a given appointment wasn't synchronized to (and from) Exchange.
For each condition, you need to define a unique name, a descriptive text when a condition was not met, and the actual rule itself. The latter field uses a syntax that provides a simple and convenient way of writing expressions that can be parsed into expression trees. In other words, it is a predicate that the application parses and executes at runtime every time an appointment is to be synchronized. There are three components to such an expression.
The appointment
The template represents the body of a filter function that many programming languages support. The appointment
field is where it all starts: this represents the appointment. Inside the field, you may access the entire appointment graph. Common properties include:
- Category.Name
- TimeMarker.Name
- StartDate
- EndDate
- Subject
- Body
- Task.Taskno
- Task.Job.JobNo
Operator
Once the desired property has been located, its value has to be compared to some other value so the expression can determine whether to pass the appointment onto Exchange. Supported operations include:
==
oreq
orequal
: Equals!=
or<>
orne
orneq
ornotequal
: Does not equal<
orlt
or<=
orlte
: Less than (or equal)>
orgt
or>=
orgte
: Greater than (or equal)x && y
orx and y
: Logical ANDx || y
orx or y
: Logical OR
Reference value
Finally, we need to compare the appointment with a reference value using the operator. Typically, this value will be static (as the examples), but the same appointment
object can be used to compare it to another value in the appointment's graph.
Null reference checks
Some navigation properties in the graph might be missing (e.g. appointments without time marker, category or linked task). In order to prevent null reference error messages while applying the rule, additional checks need to be included in the rule as shown in the example above.
Additional examples
Do not send appointments to Exchange that are marked with the time marker "TO BE CONFIRMED" (appointments without time marker can be sent):
"Rules": [
{
"Name": "Filter planning status",
"Rules": [
{
"Name": "FilterTimeMarker",
"ErrorMessage": "Cannot send this appointment to Outlook as it is not confirmed yet.",
"Expression": "appointment.TimeMarker == NULL OR appointment.TimeMarker.Name <> \"TO BE CONFIRMED\""
}
]
}
]
Only send appointments to Exchange that are linked to a task:
"Rules": [
{
"Name": "Filter appointments without task",
"Rules": [
{
"Name": "FilterTasks",
"ErrorMessage": "Cannot send this appointment to Outlook as it is not linked to a task.",
"Expression": "appointment.Task <> NULL"
}
]
}
]
Send all appointments to Exchange, except ABSENCES that are not confirmed yet:
"Rules": [
{
"Name": "Filter absences",
"Rules": [
{
"Name": "FilterAbsencesByStatus",
"ErrorMessage": "Cannot send this appointment to Outlook as it is not linked to a task.",
"Expression": "appointment.Task == NULL OR (appointment.Task.Job <> NULL AND appointment.Task.Job.JobNo == \"ABSENCES\" AND (appointment.TimeMarker == NULL OR appointment.TimeMarker.Name <> \"TO BE CONFIRMED\"))"
}
]
}
]