Story 4: Double-Entry Finance Engine
Overview​
| Field | Value |
|---|---|
| Story ID | NGE-14-4 |
| Story Points | 21 |
| Sprint | Sprint 12-13 |
| Language | Go |
Why Double-Entry Accounting?​
- Audit Trail - Every transaction is traceable
- Balance Verification - Debits always equal credits
- Financial Reports - Trial balance, P&L, balance sheet
- Compliance - Standard accounting practices
Core Principle​
Every financial transaction creates TWO entries:
- One Debit entry
- One Credit entry
- They must always balance
Technical Implementation​
// finance_engine.go
func (e *FinanceEngine) CreateTransaction(ctx context.Context, txn *Transaction) error {
// Validate balance
totalDebit := decimal.Zero
totalCredit := decimal.Zero
for _, entry := range txn.Entries {
totalDebit = totalDebit.Add(entry.Debit)
totalCredit = totalCredit.Add(entry.Credit)
}
if !totalDebit.Equal(totalCredit) {
return fmt.Errorf("unbalanced: debit=%s, credit=%s", totalDebit, totalCredit)
}
// Insert with running balance
return e.db.WithTx(ctx, func(tx pgx.Tx) error {
// Insert transaction header
// Insert ledger entries with balance_after
return nil
})
}
// Invoice: Debit Receivable, Credit Income
func (e *FinanceEngine) RecordInvoice(ctx context.Context, invoice Invoice) error {
return e.CreateTransaction(ctx, &Transaction{
Entries: []LedgerEntry{
{AccountID: "1310", Debit: invoice.Amount}, // Receivable
{AccountID: "4100", Credit: invoice.Amount}, // Income
},
})
}
// Payment: Debit Cash/Bank, Credit Receivable
func (e *FinanceEngine) RecordPayment(ctx context.Context, payment Payment) error {
return e.CreateTransaction(ctx, &Transaction{
Entries: []LedgerEntry{
{AccountID: "1100", Debit: payment.Amount}, // Cash
{AccountID: "1310", Credit: payment.Amount}, // Receivable
},
})
}
Chart of Accounts​
| Code | Name | Type |
|---|---|---|
| 1000 | Assets | ASSET |
| 1100 | Cash | ASSET |
| 1200 | Bank | ASSET |
| 1310 | Student Receivable | ASSET |
| 4100 | Tuition Income | REVENUE |
| 4200 | Transport Income | REVENUE |
| 4400 | Late Fee Income | REVENUE |