This is a guest blog post from Dmitri Riz, the director of InfoStrat's Microsoft Dynamics Practice.
We are all familiar with Business Process Flows (BPF) in Dynamics 365 – they’ve been around for a while. The set of chevrons at the top of the single record form typically represents major phases in the record’s lifecycle.
|
Phases of record lifecycle or BPF |
The problem
When starting a new project, we usually end up designing and building a set of BPFs for the most important records in our system. However, one of the first questions we often hear from users after the new project goes live is “Is there a way to hide BPF area of the form?”. The real-life usage of BPFs sometimes falls short of the promise to make users' lives easier by guiding them through a business process.
The reason many users often fail to see much utility in BPFs is that a typical BPF is weakly connected to actual processes users go through when working with data and often fails to serve as an enforcer of institutional rules. A purely visual guide with a random set of fields under each stage’s chevron quickly becomes inadequate.
One of the main ingredients that is often lacking is the missing or unenforced connection between a record’s status value (which is typically mapped to a lifecycle stage) and the selected stage in a BPF. For example, if there are Draft and Review values in the status field, the ideal system should:
- have Draft and Review BPF stages
- allow the record to automatically acquire Review status if a user moves the record from Draft to Review by clicking on the Next button in the BPF
- automatically change the active BPF stage to Review if another process (workflow, integration, import etc.) changes the record’s status to Review
- prevent edits to Draft-specific fields (e.g. draft status date) when the record is no longer in Draft status
One of our recent projects included an intake process for grant applications, whereby the client organization hires external vendors to review and process applications. External vendor personnel often have limited knowledge of the application lifecycle stages used by the client, and many of them are unfamiliar with Dynamics 365.
The solution uses an enhanced BPF to implement a guided process where an application reviewer would be able to fully rely on the BPF to guide a user through the application’s lifecycle.
The key decision in this process was to map BPF stages to the application status values and only include a mandatory decision field in each BPF stage. Automated processes where used to synchronize BPF stages with application status.
|
Mapping BPF stages to application status |
During the process, a user reviews the application data on the form and then decides (e.g. approve, reject, send for more information etc.) by selecting a value in a decision field.
Each BPF stage therefore contains only stage-specific decision fields.
The application lifecycle diagram, depicted below, is not very complex, but it has multiple forks; where some forks can lead to prior stages in the process, creating loops.
|
Application lifecycle flowchart |
For example, Completeness Review stage (3rd box from the top), can lead to Household Eligibility Review or Request for Additional Info. This latter stage can create a closed loop by sending the process back to Completeness Review stage.
The challenge in implementing this flow is that once the BPF flow is forked, you cannot continue from the forked branch to the main branch - a run-time error will result.
In another example, if we imagine ‘Completeness’->’Household Eligibility’->’Project Set-up’ as the main branch of the process (as it belongs to a happy branch that can lead to an approval), if we go to ‘Request for Info’ fork in the BPF, there is no built-in way to go to ’Household Eligibility’ after that.
If you look at the BPF designer, the fork is created using a condition:
|
Creating a fork in BPF designer |
And the decision field for the Completeness Review stage:
|
Added decision field |
The Accepted value of the decision field leads to the next stage on the main branch and Not Accepted starts a loop.
As mentioned earlier, BPFs do not allow you to reconnect branches. In the designer, this leads to a very cumbersome BPF:
|
BPF designer |
All the suddenly terminating branches, except the top one, are there to support forks and loops.
The solution
To avoid potential confusion between looking at the field value and the BPF area, map the record status values to BPF stages.
The main data element displayed in a BPF’s stage is a decision field which drives the flow to the next stage. This is strictly speaking not necessary for linear flows but the decision field is helpful to serve as an indicator of an acceptance of the current state and an additional audit mechanism
|
Decision field as indicator of current state |
One workflow per stage (status value) is required to implement the decision gate selection. E.g. for the process described here there’s a workflow for Draft, Pending Doc, Completeness, and all other possible stages. This workflow will be triggered by the change in the corresponding decision gate field and its main task is to set the matching record status. When a user makes a choice in ‘Household Eligibility’ stage shown above, this workflow will run and will set the appropriate status based on the decision taken.
|
Workflow to set status |
If possible, this workflow should be synchronous to ensure that if a user clicks on the next stage button in the BPF area, the stage-exit workflow will update the UI to the next status on save.
The next piece of the puzzle is to solve the ever-branching BPF. While we cannot reconnect a branch to the main trunk, we can set it programmatically by:
- First resetting the flow to the last stage preceding the fork
- Then pushing it forward to the appropriate stage, as per business logic.
For example, looking at the flow diagram, if, after ‘Completeness Review’ (3rd from the top) we go to ‘Request for Info’ (the right fork) and from this go to ’Household Eligibility’ (the left fork, on what we imagine as a main branch), we will need to somehow:
- Return to the fork (Completeness Review)
- Set the appropriate stage on the main branch (to ’Household Eligibility’)
A synchronization workflow that is triggered by the Status field of your record (Application Status in my scenario) can be used to accomplish this task.
|
Workflow to reconnect to main branch |
This workflow is using custom workflow activities Dynamics-365-Workflow-Tools (https://github.com/demianrasko/Dynamics-365-Workflow-Tools ) to programmatically set BPF stages. It examines the Application Status value and sets the stage appropriately if it is different than expected (for example if status is set directly by a user, via integration or import).
In the screenshot below, if new status is Completeness Review, and the stage is something different, the workflow will:
Return to the stage on the main branch that is guaranteed to precede the fork (Pending Documents);
Set the correct stage (Completeness Review)
|
Workflow to set status to correct stage |
The last trick is to add a little JavaScript to disable all decision gate fields that do not correspond to the current active BPF stage. For example, if the current stage is Completeness Review, only the Completeness Review decision option set should be enabled; all other fields on all other BPF stage chevrons should be disabled. The same code can also disable other stage-specific fields (such as additional qualifying info; dates, etc.).
This is done by setting up a static collection of all fields and dynamic collection of fields that should be enabled based on a stage:
var status = Xrm.Page.getAttribute('statuscode').getValue()
controlGates = ['cdr_additionalinfoaccepted', 'cdr_completenessrevaccepted', 'cdr_eligibilityreviewdecision', 'cdr_fundingavaildecision', 'cdr_glodecision', 'cdr_ineligibleappealdecision', 'cdr_projectsetupdecision'],
enabledGates = [];
switch (status) {
case 754310001: // compl review
enabledGates = ['cdr_completenessrevaccepted'];
break;
case 754310002: // Eligibility review
enabledGates = ['cdr_eligibilityreviewdecision'];
break;
// etc.
At the end of the case, we disable all members of controlGates collection except those that made it into enabledGates.
Bonus: in addition to one main synchronization workflow, and one workflow for each stage exit, the solution also uses one workflow per stage initiation. Stage-start workflows are called from the main synchronization workflow and are used to set conditions for stage entry. For example, since the business process may contain time-sensitive loops, when we initiate a stage, we might need to reset loop timers.
Concerns
While all of this works great, there is quite a lot of orchestration required to implement just this one flow. While it is tricky due to its loopy nature, it is not very complex. Many business process flows may be significantly more complex.
The other issue is that the synchronization workflow depends on matching stage names. If a BPF gets redesigned, or stages added or renamed, the synchronization workflow would require modifications to include changed stage labels.