DailyOS — Daily Cash-Up & Back-Office Paperwork for Multi-Site Retail
The day's takings reconciled, the supplier invoices scanned and filed, and the accountant's exports ready — with the industry's own arithmetic stored as data rather than code.
A fuel station's day-end paperwork is a sheet of arithmetic nobody enjoys: fuel dips, a dozen card types, IOUs, drive-offs, vault drops, all reconciled against the terminal total to find the difference. Do it on paper and the errors surface weeks later at the accountant's desk. Then there's the other half of the back office — supplier invoices and cash purchases arriving as loose paper, photographed page by page, needing to be filed somewhere the accountant can actually find them. Building software for this is easy for one industry and hard for the next: a restaurant closes by service rather than by day, a workshop by week, and every vertical's sheet has different fields, different formulas, and different validation. The usual outcome is a codebase forked per industry, or a mess of conditionals inside the core.
A multi-tenant Django application scoped by business and site, where staff and managers see their own site, business admins see their whole business, and the platform admin deliberately cannot see customer trading data through the application at all. Day-end reconciliation runs with live totals in the browser, a draft-and-submit flow, and a mandatory note whenever the difference exceeds five dollars. Supplier invoices and cash purchases accept multiple photographed pages and merge them into a single PDF for preview and download. Everything filters and exports to PDF, Excel, and CSV. The generalisation problem is answered with vertical packs: field definitions, formulas, validation rules, and report layouts live in the database, so a new industry is seed data rather than a release. Pack formulas are stored as text and evaluated by walking a parsed AST against a whitelist — never eval — and because the same formulas must also drive the live totals in the browser, there are two implementations. Neither is the source of truth: a shared JSON fixture is, and both the Python and JavaScript sides are run against it in the test suite. Packs are versioned and freeze the moment a record is entered against them, since editing one afterwards would silently change how an already-filed record reprints.
Outcome
Deployed for a fuel-station operator and in daily use. 231 tests, including a generalisation gate that constructs an industry the codebase has never heard of entirely from database rows and drives the whole engine with it — if a new vertical ever requires a change inside the core, that test is what catches it. Three packs ship: fuel, restaurant, and workshop. The pack engine is complete and covered; wiring it through to what a customer sees is the remaining step, and the project's README says so explicitly rather than claiming otherwise — it even gives the grep command to check whether that status is still accurate.
Key features
- Multi-tenant scoping by business and site, with the platform admin structurally excluded from customer trading data
- Invite-only account hierarchy with forced password change at first login
- End-of-day reconciliation with live in-browser totals and draft/submit flow
- Mandatory explanatory note when the reconciliation difference exceeds the threshold
- Late-entry window so a night shift can close the previous day after midnight
- Supplier invoices and cash purchases with multi-page photo upload merged into one PDF
- Report filters with PDF, Excel, and CSV export, previewed before download
- Field-level audit log across suppliers, invoices, and day-end records
- Archive-instead-of-delete for day-end records, freeing the date for a corrected entry
- Vertical packs storing fields, formulas, validation, and report layout as database rows
- AST-whitelist formula evaluator with a matching JavaScript implementation, both validated against one shared fixture
- Defined edge-case behaviour — division by zero yields zero, unknown fields read as zero — so two implementations cannot drift
- Pack versioning that freezes on first use, pinning existing records to the version they were entered under
- Per-pack cash-up periods: daily, per-service shift, or weekly, with configurable cut-off hours
- Subscription status enforcement where suspension makes the account read-only rather than locking customers out of their own records
- Guarded migration path for an application rename, with idempotent pre-flight and refusal on ambiguous state
- Uniqueness-tightening migration that reports every conflicting row and stops without changing anything
- Verification command that replays the whole history through the engine and exits non-zero on a single mismatch
- Docker Compose setup with PostgreSQL, and migrations deliberately kept out of container startup
Tech stack
Build something similar?
Tell us about your project. We'll share how we'd approach it.
Start a projectMore Web Application projects
Pigeon Pedigree Pro — Multi-Tenant Loft Management SaaS
Photograph a paper pedigree certificate and the birds appea…
HireOMate — Peer-to-Peer Rental Marketplace
Rent out the drill, the trailer, the camping gear — with de…
Sisters Kitchen — Home Kitchen Ordering & Catering Website
A family takeaway kitchen's weekly menu, pre-orders and cat…