7.6 KiB
7.6 KiB
Feature — Finance
Unified financial tracking with encrypted amounts, recurring transactions, budget targets, billable entries, monthly summaries, and cash flow projections.
Schema
financial_entries
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | |
type |
varchar(10) |
NOT NULL | income or expense |
date |
date |
NOT NULL | |
amount_encrypted |
bytea |
NOT NULL | Encrypted amount |
currency |
varchar(3) |
default 'USD' |
|
category |
varchar(30) |
NOT NULL | |
description |
varchar(255) |
nullable | Plaintext (expenses only) |
description_encrypted |
bytea |
nullable | Encrypted (income only) |
domain_id |
uuid |
FK → domains.id, nullable |
|
project_id |
uuid |
FK → projects.id, nullable, ON DELETE CASCADE |
|
recurring_transaction_id |
uuid |
FK → recurring_transactions.id, nullable |
Link to recurring source |
contact_id |
uuid |
nullable | |
duration_minutes |
integer |
nullable | For time-based income |
notes |
text |
nullable |
Indexes: type, date, category, domain_id, project_id, recurring_transaction_id
recurring_transactions
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | |
type |
varchar(10) |
NOT NULL | income or expense |
name |
varchar(255) |
NOT NULL | |
amount_encrypted |
bytea |
NOT NULL | |
currency |
varchar(3) |
default 'USD' |
|
category |
varchar(30) |
NOT NULL | |
day_of_month |
integer |
NOT NULL | |
domain_id |
uuid |
FK → domains.id, nullable |
|
project_id |
uuid |
FK → projects.id, nullable, ON DELETE CASCADE |
|
is_active |
boolean |
default true |
|
notes |
text |
nullable |
Indexes: type, category, domain_id
budget_targets
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | |
month |
varchar(7) |
NOT NULL | YYYY-MM or default |
category |
varchar(30) |
NOT NULL | |
limit_encrypted |
bytea |
NOT NULL | Encrypted budget limit |
currency |
varchar(3) |
default 'USD' |
|
domain_id |
uuid |
FK → domains.id, nullable |
|
notes |
text |
nullable |
Indexes: (month, category), domain_id
Unique: (month, category, domain_id)
billable_entries
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | |
domain_id |
uuid |
NOT NULL | |
project_id |
uuid |
FK → projects.id, NOT NULL, ON DELETE CASCADE |
|
contact_id |
uuid |
nullable | |
date |
date |
NOT NULL | |
title |
varchar(255) |
NOT NULL | |
description |
text |
nullable | |
amount |
numeric(12,2) |
NOT NULL | Plaintext (not encrypted) |
currency |
varchar(3) |
default 'USD' |
|
duration_minutes |
integer |
nullable | |
status |
varchar(20) |
default 'pending' |
pending, invoiced, paid |
due_date |
date |
nullable | |
paid_date |
date |
nullable |
Indexes: domain_id, project_id, contact_id, date, status
API
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/finance/summary |
Monthly summary (?month=YYYY-MM, ?domainId=) |
GET |
/api/finance/projection |
Cash flow projection (?months=) |
GET |
/api/finance/trend |
Monthly trend data (?months=) |
GET |
/api/finance/entries |
List entries (paginated, ?type=, ?domainId=, ?category=, ?dateStart=, ?dateEnd=, ?contactId=) |
POST |
/api/finance/entries |
Create financial entry |
PATCH |
/api/finance/entries/:id |
Update entry |
DELETE |
/api/finance/entries/:id |
Delete entry |
GET |
/api/finance/recurring |
List recurring transactions (?type=) |
POST |
/api/finance/recurring |
Create recurring transaction |
PATCH |
/api/finance/recurring/:id |
Update recurring |
DELETE |
/api/finance/recurring/:id |
Delete recurring |
POST |
/api/finance/recurring/:id/mark-paid |
Create entry from recurring for month { month } |
GET |
/api/finance/targets |
List budget targets (?month=) |
POST |
/api/finance/targets |
Create budget target |
PATCH |
/api/finance/targets/:id |
Update target |
DELETE |
/api/finance/targets/:id |
Delete target |
GET |
/api/finance/billable |
List billable entries (paginated, ?domainId=, ?projectId=, ?status=, ?dateStart=, ?dateEnd=) |
POST |
/api/finance/billable |
Create billable entry |
PATCH |
/api/finance/billable/:id |
Update billable |
DELETE |
/api/finance/billable/:id |
Delete billable |
Summary Response
{
"month": "2026-03",
"incomeTotal": "4500.00",
"expenseTotal": "2100.00",
"projectedExpenseTotal": "2800.00",
"recurringExpenseTotal": "1200.00",
"recurringIncomeTotal": "0.00",
"oneTimeExpenseTotal": "900.00",
"balance": "2400.00",
"projectedBalance": "1700.00",
"currency": "USD",
"changePercentage": 12.5,
"recurringTransactions": [{ "id": "...", "name": "...", "isPaid": true }],
"expensesByCategory": [{ "category": "food", "total": "600.00", "budgetLimit": "800.00", "percentUsed": 75.0 }],
"incomeBySource": [{ "sourceType": "session", "total": "4500.00", "count": 12 }],
"incomeByDomain": [{ "domain": { "id": "...", "name": "Work" }, "total": "4500.00" }],
"projection": { "estimatedIncome": "4200.00", "trend": "stable" }
}
Backend Module
FinanceModule — imports DomainsModule (via Domain entity).
Services:
FinanceService— CRUD for financial entries with encryption/decryption viaEncryptionServiceRecurringTransactionService— recurring transaction management, mark-paid (creates entry with idempotency check)BudgetTargetService— budget targets withdefaultfallback for unconfigured monthsFinanceSummaryService— monthly summary aggregation, trend calculation, 3-month trailing projectionBillableService— billable entry CRUD with status tracking (pending/invoiced/paid)
Business rules:
- All monetary amounts are encrypted at rest via
EncryptionService(except billable entries which use plaintextnumeric) - Income entry descriptions are encrypted; expense descriptions are plaintext
- Mark-paid is idempotent: throws
ConflictExceptionif recurring already paid for the month - Budget targets support
defaultmonth as template; month-specific targets override defaults - Summary computes projected expenses = actual expenses + unpaid recurring still expected
- Projection uses 3-month trailing average for estimated income/expenses
- Domain auto-resolution: if
projectIdprovided withoutdomainId, resolves viaProjectResolverService
Frontend
Routes
/finance— Finance dashboard with summary, entries, recurring, targets
Components
FinancePage— Main finance dashboardFinanceModals— Create/edit modals for entries, recurring, targets
Data (hooks)
const { summary, entries, recurring, targets, billable, createEntry, markPaid } = useFinance();
Key Interactions
- View monthly summary with income vs expense breakdown
- Track recurring transactions and mark them paid each month
- Set budget targets per category and track percent used
- View spending trend over configurable months
- Manage billable entries with status workflow (pending → invoiced → paid)
Implementation Phase
Phase 2 (Core Features) — FinanceModule, encrypted storage, budget targets, summary aggregation.