Finding the Right Events in Business Central: Payment Journals & Purchase Orders - CloudFronts

Finding the Right Events in Business Central: Payment Journals & Purchase Orders

When working with Payment Journals in Microsoft Dynamics 365 Business Central, one of the most common customization requirements is to trigger custom logic immediately after the user selects the Applies-to Doc. No..

In one of my recent client projects, the requirement was very specific:

As soon as a payment journal line is applied to an invoice (via Applies-to Doc. No. lookup), the system should automatically calculate amounts and create additional retained lines (VAT and IRIS).

Sounds simple, right?

The real challenge was finding the correct event that fires after the lookup completes and after Business Central internally updates the journal line fields.

This blog documents:

  • a. The problem statement
  • b. Why common events did not work
  • c. How Event Recorder helped identify the correct event
  • d. The final solution using a table event subscriber

Problem Statement

The client wanted the following behavior in Payment Journal:

  1. User opens a Payment Journal line
  2. User selects Applies-to Doc. No. and chooses an invoice
  3. Immediately after selection:
    • a. Applied amount should be recalculated
    • b. Retained VAT line should be created
    • c. Retained IRIS line should be created

The logic must run right after the lookup, not during posting and not on page validation.

Why Page Events Were Not Enough

Initially, it is natural to look for:

  • a. Page field OnValidate
  • b. Page actions
  • c. Page triggers

However, in this case:

  • a. The Applies-to Doc. No. lookup logic is handled internally by Business Central
  • b. Several system procedures update the journal line after the lookup
  • c. Page-level events fire too early or before all amounts are finalized

So even though the value was visible, the amounts were not reliable yet.

Using Event Recorder to Find the Right Event

This is where Event Recorder becomes extremely powerful.

Steps I Followed

  1. Open Event Recorder from Business Central
  2. Start recording
  3. Open Payment Journal
  4. Select a value in Applies-to Doc. No.
  5. Stop recording

The recorder captured a detailed list of:

  • a. Table events
  • b. Codeunit events
  • c. Sequence of execution

After analyzing the sequence, one event stood out.

The Key Event That Solved the Problem

The event that fulfilled the exact requirement was:

[EventSubscriber(
    ObjectType::Table,
    Database::”Gen. Journal Line”,
    ‘OnLookupAppliestoDocNoOnAfterSetJournalLineFieldsFromApplication’,
    ”,
    false,
    false)]
local procedure OnAfterLookupAppliesToDocNo(var GenJournalLine: Record “Gen. Journal Line”)

Why This Event Is Perfect

  • It fires after the Applies-to Doc. No. lookup completes
  • All related journal line fields are already updated
  • Amounts are reliable
  • Works consistently for Payment Journals

This is exactly the moment where custom business logic should run.

Implementing the Business Logic

Below is the simplified version of the logic implemented inside the subscriber:

local procedure OnAfterLookupAppliesToDocNo(var GenJournalLine: Record “Gen. Journal Line”)
begin
    GenJournalLine.GetUpdatedAmount();

if GenJournalLine.”Applies-to Doc. No.” <> ” then begin
        GenJournalLine.GetUpdatedAmount_(GenJournalLine);
        AppliestoDocNo := GenJournalLine.”Applies-to Doc. No.”;
        GenJournalLine.CreateRetainedVATLine(GenJournalLine, AppliestoDocNo);
        GenJournalLine.CreateRetainedIRISLine(GenJournalLine, AppliestoDocNo);
    end;
end;

What This Code Does

  • a. Recalculates the applied amount
  • b. Reads the selected Applies-to Doc. No.
  • c. Automatically creates:
    • 1. Retained VAT journal line
    • 2. Retained IRIS journal line

All of this happens immediately after the lookup, without waiting for posting.

Important Design Notes

  • a. This logic is lookup-driven, not posting-driven
  • b. Avoids heavy logic inside posting codeunits
  • c. Keeps user experience smooth and predictable
  • d. Works well with Preview Posting as no posting logic is involved

Key Takeaway

Finding the right event is often harder than writing the logic itself.

In scenarios where:

  • a. Standard UI actions trigger complex internal logic
  • b. Page events are not sufficient
  • c. Timing is critical
  • d. Event Recorder is your best friend.

This table event:

OnLookupAppliestoDocNoOnAfterSetJournalLineFieldsFromApplication

is a hidden gem for Payment Journal customizations involving Applies-to logic.

Another Real-World Case: Invoice Discount Recalculation on Purchase Orders

In the same project, we faced another tricky requirement related to Invoice Discounts on Purchase Orders.

The Problem

  • a. Invoice Discount % and Amount are maintained on the Purchase Lines subform
  • b. There is no direct page-level field on the Purchase Order header to hook into
  • c. Standard triggers like:
    • 1. OnAfterValidate
    • 2. Page field triggers
    • 3. Subform events

did not fire reliably when invoice discounts were recalculated by the system

This became an issue because the client wanted custom tax and withholding logic (IR, IS, Withheld VAT, Excise) to be recalculated immediately after invoice discount recalculation.

Why Page and Line Events Failed Again

Business Central recalculates invoice discounts using an internal codeunit:

Purch – Calc Disc. By Type

This logic:

  • a. Resets invoice discount amounts
  • b. Recalculates line values internally
  • c. Does not rely on page field validation

So once again, page-level and line-level events were too early or never triggered.

Finding the Right Event (Again) Using Event Recorder

Using Event Recorder, I traced the execution when:

  • a. Invoice Discount % was changed
  • b. Or discounts were recalculated automatically

This led to the discovery of another perfectly-timed system event.

The Key Event for Invoice Discount Scenarios

[EventSubscriber(
    ObjectType::Codeunit,
    Codeunit::”Purch – Calc Disc. By Type”,
    ‘OnAfterResetRecalculateInvoiceDisc’,
    ”,
    false,
    false)]
local procedure OnAfterResetRecalculateInvoiceDisc(var PurchaseHeader: Record “Purchase Header”)

Why This Event Works

  • a. Fires after the system resets and recalculates invoice discounts
  • b. Purchase Header and Lines are already updated
  • c. Ideal point to inject custom recalculation logic

Applying Custom Logic on Purchase Lines

local procedure OnAfterResetRecalculateInvoiceDisc(var PurchaseHeader: Record “Purchase Header”)
var
    PurchLine: Record “Purchase Line”;
begin
    PurchLine.SetRange(“Document Type”, PurchaseHeader.”Document Type”);
    PurchLine.SetRange(“Document No.”, PurchaseHeader.”No.”);

if PurchLine.FindSet() then
        repeat
            PurchLine.UpdateIRandIS();
            PurchLine.CalculateWithHeldVAT();
            PurchLine.CalculateIR();
            PurchLine.CalculateIS();
            PurchLine.CalculateExcise();

PurchLine.Modify();
        until PurchLine.Next() = 0;
end;

What Happens Here

  • a. Loops through all purchase lines
  • b. Recalculates:
    • 1. IR
    • 2. IS
    • 3. Withheld VAT
    • 4. Excise
  • Ensures values stay in sync with invoice discount changes

All of this happens automatically, without relying on UI triggers.

Key Lessons from Both Scenarios

  1. UI is not the source of truth – system codeunits are
  2. Page and field triggers are often insufficient for complex calculations
  3. Event Recorder is essential for discovering the real execution flow
  4. Table and codeunit events provide:
    • a. Correct timing
    • b. Stable system state
    • c. Preview Posting compatibility

Final Thoughts

Both of these scenarios reinforce one important principle in Business Central development:

Finding the right event matters more than writing the logic itself.

Whether it is:

  • a. Applies-to logic in Payment Journals
  • b. Invoice Discount recalculation on Purchase Orders

The solution lies in understanding where Business Central actually performs the work – and subscribing after that point.

We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfronts.com


Share Story :

SEARCH BLOGS :

FOLLOW CLOUDFRONTS BLOG :


Secured By miniOrange