How do I trigger business logic when a flowfield changes?

Flowfields play a central role in Business Central, and a common development request is to react when a flowfield changes, but how do you actually do that? Check out the video:

https://youtu.be/dmnqupfi4bI

A common question from both new and experienced Business Central developers is: “How do I trigger business logic when a FlowField changes?” In this video, Erik demonstrates why FlowFields don’t work like regular fields and walks through the correct approach — subscribing to the event that modifies the source data behind the FlowField. The practical example: automatically blocking a customer for invoicing when their balance exceeds their credit limit.

Understanding FlowFields: They’re Not Really Fields

The first thing to understand is that a FlowField is not a stored field — it’s a just-in-time calculation. Nothing is ever written to the database for a FlowField, and no triggers fire when its value “changes.” Every time you open a page, refresh, or call CalcFields, the value is recalculated on the fly from the underlying data.

Take the Balance (LCY) field on the Customer Card as an example. If you look at its definition in the Customer table (table 18), you’ll see something like this:

field(...; "Balance (LCY)"; Decimal)
{
    CalcFormula = sum("Cust. Ledger Entry".Amount WHERE("Customer No." = FIELD("No."), ...));
    FieldClass = FlowField;
}

This field simply sums up amounts from the Cust. Ledger Entry table. It’s never stored, never triggers validation, and never fires any events. So you can’t subscribe to “when the balance changes” directly.

The Solution: React to the Source Data

Instead of trying to react to the FlowField itself, you need to react to the event that changes the source data. In this case, the balance changes when a customer ledger entry is inserted — and that happens during posting via Codeunit 12 (“Gen. Jnl.-Post Line”). This codeunit has been the heart of posting in Business Central (and Navision before it) for over 30 years.

If you browse the events exposed by Codeunit 12, you’ll find several related to customer ledger entries. The one we want is OnAfterCustLedgEntryInsert — it fires right after a new customer ledger entry is inserted, giving us access to the entry that was just created.

Tip: Finding Events with AL Code Outline

Erik recommends installing the AZ AL Dev Tools / AL Code Outline extension for VS Code. It gives you an excellent tree view of base application symbols, making it far easier to browse tables, codeunits, and their events.

The Implementation

Here’s the complete codeunit that subscribes to the posting event and blocks a customer when their balance exceeds their credit limit:

codeunit 50123 "Test"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Line", 'OnAfterCustLedgEntryInsert', '', true, true)]
    local procedure MyProcedure(var CustLedgerEntry: Record "Cust. Ledger Entry")
    var
        CustRec: Record Customer;
    begin
        if CustRec.Get(CustLedgerEntry."Customer No.") then begin
            CustRec.CalcFields("Balance (LCY)");
            if CustRec."Balance (LCY)" > CustRec."Credit Limit (LCY)" then begin
                CustRec.Blocked := CustRec.Blocked::Invoice;
                CustRec.Modify(true);
            end;
        end;
    end;
}

Step-by-Step Breakdown

  1. Subscribe to the event: The [EventSubscriber] attribute hooks into OnAfterCustLedgEntryInsert on Codeunit “Gen. Jnl.-Post Line”. The two true parameters are for SkipOnMissingLicense and SkipOnMissingPermission.
  2. Get the customer record: We use CustRec.Get() wrapped in an if statement rather than calling it directly. This is a deliberate defensive pattern — if for some reason a ledger entry references a customer that doesn’t exist, our code won’t be the thing that causes an error.
  3. Calculate the FlowField: We call CustRec.CalcFields("Balance (LCY)"). Because the customer ledger entry has already been inserted at this point, the calculation will include the new entry — giving us the current, up-to-date balance.
  4. Compare and act: If the balance exceeds the credit limit, we set the customer’s Blocked field to Invoice and modify the record.

Important Caveats

Be Careful with Database Operations in Event Subscribers

Modifying records inside an event subscriber can be dangerous. If the calling code (in Codeunit 12) has already loaded the Customer record and plans to modify it after your event fires, your Modify call could cause their subsequent Modify to fail — because they’d be working with a stale version of the record. Erik inspected the posting code and determined this is safe in this particular case, but it’s something you should always verify.

Watch Out for Performance with Batch Posting

If a process posts 100 entries for the same customer (e.g., from a journal), this event subscriber would fire 100 times — calculating the balance and potentially modifying the customer record on each iteration. For scenarios like that, you should look for a higher-level event in the call stack that fires once for the entire batch, rather than once per entry. The right event depends on the specific process and what you’re trying to achieve.

Testing It

In the video, Erik demonstrates the solution end-to-end:

  1. Set a customer’s credit limit to $2,000 (the customer already has a balance of ~$1,700 and is not blocked)
  2. Create a Sales Invoice for a desk at $1,500
  3. Post the invoice — the debugger hits the breakpoint in the event subscriber
  4. After CalcFields, the balance shows ~$3,000 (including sales tax), which exceeds the $2,000 credit limit
  5. The code sets Blocked to Invoice and modifies the customer
  6. Back on the Customer Card, the customer is now blocked — mission accomplished

Summary

You cannot directly trigger business logic when a FlowField changes because FlowFields are never stored and never fire events. Instead, identify the source of the FlowField’s data, find an appropriate event on the table or codeunit that modifies that source data, and subscribe to it. In this example, the Balance (LCY) FlowField sums customer ledger entries, so we subscribe to OnAfterCustLedgEntryInsert in the posting codeunit. Just remember to be defensive in your event subscriber code, be cautious about database modifications that could conflict with the caller, and consider performance implications when the event fires in high-volume batch scenarios.