Financial Automation
Automated Billing & Tax Engine
Unattended recurring billing — scheduled worker services that generate invoices, calculate tax through Avalara AvaTax, and post to SAP Business One with idempotent, replay-safe runs.
- 01 The problem
- Monthly billing was a manual, error-prone exercise — in a system where a mistake reaches the customer as money.
- 02 The hard part
- Partial failure is the normal case — scheduled runs against an ERP and a tax API that can be slow, down, or mid-maintenance.
- 03 The outcome
- Invoice → tax → posting runs unattended, idempotent end-to-end. An interrupted run replays without double-billing.
Context
Recurring customer billing was a manual, error-prone monthly exercise: staff generated invoices by hand in the ERP, applied tax rules themselves, and reconciled mistakes after the fact. The goal was to make the entire cycle — invoice generation, tax calculation, posting, reporting — run unattended.
Constraints
- This code moves money. A duplicate invoice or a missed one is not a bug ticket, it is a customer-facing financial error.
- Tax rules vary by jurisdiction and change constantly — encoding them in-house was a permanent correctness liability.
- Runs happen on schedules against external systems (ERP, tax API) that can be slow, down, or mid-maintenance; partial failure is the normal case to design for, not the exception.
Architecture
A .NET Worker Service triggers billing runs on schedule. The billing engine evaluates recurring billing rules, calls Avalara AvaTax for jurisdiction-correct tax on each invoice, and posts the result to SAP Business One. Every step records its state in SQL Server keyed by an idempotency key, so a run that dies midway — network fault, ERP maintenance window, deploy — simply replays: already-posted invoices are skipped, unfinished ones complete.
Decisions & Trade-offs
- At-least-once with idempotent posting, not exactly-once. Exactly-once delivery across a scheduler, a tax API, and an ERP is a fiction; idempotency keys make retries safe and turn “did it run twice?” into a non-question.
- Delegating tax to AvaTax over in-house tax tables. An external dependency and per-call cost, in exchange for jurisdiction changes being someone else’s full-time job. For tax, buy beats build.
- Reconciliation as a feature, not an afterthought. Custom reports compare what the engine believes it billed against what the ERP posted — automation that finance can verify is automation that finance trusts.
Impact
Recurring billing became a background process instead of a monthly project: invoices generate, tax calculates, and postings land in the ERP without human touch, with reconciliation reports as the safety net. The same idempotent-run pattern was reused for the platform’s other automation — data migration jobs and scheduled financial reporting.
Next case · Applied Cryptography
Secure NFC e-Prescription System