
Legacy ERP Modernization Without Replacing the ERP: How We Built a Financial Integration Layer for a Specialty Hardware Distributor
- Ed Hitchcock

- Apr 28
- 7 min read
By Ed Hitchcock, Enterprise AI Systems Architect, SupplyTech Solutions
The financial visibility gap
When we walked into this engagement, the specialty hardware distributor had a familiar problem. Operations knew which loads moved yesterday. Finance knew the bank balance. Neither side could answer the question that mattered: what did yesterday cost us, and what did it earn?
The ERP, a long-running QuickBooks Enterprise install with custom fields layered on over fifteen years, was the system of record. Three regional dispatchers across four DCs were keying freight invoices into AP every Tuesday and Friday. Accessorial charges, detention, layover, redelivery, fuel surcharge adjustments, lived in email threads and PDF scans until someone had time to reconcile them. The reconciliation lag ran 30 to 45 days from load tender to GL posting, a number we documented in Part 1 of this series.
We did not need to replace QuickBooks. We needed to make it tell the truth in something close to real time. That was the scope of legacy ERP modernization for this client: keep the ledger, fix the data flow into and out of it.
Why we did not rip and replace
A common consulting reflex is to recommend an ERP migration when the data is messy. We pushed back on that for three reasons.
First, the messiness was not in the GL. The chart of accounts was clean, the audit trail was intact, and the close process worked. The mess was in everything that fed the GL.
Second, this client had roughly 1,200 customer accounts and 15 years of pricing history, terms, and credit decisions encoded in QuickBooks. That institutional memory would survive a migration only at significant cost and risk.
Third, the team knew QuickBooks. Re-training a finance team on a new platform during a logistics consolidation was an unforced error we declined to make.
So the brief became a financial integration architecture: an event-driven layer that sits between the operations platform and QuickBooks, posts the right entries to the right accounts within hours instead of weeks, and captures accessorials at the moment they happen.

What we built
The integration layer has four components, each with a defined responsibility.
The first is an event bus tied to the operations platform. Every state change in a load lifecycle, tendered, accepted, picked up, delivered, billed, paid, emits a structured event. This is the same event stream we built in Part 1 to consolidate the three logistics channels. For financial integration we added two new event types: accessorial-incurred and rate-adjusted.
The second is a posting engine. It listens to the event bus, applies posting rules, and writes journal entries to QuickBooks through the QuickBooks Online API and a local QuickBooks Web Connector for the Enterprise instance. Posting rules are versioned, reviewable, and owned by the controller, not by IT. When the controller wants to change how detention charges hit the GL, she edits a rule, not a SQL trigger.
The third is an accessorial capture workflow. Drivers, dispatchers, and receivers can document accessorial events from the mobile app or the web dashboard at the moment they occur. Each captured event includes the load reference, the charge type, the supporting evidence (photo, signature, timestamp), and a proposed amount based on the carrier contract. The posting engine treats a captured accessorial like any other event: it gets a journal entry within the hour.
The fourth is a reconciliation dashboard for the finance team. It surfaces unmatched events, posting failures, and exceptions that need human judgment. The team works the dashboard the way they used to work email, but with every item already linked to its source load and its proposed GL impact.

How the posting rules actually work
The single hardest part of this engagement was getting the posting rules right. Freight accounting has more edge cases than people remember.
A standard truckload move with a clean BOL and contract rate posts simply: debit freight expense, credit AP, with the load reference as the memo. We have that case fully automated.
A load with three accessorials, two of which are disputed by the carrier, is a different story. The posting engine creates a primary AP entry for the contract rate, separate AP sub-entries for each accessorial, and flags the disputed items as pending. The pending items live in a sub-ledger until the dispute resolves. When dispute resolution drops a final figure, the engine reverses the pending entry and posts the agreed amount.
A spot-market load that fell off the routing guide and hit the load board posts to a different freight expense account, because this client wants the financial impact of routing-guide failure visible at the GL level. That is a posting rule, owned by the controller, version 4 at last count.
We wrote roughly 60 posting rules across the standard cases. The remainder are exceptions handled by the reconciliation dashboard. Our target was that 80 to 85 percent of weekly load volume posts without human review, and we are tracking inside that range.

Same-day financial visibility, defined precisely
When we pitched same-day financial visibility, the controller asked the right question: same-day for what?
We negotiated three definitions and built to them.
Same-day for AP accrual. Every delivered load is accrued to AP within four hours of proof of delivery. The accrual is based on contract rate, with accessorial estimates if the dispatcher has flagged them. This means the daily AP report reflects the previous day's operations with high accuracy, where before it reflected last Tuesday.
Same-day for revenue recognition on customer invoices. Every billable shipment is invoiced within eight hours of delivery, against the customer's contract or the agreed spot rate. This collapsed the average invoice lag from nine days to under one.
End-of-week for full reconciliation. By Friday close, the previous week is fully reconciled: accessorials documented, disputes opened, exceptions worked. Month-end close, which used to take six business days, now takes two.
These are the numbers the finance team can defend, not aspirational targets we put in a deck.
What this did to the close process
The close process was the canary. Before integration, the controller's team spent the first six business days of every month chasing freight invoices, matching POs, resolving accessorials, and posting catch-up entries. Most of that work was clerical, and most of it was avoidable if the data had moved cleanly during the period.
After integration, the same close runs in two business days. The controller redirected roughly 10 to 15 hours per week of finance team capacity from chasing paper to actual analytical work: margin review, customer profitability, lane-level cost analysis. That capacity reallocation was the part of the project that the CFO cared most about, more than any specific automation metric.
We did not promise a specific headcount reduction, and the client did not pursue one. The team is the same size. They are doing different work.
Accessorial capture, the part that paid for itself
The accessorial capture workflow was a small piece of the integration architecture, and it had the largest single financial impact in the first 90 days.
Before the project, accessorials lived in two states: charges the carrier billed and the client paid because nobody had documentation to dispute, and charges the client could have billed customers but did not, because nobody captured the event in time. Both leaks ran in the same direction: against the client.
After the project, every accessorial event is documented at the source. Driver detention is timestamped from the moment the truck arrives at the receiver to the moment the load releases. Layover is geotagged. Redelivery is photographed. The supporting evidence is attached to the event and follows it through to the AP or AR posting.
In the first 90 days post-integration, the client recovered approximately 35 to 45 percent more accessorial revenue from customers, and disputed approximately the same percentage more carrier accessorial charges, both compared to the prior 90 days. The capture rate is the lever. We did not change the rates or the contracts. We changed the documentation discipline, and the integration layer made the discipline cheap to maintain.
What broke and what we changed
Two things broke in the first month that are worth documenting.
The first was a posting rule that handled a specific edge case in fuel surcharge calculation incorrectly, resulting in roughly 30 loads with overstated AP accruals. The reconciliation dashboard caught the pattern within a day, the controller flagged it, we corrected the rule and reposted the affected entries. Total exposure: under $4,000 in temporarily misstated accrual. Net learning: rule changes get a sandbox replay against the prior week before deployment.
The second was an integration timeout against the QuickBooks Web Connector during a holiday weekend, which queued about 200 events. The posting engine handled the backlog correctly when the connection restored, but the dashboard alerts were not noisy enough to wake the on-call. We rebuilt the alerting with explicit SLAs: any queue depth over 50 events for more than two hours pages someone.
Neither of these caused a financial misstatement that survived past the next reconciliation cycle. Both reinforced our preference for systems that fail visibly and recover predictably.
What we left for the analytics layer
We deliberately did not build analytics into the financial integration. The integration layer is concerned with truth: what happened, where does it post, can the controller defend it. Analytics is a different concern: what does it mean, how do we steer.
Part 4 of this series will cover the analytics layer we built on top of this foundation, including lane-level cost modeling, tender-rejection economics, and the financial metrics dashboards we built for the executive team. The integration layer is what made the analytics layer possible. Without clean, timely, trustworthy data flowing into the GL, every analytics build is downstream of garbage.
What this means for distributors with legacy ERP
If your ERP is doing its job as a system of record but the data flowing into it is slow, manual, and error-prone, legacy ERP modernization does not require a migration project. It requires an integration architecture: an event bus, a posting engine with controller-owned rules, an exception workflow, and the discipline to define same-day visibility precisely instead of as a slogan.
Most distributors we talk to have spent a decade encoding institutional knowledge into their ERP. Replacing the ERP throws that knowledge away. Building a clean integration layer keeps it, and brings the rest of the operation up to its standard.
That is the pattern we built here. We are deploying variants of it for two other clients now, and the financial-impact profile is similar in both: capacity reallocation in finance, faster close, and recovered accessorial margin that pays for the project inside a year.


Comments