Billing is the primary feature of AquaDealers. Every bill creates a cascade of data changes across multiple tables in a single atomic transaction.
create_bill_v2 RPCA single PostgreSQL function that performs all of the following in one transaction. If any step fails, the entire transaction is rolled back — no partial state.
| # | Operation | Table |
|---|---|---|
| 1 | Insert bill header (number, date, farmer, totals, branch) | bills |
| 2 | Insert line items with quantities, rates, discounts | bill_items |
| 3 | Deduct stock from oldest FIFO lots per item | inventory_lots |
| 4 | Record inventory movements (type: 'sale') | inventory_movements |
| 5 | Insert payment record (if amount_paid > 0) | payments |
| 6 | Allocate payment to this bill | payment_allocations |
| 7 | Create cash book entry (if cash payment) | cash_book |
| 8 | Update farmer due balance (via trigger) | farmers |
| 9 | Record transaction event for undo capability | transaction_events |
When the device is offline, bills are saved locally and synced when connectivity returns. The sync mechanism uses create_bill_offline_sync RPC with client_ref idempotency.
Each offline bill gets a client_ref — a UUID generated on the client. There is a unique index on (dealer_id, client_ref) on the bills table. If the sync request is sent twice (e.g., network timeout followed by retry), the second attempt is safely rejected as a duplicate.
Up to 5 concurrent drafts stored in sessionStorage. Each draft preserves:
Drafts survive page refreshes within the same browser tab but not across tabs or after closing the browser.
5 invoice template designs available:
| Template | Style |
|---|---|
| TemplateOne | Clean minimal — product, qty, rate, amount |
| TemplateTwo | Detailed — includes HSN, MRP, discount columns |
| TemplateThree | GST-focused — full GST breakdown (CGST + SGST) |
| TemplateFour | Compact — smaller font, more items per page |
| TemplateFive | Modern — styled header, signature area prominent |
Each template supports toggling these columns on/off via Settings:
Templates are branch-specific — different branches can use different templates.
html2canvas renders the invoice DOM to a canvas, converts to PNG, then embeds in a jsPDF document. This ensures the PDF matches the on-screen invoice pixel-for-pixel.jsPDF — directly draws text, tables, and lines for consistent layout regardless of screen rendering.Format: AD-{YYYYMMDD}-{sequence}
The sequence is a branch-specific auto-incrementing counter. Example: AD-20260909-042 is the 42nd bill from this branch on September 9, 2026.
Estimates (quotations) use the same billing flow but with no side effects.
| Aspect | Bill | Estimate |
|---|---|---|
| Database flag | is_estimate = false | is_estimate = true |
| Stock impact | Deducts from FIFO lots | None |
| Payment | Records payment, updates cash book | None |
| Farmer due | Increases outstanding balance | No change |
| WhatsApp notification | Sent (if configured) | Not sent |
| Listed in | /bill-history | /estimates |
| Conversion | — | Can be converted to a real bill |
When an estimate is converted to a bill, the system creates a new bill using the estimate's items and runs the full create_bill_v2 transaction. The estimate record is updated with a reference to the new bill.
Returns handle the reversal of sold goods. The system restores stock to the original FIFO lots using LIFO (last-in-first-out) — the most recently consumed lot is refilled first.
bill_returns — header: linked to original bill, return date, total return amount, reasonbill_return_items — line items: product, quantity returned, lot referencesReturns use LIFO for lot restoration. If a bill consumed 50 units from Lot A and 30 from Lot B (in that FIFO order), a return of 20 units restores to Lot B first (the last lot consumed), then Lot A if the return quantity exceeds what came from Lot B.
If a return exceeds the remaining unpaid balance on the bill, the excess becomes return_credit_balance on the farmer record. This credit is automatically applied to the farmer's next bill.
Every return creates inventory movement records with reference_type = 'bill_return' and reference_id pointing to the return. This ensures complete traceability of every unit in and out.
| RPC | Purpose |
|---|---|
preview_farmer_return_v1 | Preview what will happen — shows affected lots, amounts, credit calculation — before committing |
create_farmer_return_v1 | Execute the return — restore stock, adjust farmer balance, create audit entries |
replace_farmer_return_v1 | Replace (edit) an existing return — undoes the previous and applies the new |
Transfer stock between branches while preserving FIFO lot data. Each transfer creates movement records at both ends.
create_stock_transfer RPCtype = 'transfer_out'type = 'transfer_in'reference_idedit_bill_v1Every edit creates a record in bill_audit_logs with:
The bill record itself is flagged with is_edited = true so edited bills are visually distinguished in bill history.
cancel_bill_v1| Constraint | Rule | Reason |
|---|---|---|
| Timing | Same-day only | Prevents backdated financial manipulation |
| Payment | amount_paid must be 0 | Bills with payments must be handled via returns or editing |
| Status | Must not already be cancelled | Idempotency — returns 'already_cancelled' without error |
status = 'cancelled'reference_type = 'cancellation'cancel_bill_v1 on an already-cancelled bill returns 'already_cancelled' status without error or side effects. Safe to retry.
A universal undo system that covers all financial transactions in the application.
| Type | Undo Behavior |
|---|---|
| Bills | Cancels the bill, restores stock, reverses farmer due |
| Purchases | Removes inventory lots, reverses supplier balance |
| Payments (farmer) | Reverses payment allocation, restores farmer due |
| Supplier Payments | Reverses payment, restores supplier balance |
| Returns | Re-deducts returned stock, reverses farmer credit |
| Stock Transfers | Reverses transfer at both branches |
| Expenses | Removes expense record and cash book entry |
| Manual Cash Entries | Removes the entry from cash book |
transaction_events records every financial transaction with type, reference ID, timestamp, and actorundo_transaction_v1deleted_at or cancelled_at timestamps rather than hard deletion. This preserves the audit trail — undone transactions are still visible but marked as undone.