Case study · Billing, inventory, accounting and GST
Rechvix: Billing, inventory, accounting and GST
Accounting, inventory, GST and business operations on a double-entry ledger, for multi-company and multi-branch businesses.
- My role
- Lead developer. I wrote the architecture, the backend, the web frontend, the security model and the packaging.
- Licence
- AGPL-3.0

Context
Rechvix is a self-hosted billing, inventory, accounting and tax platform for Indian businesses. It handles GST, including e-Invoice and e-Way Bill, and it is structured so another country's tax rules can be added without rewriting the core.
I started it from an engineering brief rather than from an existing product. The brief treated the accounting and tax logic as the hard part and the interface as something that has to be fast for a person standing at a billing counter.
Problem
Most billing tools I looked at fall into two groups. Some are spreadsheets that cannot produce a GST e-Invoice. Others are subscriptions that put every invoice, ledger and customer record on a vendor's servers, often without real double-entry accounting underneath.
A billing system fails quietly when its numbers drift: stock that no longer matches movements, a ledger that does not balance, an invoice that was edited after it was filed. I wanted those failures to be structurally difficult, not merely discouraged.
Users
Owners and accountants of small and mid-size businesses, billing-counter operators, and distributors or multi-branch retailers who hold large product catalogues across several warehouses.
Operational workflow
Documents move from quotation and proforma through tax, cash or credit invoices, with credit notes, debit notes and returns. Drafts are editable; finalising a document is a single transaction that takes stock, posts the ledger entries, applies tax and allocates the document number together.
Engineering challenge
Finalising an invoice has to touch inventory, the accounting ledger, tax and numbering atomically. If any part fails, none of it may persist. That requirement shaped almost every other decision.
Architecture
A modular monolith, deliberately not microservices. Splitting invoice finalisation across network boundaries would trade correctness for an architectural style the scale does not need.
Dependencies point one way: HTTP handlers, then application services, then pure domain rules, then repository interfaces implemented against PostgreSQL. Domain packages never import the HTTP layer or a provider SDK. Modules call each other through application-layer interfaces, never through each other's repositories. External government and messaging APIs are reached only from adapter packages, and a separate worker process runs the outbox consumer and scheduled jobs.
Data model decisions
Stock is an append-only movement ledger. A balance table holds the current position as a projection that can always be rebuilt by replaying the movements. I record the reasoning for maintaining that projection in application code rather than a trigger in an architecture decision record, and wrote it up in Stock balances belong in application code.
Money uses a decimal type end to end. Floating-point money was one of the failure modes the project was designed against.
Every journal entry is balance-enforced, so the trial balance cannot be left unbalanced by a partial write.
Backend
Go with a chi router and sqlc-generated queries behind hand-written repository interfaces. Costing is weighted-average, expressed as a strategy so FIFO can be added later without changing the movement ledger. E-Invoice and e-Way Bill sit behind a provider interface and were built against the government sandbox first.
Frontend
A React and TypeScript web application, with a desktop shell packaged for the Microsoft Store. The billing screen is designed around keyboard and barcode use. The catalogue is paginated on the server after I found it loaded every product into the browser on each visit, which would not survive a distributor-sized catalogue.
An automated accessibility suite runs on every pull request: Playwright with axe-core scans the login page and checks keyboard tab order.
Security
Tenant isolation uses two layers. Every tenant table carries an organisation identifier enforced by PostgreSQL row-level security, and every repository lookup also filters by organisation in its own SQL. I added the second layer after reading every repository and finding that thirteen lookups relied on row-level security alone. The reasoning is in Row-level security is not your only tenant boundary.
The database has two roles. A migrator role owns the schema and runs migrations only. A separate non-owning runtime role is what the application connects as. Connecting as the table owner would silently bypass every row-level security policy, so the application warns at startup if its runtime role owns tenant tables.
Passwords use Argon2id with parameters recorded in a decision record. TOTP multi-factor authentication can be made mandatory for owners, admins and accountants. Permissions are scoped to branch and warehouse.
Infrastructure
Packaged for one-command self-hosting with Docker Compose and as a CasaOS app. Core operation needs no internet connection; only integrations the operator turns on, such as government e-Invoice submission, make outbound calls. There is no telemetry.
Reliability considerations
Backup and restore is documented as a runbook with a restore drill: restore into a scratch PostgreSQL, then check row counts and the trial-balance invariant. Writing that runbook exposed a real gap, that API keys had no scope for backup operations, which I recorded instead of hiding.
Difficult tradeoffs
Maintaining stock balances in Go instead of a trigger gives one place for the costing logic and readable stack traces, at the cost of having no database-level backstop against a raw insert into the movement table. I accepted that and documented the mitigation: the balance is re-derivable from the ledger and the restore drill checks it.
Version one has a single Owner role, so a team member added through the Team screen is a full peer of the owner. Restricted roles were deferred and are recorded as such.
What I personally worked on
All of it: the brief, the architecture and its decision records, the Go backend, the web frontend, the PostgreSQL schema and security roles, the test suites, CI and the self-hosted packaging.
Lessons learned
A stated design principle is not the same as an implemented one. Reading every repository query turned up a gap between what the README promised and what the code did. Security review is most useful when it reads the code and not the intent.
Technologies
- Go
- PostgreSQL
- chi
- sqlc
- React
- TypeScript
- Docker
- Playwright
Screenshots
Captured from running instances.
Rechvix: frequently asked questions
- What is Rechvix?
- Rechvix is a self-hosted billing, inventory, accounting and tax platform for Indian businesses. It runs on a double-entry ledger, supports GST including e-Invoice and e-Way Bill, and handles multiple companies and branches.
- Who built Rechvix?
- Raktim Ranjit, founder of NodeDR Infotech Private Limited, designed and built it as lead developer, including the architecture, backend, web frontend, security model and packaging.
- Is Rechvix open source?
- Yes. The source is published on GitHub under the AGPL-3.0 licence.
- How does Rechvix keep stock and the ledger in agreement?
- Finalising an invoice is one database transaction that takes stock, posts ledger entries, applies tax and allocates the document number together. If any step fails, none persists.
- How are different companies kept apart?
- Tenant boundaries are enforced twice: by organisation filters in SQL queries and by PostgreSQL row-level security, using a runtime database role that is separate from the migrator.




Expertise this project shows
Related articles
- Stock balances belong in application code, not a database trigger
- Row-level security is not your only tenant boundary
- A PostgreSQL backup is only useful after a restore drill
- Open-source GST billing and accounting software with a double-entry ledger: how Rechvix works
- GST billing software for small businesses: what to look for in India
- GST invoice requirements: a field-by-field checklist for India
- Tally vs Vyapar vs Zoho Books: choosing accounting and billing software in India
- Billing software with inventory: what to check so stock and accounts agree
- ERP vs CRM: what each one does and which you need first
- ERP software for small business: do you need one, and how to pick
- A timed-out invoice request should not create a second invoice
- A business day is not always a calendar date in the server's time zone
- An audit trail and an application log answer different questions