Composite States: General Info

This chapter will give you a comprehensive understanding of what a composite state is and how to implement and use it in a workflow.

Learning Objectives

In this chapter, you will learn how to do the following:

  • Implement a composite state
  • Define a transition for a composite state
  • Update a composite state in a predefined workflow

Applicable Scenarios

You implement a composite state when you have a set of states with common properties and you want to simplify the process of moving an entity though all these states.

Basics of 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:

  1. 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
  2. Does not check the fields that should be updated when the record enters the state and leaves it
  3. 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.

Implementation of a Composite State

You add a composite state inside the AddFlowStates method by using the AddSequence method. You specify the name of the composite state as the type parameter of the method. In the lambda expression provided for the AddSequence method, you can add states that are a part of the composite state by using the WithStates method. The states in a composite state 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, you call the IsSkippedWhen method in the state definition. In the parameter of the IsSkippedWhen method, you specify the condition that determines whether the state should be skipped.

The following code shows an example of the declaration of a composite state.

.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 following states:

  • hold, which is skipped when the IsNotOnHold condition is true
  • creditHold, which is skipped when the IsCreditHoldChecked condition is true
  • balanced, which is never skipped

Declaring of 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 a composite state.
  • A transition from a composite state to any state of a workflow.

    This transition is inherited by the states of the composite state.

  • A transition 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 the proper state and check conditions again.

    To define this transition, in the Add method for the transition, specify the target state, which is the same as the source state.

  • A transition from one state of a composite state to the state defined after it.

    To define this transition, in the Add method for the transition, call the ToNext method.

  • If a composite state is defined inside a parent composite state, a transition from a state in the child composite state to the next state in the parent composite state.

    To define this state, in the Add method for a transition, call the ToParentNext method.

Important: A composite state cannot be the initial state of a workflow. You must specify a transition from an initial state to the composite state.

The following code shows an example of transitions that include a composite state.

.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 to the Open state

Customization of an Existing Composite State

You can customize a composite state that has been defined in the 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 the Add, Update, or Delete methods, respectively. To specify the location of a state in the updated composite state, use the PlaceAfter method.

The following code shows an example of an updated composite state.

.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 pendingApproval state.