Order cancellation with conditional gateways and exception handling


Up to now, we have been implementing what is more-or-less a "straight-line" process from beginning to end. Realistic business processes are rarely like this, though. If there were never any dynamic conditions to handle, there wouldn't be very much point in using the process designer to model it out.

We have already built the Orders View that allows users to set the status of an Order as Cancelled. We would probably not think of this as the expected part of the life-cycle of an order, though - it is something unexpected. We will combine two powerful techniques - Exclusive Gateway and Business Error (see Handling-errors-in-a-process) - to create one implementation of this requirement.

Using an Exclusive Gateway for conditional flow

Let's say that while waiting for the Order state to become In Progress, somehow, its Status field value became Cancelled instead. We need to handle that. The simplest way to do that is to put a conditional check right after it wakes up.

Luckily, we have designed the process so far that when there is a "wait" going on, it "wakes up" because the Status value is "greater than" where it was expected to be - Submitted at this point - so we know that it will, in fact, wake up. We were wise to have the most general condition possible there, because Status >= In Progress covers Cancelled as well as In Progress.

We then can put an Exclusive Gateway element in the flow, so it can take alternate flows depending on whether it is Cancelled or something else (like In Progress or Delivered).  You might think that the easiest thing to do here is just flow to a Send Message activity and terminate the process, something like this:

 image-2024-4-14_20-3-13.png

All of the conditional logic is built into the flows coming out of the Exclusive Gateway, where each can have a Condition defined (the Exclusive Gateway element itself does not have any condition).  At least one of the flows must have no Condition defined; this is called the default flow. In this case, the default flow is to continue the process with Notify In Progress.

To create the Condition that will cause it to flow to Notify Submitter of Cancellation:

  1. Select the flow from the Exclusive Gateway to Notify Submitter of Cancellation.
  2. Click on the Condition to bring up the Edit Expression dialog.
  3. Create the expression in a similar way to the Completion Criteria of User Task, making sure to select the Status field from Activities > Wait for In Progress. This time, we are looking for an exact match of Cancelled, since this flow is specifically to handle cancellation. 

image-2024-4-14_20-28-41.png

The flow properties now look like this:

image-2024-4-14_20-5-15.png

The default flow coming out of the gateway now represents every situation where the Condition is false, including all the other Status values greater than Submitted

This works, and can be tested. However, let's consider the implication of this approach. This "brute force" approach to handling cancellation will require that the handling of every condition will be by a flow to either duplicated handling (like notifications), or, criss-crossing lines that will be become difficult to maintain. In this case, you can see that you will actually have to include the Send Message activity, or another flow to it, from every place where a User Task can cause the process to wait.

What would be nice is to have the ability to just jump out of the process whenever cancellation has occurred, and handle it in one place. We can do exactly that using the error handling capability of process designer. Perhaps more importantly, we can use this case to understand how to consolidate error handling in one place in general.

Throwing a Business Error when the Status becomes Cancelled

Let's imagine that cancellation should interrupt the process, but you would like the handling of it to be elsewhere. We can create a simple example of this by the following strategy:

  1. Instead of terminating the process, a state changed to Cancelled should raise something called a Business Error.
  2. We can write a "wrapper" process that will "catch" these errors, and handle them.

Conceptually the process runtime execution is like this (remember you can zoom in on this diagram by clicking on it).

Cancellation - wrapper process conceptual diagram.PNG

A few interesting things to note here:

  • Throwing the error, through an element called Error End terminates the current flow, but does not terminate the process.
  • The error can be handled by another event called Error Boundary, which can turn execution back into a "normal" flow to handle it however you like (retry, send a message, do something else, and so on).
  • The Error Boundary—much like the Timer, has to be decorating some element that represents a "scope" of execution where the Error End was executed. This could be a Sub-Process. We have chosen to make it a Call Activity that runs the Order Lifecycle process.

Let's build this, starting from the Order Lifecycle process.  The first thing we will do is replace the Notify Submitter of Cancellation activity with an Error End and terminate the flow.  The specific steps are:

  1. Drag in an Error End element from the Events section of the Palette
  2. Select the Cancelled flow coming out of the Exclusive Gateway to Notify Submitter of Cancellation and drag the end to flow to the Error End element instead.
  3. Delete the Notify Submitter of Cancellation activity - we are going to handle this in the wrapper now.
  4. Configure the properties of the Error End to have it throw a Business Error called Cancelled.


The Order Lifecycle process should look like this. This is how we configure the Business Error with a simple text name (we will need to use this name later, so something like an identifier is appropriate).

image-2024-4-14_20-8-46.png

Handle cancellation after delivery

We are assuming this would be allowed while an Order is In Progress (you could make a case that we should not allow this, but for this tutorial, we will show how the wrapper will handle it for multiple cases).

Repeat this same technique as above by creating an Exclusive Gateway after Waiting for Delivery that flows to the same Business Error.

The error flows now look like this:

image-2024-4-14_20-11-15.png

Now, you can save and close the process and create the "wrapper".

Handling cancellation in the wrapper process

Create the new process, called Order Lifecycle Wrapper. Make sure it has the Order record input parameter defined, so we can pass it down to the Order Lifecycle process.


To create this is pretty simple, and is mostly laid out in the conceptual diagram above. Here is a step-by-step:

  1. Configure a Call Activity to invoke Order Lifecycle. The entire Order input parameter can be passed cleanly down.

    image-2024-4-14_20-13-59.png
  2. Drag in an Error Boundary event from the Palette and attach it to the edge of the Call Activity, much as we did the Timer earlier. Configure it with Add/Remove Errors to Catch a Specific Business Error. You should see  Cancelled in the list of Error End Event.

    image-2024-4-14_20-14-58.png
  3. Drag in, and configure, a Send Message activity to handle the error by notifying the submitter, and connect the flow from the Error Boundary to it.
  4. Create the missing flows to the End Event to make the process legal.

The Order Lifecycle Wrapper should now look like this.

image-2024-4-14_20-17-30.png

Updating the rule

If you thought you were done now, there is one thing you may have forgotten. What causes these Processes to be run? At the start of this Module, you created a Rule that fires after Order creation, and it explicitly starts the Order Lifecycle Process. Don't forget to go update that Rule to invoke the Order Lifecycle Wrapper instead.


image-2024-4-14_20-18-55.png

That's it! You have now set up the logic needed to handle errors thrown by the lifecycle process. 

Testing

You can test this exactly as you have tested the other enhancements to this process: changing the Status of the Order and observing what happens. You should notice that when an Order is active, you will see both Order Lifecycle and Order Lifecycle Wrapper processes in the Processes > Manage Processes console.

Challenge

  • Branch your process unconditionally (execute multiple flows that then join up) to produce two notifications in parallel.
  • In the Order Lifecycle Wrapper, instead of hard-coding the name of the Order Lifecycle to be invoked, configure it to get the name of the process to call from an expression.
  • A new requirement has come in. Now, once the Order is In Progress, you will not be allowed to change it to Cancelled using UI. 

What we learned

You learned many cool and powerful techniques in putting in this particular feature:

  • An Exclusive Gateway can be used to make a conditional branch in a flow. The Condition is specified on all flows except one (the default flow). 
  • The Error End event can be used to terminate a flow without terminating a process.
  • The Error Boundary event can "catch" an Error End without a flow being drawn between them, and handle it with whatever logic is appropriate.
  • One process can call another one using Call Activity. The process to call can be hard-coded or dynamically determined.
  • Rules can be used to validate data submitted in the user interface.



 

Tip: For faster searching, add an asterisk to the end of your partial query. Example: cert*