Invoice Generator
An Android-first invoicing app with local drafts and PDF sharing.
I'm building Ledgerlite, an Android-first invoice generator, and it is under testing. The app manages business details, customers and saved items, then uses them to create invoices with discounts, tax calculations, due dates and payment status.
The codebase has three workspaces: an Expo and React Native app, an Express API backed by MongoDB, and a shared TypeScript engine for money, tax, validation and invoice status. Drafts are stored locally in SQLite and queued for sync. Saved invoices can be previewed, printed and shared as PDFs.
The repository includes engine, mobile and API tests, plus Playwright browser tests. I'm treating the implementation as work under testing rather than a finished release; device behaviour, sync failures and the invoice lifecycle are part of that work.
The preview and the server need the same totals
The app and API import the same invoice engine. Money uses integer minor units, with BigInt arithmetic for rounding and proportional discount allocation. The engine handles tax-inclusive and tax-exclusive prices, line taxes and optional invoice round-off.
The API recomputes totals when saving instead of trusting a total from the phone. If the supplied grand total or tax total disagrees with the server calculation, it rejects the save. Engine tests cover the arithmetic, including property tests for money rules.
Retry a draft without creating a second invoice
Local drafts have a stable localId. The sync runner prevents overlapping queue flushes and backs off after failures. On the server, creating an invoice checks for an existing invoice with that localId within the business before allocating a new number.
The invoice counter update and invoice insertion share a MongoDB transaction. This keeps a failed create from consuming a sequence number. Business-scoped queries and unique indexes support the numbering and retry logic.
Sharing a PDF does not mean the invoice was sent
Invoice preview, printing and PDF generation use the same HTML template. Native PDF export uses Expo Print, and the share flow attaches the PDF alongside a message for the customer.
Opening the phone's share sheet does not reliably tell the app whether the customer actually received anything. The sharing helper leaves invoice status unchanged, keeping the send decision separate from opening the share sheet.
What I’d do differently
- 01I'd give device testing the same attention as the shared calculation engine. A correct total still needs a usable editor, a readable PDF and a share flow that behaves correctly on a phone.
- 02I'd keep interrupted sync, repeated saves and account changes in the regression checks. Local drafts and server-assigned invoice numbers need to remain consistent when requests fail or are retried.
