acuamitca.com logo ACUAMITCA
~/blog/article

Understanding Acumatica Events

Acumatica Customization 13 mins read July 31, 2026

Understanding Acumatica Events

A comprehensive guide to graph events, field events, and execution sequences

The Acumatica Framework provides a robust event model that allows developers to inject business logic at specific points during data manipulation. Understanding these events and their execution order is essential for building reliable customizations and extensions.

⚡ Why Events Matter: Events are the primary mechanism for implementing business logic in Acumatica. They handle validation, calculations, default values, and cross-record interactions. [citation:10]


Event Handler Types

The Acumatica Framework provides two types of graph event handlers: classic and generic. [citation:1] Both work the same way, but generic handlers are recommended for modern development. [citation:1]

Classic Handlers

Use the DAC name, field name, and event type in the method name:

  • DACName_RowEventName - Row-level events
  • DACName_FieldName_FieldEventName - Field-level events

Example: CROpportunityProducts_RowSelected

✅ Generic Handlers (Recommended)

Use type parameters with the Events class:

  • _(Events.RowEventName<DAC> e) - Row-level
  • _(Events.FieldEventName<DAC, Field> e) - Field-level

Example: _(Events.RowSelected<CROpportunityProducts> e)

💡 Pro Tip: Use _ as the method name for generic event handlers – it's the established best practice. You can also create multiple handlers for the same event by adding a number: _2, _3. [citation:1]

Code Examples

Classic Handler:

protected virtual void CROpportunityProducts_RowSelected(
    PXCache sender, PXRowSelectedEventArgs e)
{
    // Business logic here
}

Generic Handler (Recommended):

protected virtual void _(Events.RowSelected<CROpportunityProducts> e)
{
    // Business logic here
}

Field-Level Generic Handler:

protected virtual void _(Events.FieldUpdated<CROpportunityProducts.contactID> e)
{
    // Business logic here
}

The PXCache and Data Events

All data-related events in Acumatica are raised by PXCache objects. The cache manages modified data records and provides the controller for basic operations like Insert(), Update(), Delete(), and Persist(). [citation:12]

Each cache object contains:

  • Modified Records: Data that has been Inserted, Updated, or Deleted but not yet saved
  • Controller: Methods that execute data operations and raise events

🔍 Important: PXCache instances are created and destroyed on each request. Modified records are serialized to the session between requests. This is why you need to handle events properly to maintain data consistency. [citation:12]


Event Execution Sequence

Events in Acumatica are raised in a specific, well-defined order. Understanding this sequence is critical for writing reliable business logic. [citation:2][citation:5]

Update of a Data Record

When a data record is updated, the following sequence occurs: [citation:2]

  1. Field Events - For each updated field in sequence:
    • FieldUpdating - Before the field value is changed
    • FieldVerifying - To validate the new value
    • FieldUpdated - After the field value is changed
  2. RowUpdating - Before the record is updated in the cache. At this point, e.Row holds the old version and e.NewRow holds the updated version.
  3. Actual Update - The data record is copied to the PXCache
  4. RowSelected - Only the updated record can be accessed through e.Row
  5. RowUpdated - After the update completes. e.Row holds the updated instance, while e.OldRow holds the previous version. [citation:2]

⚠️ Important: You can still stop the update during RowUpdating by throwing a PXException. Once RowUpdating completes without cancellation, the update proceeds. [citation:2]

Insertion of a Data Record

When inserting a new record: [citation:6]

  1. Field Events - For each field (including FieldDefaulting for default values)
  2. RowInserting - Before the record is inserted into the cache
  3. Actual Insert - The record is added to PXCache
  4. RowInserted - After the insertion completes

💡 Pro Tip: If you change data fields during RowInserting, no additional field events will be raised for those changes. Use RowInserted to add default detail records after the master record is inserted. [citation:6]


Graph vs. Attribute Event Handlers

Events in Acumatica can be handled in two different contexts: [citation:3]

📋 Graph Event Handlers

Defined as methods in a business logic controller (BLC) class for a specific DAC or field.

  • Apply to specific DACs and graphs
  • Can use both classic and generic syntax
  • Example: _(Events.RowSelected<SOOrder> e)

🏷️ Attribute Event Handlers

Defined in attribute classes derived from PXEventSubscriberAttribute.

  • Apply to all DAC objects or fields with the attribute
  • Implement IPXEventNameSubscriber interfaces
  • Example: IPXFieldVerifyingSubscriber and IPXRowPersistingSubscriber [citation:3]

Attribute Event Handler Example:

public class MyAttribute : PXEventSubscriberAttribute,
                           IPXFieldVerifyingSubscriber,
                           IPXRowPersistingSubscriber
{
    public virtual void FieldVerifying(PXCache sender,
                                       PXFieldVerifyingEventArgs e)
    {
        // Validation logic applied to all fields with this attribute
    }

    public virtual void RowPersisting(PXCache sender,
                                      PXRowPersistingEventArgs e)
    {
        // Pre-save logic applied to all DACs with this attribute
    }
}

Execution Order of Event Handlers

The order in which event handlers execute depends on the event type and how they're declared. [citation:8]

Bubbling Strategy (Bottom-Up)

Events added to the end of the collection execute from the base event handler up to the highest extension level. [citation:8]

  • FieldUpdated
  • RowSelecting
  • RowSelected
  • RowInserted
  • RowUpdated
  • RowDeleted
  • RowPersisted

Tunneling Strategy (Top-Down)

Events added to the beginning of the collection execute from the highest extension level down to the base handler. [citation:8]

  • FieldSelecting
  • FieldDefaulting
  • FieldUpdating
  • FieldVerifying
  • RowInserting
  • RowUpdating
  • RowDeleting
  • RowPersisting

Event Handlers with Additional Parameter

When using the PXOverride pattern with event handlers, you can control execution order explicitly. The delegate pattern allows you to decide whether to invoke the base handler: [citation:8]

protected virtual void FieldVerifying(PXCache sender,
                                       PXFieldVerifyingEventArgs e,
                                       PXFieldVerifying del)
{
    // Custom validation first
    if (e.NewValue == null)
        throw new PXException("Value cannot be null");

    // Invoke the base handler or skip it
    del(sender, e);
}

Controlling Field Event Sequence

Field events are triggered in the order fields are declared in the DAC. This can be controlled using the TabOrder property of the PXUIField attribute. [citation:7]

#region DocType
[PXDBString(3, IsKey = true, IsFixed = true)]
[ARInvoiceType.List()]
[PXUIField(DisplayName = "Type", TabOrder = 0)]
public override String DocType { get; set; }
#endregion

⚠️ Warning: Changing the TabOrder can alter the event execution flow and potentially break existing logic. Use this carefully when customizing. [citation:7]


Workflow Events

Acumatica also supports workflow events for cross-graph interactions. These are similar to .NET events and can trigger transitions between workflow states. [citation:11]

Key components of workflow events:

  • Event Declaration: Define events in a nested class within the DAC using PXEntityEvent
  • Event Handler: Declare a PXWorkflowEventHandler member in the graph
  • Event Binding: Bind handlers to events in the screen configuration
  • Firing Events: Call FireOn() on the event instance
// Event declaration
public partial class SOOrderShipment : IBqlTable
{
    public class Events : PXEntityEvent<SOOrderShipment>.Container<Events>
    {
        public PXEntityEvent<SOOrderShipment, SOInvoice> InvoiceLinked;
    }
}

// Firing the event
SOOrderShipment.Events
    .Select(e => e.InvoiceLinked)
    .FireOn(graph, self, invoice);

Best Practices

✅ Use Generic Event Handlers

They're easier to declare, use, and validate in Visual Studio. Use Acuminator to refactor classic handlers. [citation:1]

✅ Follow Naming Conventions

Use _ as the method name for generic handlers. Avoid creating multiple handlers for the same event when possible. [citation:1]

✅ Understand Execution Order

Know whether your event uses bubbling or tunneling strategy to ensure logic executes at the right time. [citation:8]

✅ Handle Exceptions Properly

Throw PXException with meaningful messages. Use e.Cancel to stop processing when needed. [citation:2]


Common Pitfalls to Avoid

❌ Modifying Cache During Events

Be careful when changing data during RowInserting or RowUpdating as it may skip field events.

❌ Assuming Event Order

Don't assume handlers execute in declaration order. Understand the execution strategy for your event type.

❌ Ignoring Performance

Avoid heavy operations in frequently fired events like RowSelected and FieldDefaulting.

❌ Not Using e.NewRow

During updates, always use e.NewRow for new values and e.Row for current cache values.


Conclusion

📌 Key Takeaway: Acumatica's event model is a powerful system for implementing business logic. Understanding the types of event handlers, the sequence of event execution, and the differences between bubbling and tunneling strategies is essential for building reliable customizations.

🔍 Further Reading: Explore the Acumatica Developer Network for detailed information on specific event types, the PXCache class, and advanced event handling patterns with PXOverride.