To Define a Composite State in a Workflow
About Composite States
A composite state, which can also be referred to as a parent state, is a state that contains an ordered sequence of nested states and the transitions to and from these states. With composite states, you can specify common settings (actions, fields to be updated, and transitions) for a group of states only once—in the composite state that includes these states.
For each of the states inside the composite state, you can do the following:
- Specify whether this state should be skipped by using a skip condition
- Specify a transition to the next state inside the composite state instead of a transition to a specific state
- Specify a transition to the state that is the next one after the composite state if this composite state is itself a nested state in another composite state
- Override the settings inherited from the composite state, if needed
These capabilities make workflow customization much easier. You do not need to explicitly specify target states for transitions. Thus, if you add or remove states in the workflow or change the order of states, you do not need to modify all the affected transitions.
A record created on a form cannot be in a composite state; it can only be in one of its nested states. When a record enters any nested state in a composite state, the system checks the skip condition specified for this state, if one has been defined. If the condition is fulfilled, the system does the following for the current state:
- Assigns the default values for the fields as specified on the State Properties tab of the Workflow (Tree View) page for the form if the current state is the initial state of the workflow
- Does not check the fields that should be updated when the record enters the state and leaves it
- Does not check any of the workflow settings, and moves the record to the next state inside the composite state
If no skip condition is specified, the system uses the typical workflow for this state. This means that the transitions are triggered only by actions or event handlers, and the system does not check the skip condition again while the record remains in this state.
Implementing a Composite State
You add a composite state inside the AddFlowStates method by using the AddSequence method. As the type parameter of the method, specify the name of the composite state. In the lambda expression provided for the AddSequence method, you can add states that are a part of the composite state using the WithStates method. The states in the composite states are declared the same way as they are declared in the AddFlowStates method.
To specify that a state should be skipped in the composite state, call the IsSkippedWhen method in the state definition. The condition depending on which the state should be skipped is specified in the parameter of the IsSkippedWhen method.
Example of a composite state declaration is shown in the following code.
.WithFlowStates(fss =>
{
fss.Add(initialState, flowState => flowState.IsInitial(g => g.initializeState));
fss.AddSequence<State.HoldToBalance>(seq =>
seq.WithStates(sss =>
{
sss.Add<State.hold>(flowState =>
{
return flowState
.IsSkippedWhen(conditions.IsNotOnHold)
.WithActions(actions =>
{
...
});
});
sss.Add<State.creditHold>(flowState =>
{
return flowState
.IsSkippedWhen(conditions.IsCreditHoldChecked)
.WithActions(actions =>
{
...
});
});
sss.Add<State.balanced>(flowState =>
{
return flowState
.WithActions(actions =>
{
...
});
});
}));
}
In the code above, the HoldToBalance
composite state is declared. The
composite state includes the hold
, creditHold
, and
balanced
states. The hold state is skipped when the
IsNotOnHold
condition is true. The creditHold
state is
skipped when the IsCreditHoldChecked
condition is true. The
balanced
state is never skipped.
Declaring a Transition for a Composite State
You can define the following transitions involving a composite state:
- A transition from any state of a workflow to the composite state
- A transition from a composite state to any state of a workflow
All such transitions are inherited by states of a composite state.
- A transitions from a specific state of the composite state to any state of the workflow
- A transition from a composite state to itself so that the workflow engine can search for a
proper state and check conditions again
To define such transition, in the Add method for a transition, specify the target state that is the same as the source state.
- A transition from one state of a composite state to the state defined after it.
To define such transition, in the Add method for a transition, call the ToNext method.
- If a composite state is defined inside another composite state, you can define a transition from a state in a child composite state to the next state in the parent composite state. To define such state, in the Add method for a transition, call the ToParentNext method.
Example of the transitions including a composite state are shown in the following code.
.WithTransitions(transitions =>
{
transitions.AddGroupFrom(initialState, ts =>
{
ts.Add(t => t.To<State.HoldToBalance>()
.IsTriggeredOn(g => g.initializeState)); // To composite state
});
transitions.AddGroupFrom<State.HoldToBalance>(ts =>
{
ts.Add(t => t
.To<State.HoldToBalance>()
.IsTriggeredOn(g => g.OnUpdateStatus)
.When(conditions.IsARInvoice));
ts.Add(t => t
.To<State.open>()
.IsTriggeredOn(g => g.OnReleaseDocument)
.When(conditions.IsOpen));
}
}
In the code above, the following transitions are declared:
- A transition from the initial state to the
HoldToBalance
composite state - A transition from the
HoldToBalance
composite state to itself - A transition from the
HoldToBalance
composite state
Updating an Existing Composite State
You can customize a composite state defined in source code of MYOB Acumatica. To update a composite state, use the UpdateSequence method in the WithFlowStates method. In the WithStates method of the UpdateSequence method, you can add, update, or delete a state by using Add, Update, or Delete methods respectively. To specify the location of state in the updated composite state, use the PlaceAfter method.
An example of an updated composite state is shown in the following code.
.WithFlowStates(states =>
{
states.UpdateSequence<State.HoldToBalance>(seq =>
seq.WithStates(sss =>
{
sss.Add<State.pendingApproval>(flowState =>
{
return flowState
.IsSkippedWhen(conditions.IsApproved)
.WithActions(actions =>
{
...
})
.PlaceAfter<State.hold>();
});
}));
})
.WithTransitions(transitions =>
{
transitions.AddGroupFrom<State.pendingApproval>(ts =>
{
ts.Add(t => t
.To<State.HoldToBalance>()
.IsTriggeredOn(g => g.OnUpdateStatus));
ts.Add(t => t
.ToNext()
.IsTriggeredOn(aproveAction)
.When(conditions.IsApproved));
ts.Add(t => t
.To<State.rejected>()
.IsTriggeredOn(rejectAction)
.When(conditions.IsRejected));
});
});
In the code above, the pendingApproval
state is added to the
HoldToBalance
composite state and placed after the hold
state. Three transitions are added from the pendingApprovel
state.