Sageware Technology
Sageware TechnologyPreparing your experience…
Loading
All Case StudiesERP · Financial Core

The Financial Core

Payments and the general ledger rebuilt as one event-driven backbone — 500K+ transactions a day, and a period-end close that fell from five days to under eight hours.

Client

Multi-Tenant Enterprise ERP Platform

Industry

Enterprise Finance · ERP Platform Engineering

Duration

18 months

Year

2025

Architecture diagram: payment engine and general ledger posting engine joined by an event backbone, with period-end batch and an append-only audit store

Background

In most ERP platforms, money movement and the ledger are two separate systems that agree with each other once a month. Payments run in one module, the general ledger runs in another, and the gap between them is closed by an overnight batch job and a finance team with a spreadsheet. This platform was no different: sub-ledger data from payables, receivables, payroll and inventory reached the GL through jobs that ran after midnight, so balances were always a day stale, and every period-end close opened with a hunt for what had not posted. Closing a period took five days.

The Challenge

Rebuilding either half alone would not have helped. A faster payment engine still hands the ledger a pile of transactions to reconcile later; a faster ledger still waits on the batch. The two had to become one system — which meant solving the hard parts of distributed software on a path where the ordinary failure modes are unacceptable. A retried request must never become a second payment. A consumer that falls behind must never silently drop a journal posting. And the output is audited, so every entry has to carry enough evidence to reconstruct who changed what, when, and why — months after the fact.

Our Solution

SageWare rebuilt payments and the general ledger as a single event-driven backbone. The payment engine models the invoice-to-pay lifecycle — scheduling, approval, execution across rails, bulk file handling — and publishes domain events rather than writing to the ledger directly. The GL posting engine consumes those events alongside sub-ledger activity from AP, AR, payroll and inventory, and generates double-entry journal entries in real time against configurable mapping rules, with multi-entity, multi-currency and multi-book support. Because the ledger posts continuously as payments settle, reconciliation stops being a month-end event and becomes a property of the system. Period-end work that genuinely is batch-shaped — trial balance, revaluation, intercompany elimination — stayed batch, and every modification anywhere in the chain lands in an append-only audit store.

Key Deliverables

  • Payments and the general ledger as one event-driven backbone rather than two reconciled systems
  • Exactly-once payment execution via caller-supplied reference IDs and distributed locking
  • Real-time double-entry posting from AP, AR, payroll and inventory sub-ledgers
  • Multi-entity, multi-currency and multi-book ledger with configurable account mapping
  • Period-end automation for trial balance, revaluation and intercompany elimination
  • Append-only audit store capturing user, timestamp, reason and before/after values

Results & Impact

0K+
Transactions per day
Financial transactions processed at sub-100ms latency across the backbone
5d → 8h
Period-end close
Continuous posting left almost nothing to reconcile at month end
0%
Less manual reconciliation
Automated invoice-to-pay matching with exceptions surfaced rather than hunted
Zero
Duplicate payments
Prevented at the door by idempotency keys and a distributed lock on the send path
Real-time
Sub-ledger posting
Double-entry journals generated as source transactions occur, not overnight

How We Did It

01

Domain & Event Modelling

Modelled the invoice-to-pay lifecycle and sub-ledger activity as domain events, then fixed service boundaries, data contracts and an API versioning strategy before any code was written.

02

Ledger Architecture

Designed the chart of accounts model, multi-entity and multi-book structures, and the configurable mapping rules translating sub-ledger transactions into GL accounts.

03

Payment Engine & Idempotency Core

Built the payment engine with exactly-once semantics on the send path, integrated rails over ISO 20022, and added Spring Batch jobs for SFTP bulk payment files with per-format parsers.

04

Real-Time Posting Engine

Built the GL consumer generating double-entry journals in real time with multi-currency handling, plus the control-account reconciliation engine and its automated discrepancy alerting.

05

Period-End Automation & Controls

Automated trial balance, revaluation and intercompany elimination on batch, and delivered the append-only audit store and role-based GL inquiry APIs.

Decisions & Trade-offs

Events instead of the nightly batch — and why that was the whole point

Moving sub-ledger data onto an event backbone rather than an overnight job is what collapsed the close. The close did not get faster because the batch got faster; it got faster because a ledger that posts continuously has almost nothing left to reconcile at month end. The cost is that ordering, replay and consumer lag become our problem rather than the scheduler’s — which is why event contracts are versioned and every topic is ordered and replayable.

Idempotency keys and a short distributed lock, not a dedup table

A dedup table makes the database the arbiter of whether a payment already went out, which holds right up until the write that records the payment and the call that sends it sit in different transactions. Instead every payment carries a caller-supplied reference ID and takes a short distributed lock for the duration of the send, so a retry either finds the original or waits on it. Duplicates are prevented at the door rather than detected afterwards.

Streaming for fan-out, batch for the things that are actually batch

Kafka is the right tool for an event several independent consumers need — ledger, notifications, audit. It is the wrong tool for “revalue every account in this ledger.” Period-end closing stayed on Spring Batch because those jobs are bounded, ordered and restartable, and a half-finished revaluation needs to resume, not replay. Mixing the two deliberately was cheaper than forcing either model to cover both.

An append-only audit store, not a change log

Auditors rarely ask what a balance is. They ask what it was, who changed it, and on what authority. Capturing user, timestamp, reason and before/after values on every modification — append-only, never updated in place — turns that from an investigation into a query, and is what made the platform SOX-ready rather than merely well-logged.

Scope & Boundaries

We built the financial core, not the whole ERP. GL inquiry APIs expose account balances, trial balance and journal history behind role-based access control, and the platform’s existing front end consumes them — we did not replace the reporting layer or the UI. Tax determination and statutory filing remained with the incumbent system. Payment rail coverage is domestic; cross-border settlement was explicitly out of scope.

Next Case Study

Trading Partner EDI Integration

B2B Commerce · Document Integration