Workflow Events: General Information

A workflow event is an event that can be used to trigger a transition. Workflow events introduce cross-graph interactions and are similar to .NET events.

In this chapter, you will learn how to use existing events and create your own events in a custom workflow by using Workflow API.

Learning Objectives

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

  • Explore the code of the predefined workflow to find the event
  • Create a custom event handler
  • Bind the event handler to an event
  • Register an event handler in a particular state
  • Create a transition triggered by an event
  • Create a custom event
  • Override a method to fire the event

Applicable Scenarios

You use workflow events when you need to implement cross-graph interaction for forms that use workflows.

For example, in MYOB Acumatica, most of the predefined data entry forms have workflows defined. If the workflow of a custom form is dependent on transitions that happen on an existing MYOB Acumatica form, you can implement the interaction between workflows by using workflow events.

Defining of a Workflow Event

To define a workflow event, you need to define the following entities:

  • The event itself.

    An event is an object in code that corresponds to a DAC and has a name; it can be defined anywhere. However, we strongly recommend that you place it in a nested class in the DAC the event relates to. To define a new event, you need to create a new class that inherits from the PXEntityEvent.Container class. As the type parameter of the PXEntityEvent class, you specify the name of the primary DAC of the form where the event is fired. In the class, you create the event as a field of the PXEntityEvent type.

    This class has one or two generic parameters. The first parameter must be the same one that you used in the class declaration. This parameter shows that this event happens on records of the specified DAC. Also, you can use the second parameter to provide additional information about the event that occurred. The second parameter usually holds an additional DAC record that corresponds to the event.

    An example of an event declaration is shown in the following code. Among other events, the InvoiceLinked event is declared. This event shows that the invoice, which is defined as the second type parameter, is linked with an SOOrderShipment record.

    public partial class SOOrderShipment : PXBqlTable, IBqlTable{
      public class Events : PXEntityEvent<SOOrderShipment>.Container<Events>{
        public PXEntityEvent<SOOrderShipment, SOShipment> ShipmentLinked;
        public PXEntityEvent<SOOrderShipment, SOShipment> ShipmentUnlinked;
        public PXEntityEvent<SOOrderShipment, SOInvoice> InvoiceLinked;
        public PXEntityEvent<SOOrderShipment, SOInvoice> InvoiceUnlinked;
      }
    }
    
  • An event handler.

    You can define an event handler as a member of a graph or a graph extension. You do this by declaring a member of the PXWorkflowEventHandler type, as the following code example shows.

    public PXWorkflowEventHandler<SOShipment, SOOrderShipment, SOInvoice> OnInvoiceLinked;
    public PXWorkflowEventHandler<SOShipment, SOInvoice> OnInvoiceReleased;

    In the first type parameter, you need to specify the primary DAC to indicate that this handler is used to manipulate the records of the primary DAC. In the second type parameter, you need to specify the DAC where the event for this handler is defined. The optional third parameter can be used to further restrict the handler to only those events that provide additional information for a record.

  • The binding of the event handler to an event.

    To bind the event handler to an event, in the screen configuration, you call the WithHandlers method, and add the handler by using the Add method, as the following code example shows.

    .WithHandlers(handlers =>
    {
      handlers.Add(handler =>
      {
        return handler
          .WithTargetOf<SOOrder>()
          .OfEntityEvent<SOOrder.Events>(e => e.ShipmentCreationFailed) //PXEntityEvent<SOOrder>
          .Is(g => g.OnShipmentCreationFailed) // PXWorkflowEventHandler<SOOrder, SOOrder>
          .UsesTargetAsPrimaryEntity()
          .DisplayName("Shipment Creation Failed");
      });
    })

    In the OfEntityEvent method, you need to select the workflow event declared in the DAC. The event must match the type specified in the WithTargetOf method and the type of the current screen configuration context. In the Is method, you need to specify the event handler that matches the event signature. By calling the UsesTargetAsPrimaryEntity method, you specify that the workflow should use the record on which the event is fired. For a cross-graph execution, you need to call the UsesPrimaryEntityGetter method instead and select the DAC object that should be used to get the source document for the event.

  • The registration of an event handler in the workflow state where the corresponding event should be fired.

    To register an event handler in a workflow state, you call the WithEventHandlers method in the state definition, as the following code example shows.

    flowStates.Add<State.completed>(flowState => {
      return flowState
        ...
        .WithEventHandlers(handlers =>
        {
          handlers.Add(g => g.OnInvoiceUnlinked);
          handlers.Add(g => g.OnInvoiceCancelled);
        })
    });

Firing of a Workflow Event

After you have declared a workflow event, you need to fire it. To fire a workflow event, you select an event from the list of events declared in the class derived from the PXEntityEvent class and call the FireOn method. In the FireOn method parameters, you provide the graph that will be used as a host for event execution, as well as the instances of DACs that were declared in the PXEntityEvent. An example of firing the InvoiceLinked is shown in the following code.

public static SOOrderShipment LinkInvoice(this SOOrderShipment self, 
    SOInvoice invoice, PXGraph graph)
{
  if (self is null || invoice is null)
    return self;
  
  self.InvoiceType = invoice.DocType;
  self.InvoiceNbr = invoice.RefNbr;
  self = (SOOrderShipment)graph.Caches<SOOrderShipment>().Update(self);
  
  SOOrderShipment.Events
    .Select(e => e.InvoiceLinked)  //PXEntityEvent<SOOrderShipment, SOInvoice>
    .FireOn(graph, self, invoice);
  
  return self;
}

In the code above, the InvoicedLinked event is selected from the list of SOOrderShipment events and the FireOn method is called. As parameters, the following have been provided: the instance of the graph that will be used as a host for event execution, and the instances of SOOrderShipment and SOInvoice DACs that are linked with each other. The signature of the InvoiceLinked event restricts the FireOn method to an overload that has these exact parameters.

Triggering of a Transition by an Event

To define a transition that is triggered by an event, you specify the event handler name in the IsTriggeredOn method of the transition definition, as the following code shows.

transitions.AddGroupFrom<States.completed>(ts =>
{
  ts.Add(t => t.To<States.linked>().IsTriggeredOn(g => g.OnInvoiceLinked));
});