apps.finance — Double-Entry Ledger & Accounts
Architectural core of financial integrity, ledger entries, invoice line items, and adjustments.
apps.finance — Double-Entry Ledger & Accounts
IMPLEMENTED
- Location:
backend/apps/finance/ - Responsibilities: Double-entry ledger journal, billing accounts, payment-to-invoice allocations, manual credit/debit adjustments, and idempotency key registry.
1. Database Models (6 Models)
| Model Name | Purpose | Key Fields | Invariants |
|---|---|---|---|
BillingAccount | Master ledger account for a customer | account_number, customer, currency, status | 1-to-1 with Customer |
InvoiceLine | Individual fee item associated with an invoice | invoice, description, quantity, unit_price, total | Belongs to billing.Invoice |
PaymentAllocation | Tracks partial or full payment against invoices | payment_transaction, invoice, allocated_amount | allocated <= invoice.due |
LedgerEntry | Immutable financial journal entry | account, entry_type, debit, credit, balance_after | Read-only once written |
Adjustment | Staff manual credit, debit, or waiver | account, amount, adjustment_type, reason, approved_by | Requires staff signature |
IdempotencyKey | Prevents duplicate mutations | key, tenant, status, response_data, expires_at | Scoped by tenant |
2. Ledger Invariants
Every Credit to a Customer Account increases customer balance (Cash Inflow, Payment, Advance).
Every Debit to a Customer Account decreases customer balance (Invoice Incurred, Reversal).Ledger entries are posted within atomic transactions using database row locks (select_for_update()) to guarantee consistency under high concurrent load.
3. Financial Services (apps.finance.services)
The finance service layer is authoritative for all monetary calculations, ledger entries, and balance mutations.
create_invoice_with_lines
Creates an itemized invoice, persists InvoiceLine records, computes subtotals, updates the BillingAccount, and posts a debit LedgerEntry:
- Line Calculation: Each line calculates
line_total = (quantity * unit_price) - discount + tax_amount. - Discount Precedence Policy: Explicitly provided
discountvalues on the invoice payload (including0.00) are strictly preserved and do not fall back tocustomer.discount. If omitted (None), the customer's profile discount is inherited. - Due Amount Invariant: Sets
customer.due_amount = invoice.total_payable. Becausetotal_payablealready includesprevious_dueplus net line items,previous_dueis never double-added. - Validated Fields Preservation: Preserves user-supplied or serializer-validated fields (
package_name,invoice_no,package_amount,total_payable). - Ledger Impact: Posts
LedgerEntry(entry_type=INVOICE, amount=total_payable).
apply_advance_to_invoice
Clears open invoices automatically using a customer's stored credit balance:
- Allocates up to
customer.advance_amountagainstinvoice.due_amount. - Updates
invoice.paid_amount,invoice.due_amount, and status (PAIDorPARTIAL). - Decrements
customer.advance_amountand adjustscustomer.due_amount. - Creates a
PaymentAllocationrecord binding the settlement to the invoice. - Posts a credit
LedgerEntry(entry_type=PAYMENT)and preserves the customer's net balance (advance_amount - due_amount).
reverse_recharge
Executes an audit-compliant, non-destructive compensating reversal for an erroneous recharge:
- Marks
recharge.is_reversed = True. - Marks linked
PaymentTransactionasREFUNDED(if present). - Rolls back customer
expiry_datetoprevious_expiry_date(or subtracts package validity days). - Appends a
REVERSALdebitLedgerEntry. - Restores unpaid/partial state on invoices cleared by the recharge.
- Reclaims surplus advance credit granted by the recharge.
grant_grace_period
Extends service access without requiring an immediate payment:
- Sets
customer.promise_dateandcustomer.grace_period_until. - Restores
customer.status = CustomerStatus.ACTIVE. - Emits an
AuditLogentry. - Dispatches a post-commit
NetworkSyncJob(ENABLE_USER) to ensure router access is re-enabled immediately.
sync_customer_financial_summary
Authoritatively resolves cached balance drift by recalculating from source records:
- Sums
due_amountfrom all open (UNPAID,PARTIAL,OVERDUE) invoices. - Sums unallocated credits from completed
PaymentTransactionandLedgerEntryrecords. - Synchronizes
customer.due_amount,customer.advance_amount, andbilling_account.balance.
4. API Controllers
GET /api/v1/invoice-lines/: List itemized invoice lines. Supports filtering by invoice via query parameter:?invoice=<uuid>.GET /api/v1/billing-accounts/: Customer billing accounts and live balance status.GET /api/v1/ledger-entries/: Read-only, append-only double-entry financial ledger journal.POST /api/v1/adjustments/: Staff manual fee adjustments or waivers (requires staff attribution and idempotency key).