Since Dynamic Case Management is all about working together to get to the goal of the case, tasks is the tool to get a piece of work done. Tasks are there to change the case-dossier, so the system can derive its new (sub) goals and next possible steps. There are two different type of tasks in the Case-Engine: user-tasks, performed by an actual user, and automatic-tasks, where the system does some work (for example retrieving/sending data from another system, processing an incoming message event or sending a notification). Both are modelled slightly different.
Modelling a user-task
1. Determine (sub)goals
The task you want to add probably serves a purpose in getting closer to the goal. So it can make sense to start with modelling what you want to achieve with the task. Does the data I add to the case make a milestone true? Does it add to one of the subgoals of making a milestone true? This can be added in the Process domain module (in the implementation). For example to complete the milestone that the application is ready to be judged, I need to add a copy of my drivers license. The fact that the drivers license is present could be one of the subgoals, and can be added on the rule to check whether this milestone is true.
2. Add task precondition
We should determine when our new task is relevant for a user to execute. Since we only want to bother users with necessary actions when it is needed to reaching the case goals, we should determine when it is relevant. This can also be added to the Process domain module (for example in a "Preconditie.<TaskName>" attribute). The actual rule for this added boolean can be modelled in the Process domain derivation module, since this module can access the case-dossier. In our example, the task of uploading a drivers license should be relevant when there is not yet a drivers license available in the case-dossier. When the document is available, then the precondition should be false, since we don't need to ask the user to upload the document anymore.
Note that the Case-Engine determines in each step which precondition is true and which is false. Make sure the logic of task relevancy is complete, and strict enough to not keep irrelevant tasks open.
3. Add task implementation
In the task implementation module, a flow can be added where the actual work is done by the user. The complete dossier is retrieved at starting a task (make sure the user can only see what he needs and is authorised to see in the task implementation). When the task is completed, the new case-dossier state is sent to the Case-Engine to be processed. The dossier is retrieved and sent using the aggregate model of the case-dossier, so make sure this definition holds all information.
case-dossier is shared between Case-Engine and Runtime by its aggregate definition. Make sure the models loaded in the Case-Engine and Runtime share exactly the same aggregate definition.
The task implementation should contain user-interaction, where the model is changed (in our example probably an upload container to upload you drivers license). The actual file would be uploaded to a document management system, but the dossier would hold the information that the drivers license has been added to the dossier.
After the user-interaction, the flow can end with either a "Commit" or a "Rollback".
Most likely is the commit, where the altered dossier is sent back to the Case-Engine, where the Case-Engine stores it, and determines what to do next.
The Rollback exit aborts the task for you, it does this without saving any information in the Case-Engine. Make sure you take this into account when creating the flow. In our example, the document could be stored in the document management system, but the dossier changes (the fact that the drivers license has been uploaded) will not be stored.
A Rollback just resets the task to open. Flowing in the process module after a cancel exit in the implementation does not work, since task is just aborted and set back to open without doing anything else.
4. Add task definition in the process
Since the implementation side is finished, we can now turn to the process module. A task definition should be created using a sensible name and we should determine who may see and execute the task. This will most likely be done by someone with a specific role (or different roles). This could either be done by adding roles to the task definition, or adding an authorisation algorithm. Both achieve the same thing, but the authorisation algorithm is a little bit more flexible (you can group roles, and therefore change the set of roles for multiple tasks at once). In our example it should be someone with the role of "applicant", but maybe also someone who is handling the administration, for when the drivers license is emailed for example. There is no need to create exit event, we don't want the process to "flow" after this task, but we want the rules to determine what is next.
Add the new task in the right phase (process (sub)flow) as an ad-hoc task. Duplicate the precondition attribute in the process module, and add this attribute as condition on the ad-hoc task. Also, make sure it is repeatable, since the precondition should determine whether it should be relevant again or not.
5. Connect implementation and process using the configuration module
Now that the implementation has been created, and the process has been modified, we should connect the two models. In a configuration module we should extend the task mapping with our new task. By doing this, the Case-Engine knows which flow to start when the user tries to start the task.
At the task mapping, the task from the process is connected to the flow implementation, there is no need to map events back to the process, since we have not defined any "flowing" events in the process. Then we should select the right datamapping. This datamapping serves for the Case-Engine to update the process state with any new information after finishing the task. Usually there is one datamapping definition that is used to perform a datamapping after a task.
The last step is to update the datamapping we've just selected in the task mapping. Since a precondition has been added, the process should know the value of this precondition after each finished task. Open the mapping, and map the precondition attribute from implementation to the process module.
6. Task exits
Manual tasks can have two exits: commit and rollback.
- A commit will mean the task has been completed successfully, the new case state will be stored in the database and the task will be marked as completed. When the task is completed, it may become relevant again. If so, a new task instance (task-id) will be stored in the database.
- A rollback will abort the task and save nothing. The task will be reopened, so it can be picked up again.
Modelling an automatic-task
1. Determine (sub)goals
Similar to the user-task, the task you want to add probably serves a purpose in getting closer to the goal. So it can make sense to start with modelling what you want to achieve with the task. Does the data I add to the case make a milestone true? Does it add to one of the subgoals of making a milestone true? This can be added in the Process domain module (in the implementation). For example to complete the milestone that the decision has been communicated, the system should notify the applicant. The fact that the notification (email) has been sent could be one of the subgoals, and can be added on the rule to check whether this milestone is true.
2. Add task precondition
Similar to the user-task, we should determine when our new task is relevant for a user to execute. Since we only want to bother users with necessary actions when it is needed to reaching the case goals, we should determine when it is relevant. This can also be added to the Process domain module (for example in a "Preconditie.<TaskName>" attribute). The actual rule for this added boolean can be modelled in the Process domain derivation module, since this module can access the case-dossier. In our example, the task of notifying the applicant could be relevant when the decision has been made (and confirmed), and the notification has not yet been sent.
One important difference to the user-task, is that the system-task is being executed by the system with almost no delay. So be careful at determining the precondition, since a loop is easy to make. When, for example, the result of the task is not used in the logic of the precondition well, it could be that the precondition remains true after the first try, or second try, or third try, etc. It is customary to check whether the service (like sending the email) went ok, but is also tried once. Some pattern like, adding an attribute in the dossier: "applicantNotified" which is unknown by default, but is set to FALSE when something went wrong. In this situation when it is unknown, the automatic task is relevant, when it is TRUE the (sub)goal has been met, and when it is FALSE, you should recover from some form of error.
Note that the Case-Engine determines in each step which precondition is true and which is false. Make sure the same task is not repeating itself when you don't want to, by setting up your derivation well. Also keep in mind error situations. When the service responded, but did not lead to the goal of the task, you could choose to derive the precondition to be false, and add a second (manual) task to recover.
When a technical exception occurs, the precondition will not be predetermined. The message will enter the retry mechanism, so tried for example 3 more times, afterwards, it will be sent to the DCM-Maintenance-APP for manual recovery.
3. Add task implementation
In the task implementation module, a flow can be added where the actual work is done by the system. The complete dossier is retrieved at starting a task. When the task is completed, the new case-dossier state is sent to the Case-Engine to be processed. The dossier is retrieved and sent using the aggregate model of the case-dossier, so make sure this definition holds all information.
case-dossier is shared between Case-Engine and Runtime by its aggregate definition. Make sure the models loaded in the Case-Engine and Runtime share exactly the same aggregate definition.
The task implementation should not contain any user-interaction, so no pages. The task is performed by a Blueriq Runtime (read from the queue)
After the user-interaction, the flow can end with a "Commit".
Most likely is the commit, where the altered dossier is sent back to the Case-Engine, where the Case-Engine stores it, and determines what to do next.
When no exit is reached (due to a technical exception for example), the task exits with an exception. The lock remains (since the task should processed before anything else), and no data is stored. The task is retried using the DCM-Maintenance-APP, after a few retries it will be added to the failed events list in the DCM-Maintenance-APP for a maintainer to be fixed.
The Rollback exit aborts the task for you, which is strange in the context of an automatic task, since it will not be retried until some other action has taken place. So the Rollback should be avoided using automatic tasks.
4. Add task definition in the process
Since the implementation side is finished, we can now turn to the process module. A task definition should be created using a sensible name (this is for example seen in the timeline later on). The task should be modeled on a process line, so make sure the "ok" event is modelled in the task definition.
The created task should be added to a sub process, with one starting point, the task, and one exit. The sub process should be added to the right phase as an ad-hoc process, with the precondition of the task added as a precondition. This means, when the sub process becomes relevant, the automatic task is triggered, finished, and determined what is relevant afterwards.
5. Connect implementation and process using the configuration module
Now that the implementation has been created, and the process has been modified, we should connect the two models. In a configuration module we should extend the task mapping with our new task. By doing this, the Case-Engine knows which flow to start when the user tries to start the task.
At the task mapping, the task from the process is connected to the flow implementation. Map the taskflow exit event to the "ok" event in the task definition, so the process knows what to do after task execution. Then we should select the right datamapping. This datamapping serves for the Case-Engine to update the process state with any new information after finishing the task. Usually there is one datamapping definition that is used to perform a datamapping after a task.
The last step is to update the datamapping we've just selected in the task mapping. Since a precondition has been added, the process should know the value of this precondition after each finished task. Open the mapping, and map the precondition attribute from implementation to the process module.
6. Task exits
Automatic tasks can have two exits: commit and exception. Please use the flow_type "AutomaticTask" when modelling an implementation of an automatic task.
- A commit will mean the task has been completed successfully, the new case state will be stored in the database and the task will be marked as completed. When the task is completed, it may become relevant again. If so, a new task instance (task-id) will be stored in the database.
- An exception will abort the task with an exception. The lock will remain until the task is processed successfully. It will be retried (when configured in the DCM-Maintenance-APP), or it will be stored as a failed message. Additional data can be stored in the exception, which can be used when the task is being retried.
What is happening?
The models have been changed correctly, but the task is not visible in the list for the right user.
Make sure all steps above have been performed correctly. Notice that the dossier + rules derive the case-state, but the case-state itself is stored in the database. There are a few steps that can help debugging your issue:
- make sure the case-dossier + rules lead to a precondition "true" Do you expect the task to be present in the current situation? Then opening the case, you can check the value of the precondition if the case should have been evaluated now (using the profile editor)
- if the precondition is true, we can move on the the process. Check the process state in the DCM-Maintenance-APP for your case. There should be an attribute containing Precondition.<TaskName> set to TRUE. When there is no attribute, this means the attribute was not available during the last evaluation (last task execution form example).
- Check the task-table in the DCM-Maintenance-APP. There should be a task set to "open" with the name modelled in the process. When there is not, something might have gone wrong adding your task to the process definition
- When all of the above are available, it could be that the user is not authorised to perform the task, therefore not seeing the task.
Process state is derived and set after each task execution. Make sure when the model is changed, to run some task on a case you want to see the changes on. This can be achieved by throwing a message event and performing an automatic task afterwards (with either a dossier migration, or an empty flow to just make sure the new dossier + rules are sent to the Case-Engine). When developing without any production cases, it is probably easier to just create new cases after reloading the models.
The models have been changed, process elements are deleted, the cases that were already created are not working anymore!
The process state of any case in progress is stored in the database. This can be reviewed in the DCM-Maintenance-APP:
The Case-Engine looks for ALL tasks with the status open or started when evaluating what to do next. So make sure you only delete elements when there are no cases left in your production environment with this task/listening event/timer with the status open or started. When you do, the Case-Engine cannot determine a next state and does not process your action. When you want to delete an ad-hoc task, make sure you set the precondition to false first, and migrate all cases. When there is no case left in production with an open task-status, you can delete the task from your process definition.