Budget System
Overview
Budget management with envelope-based tracking, automatic transaction mapping from bank imports, vendor bill integration via Billingo, and multi-currency support.
Envelope Types
Four envelope types defined in BudgetEnvelopeType:
| Type | Purpose | Display |
|---|---|---|
BUDGET | Periodic spending limits | Progress bar (spent vs target) |
SAVINGS_GOAL | One-time savings targets | Progress bar (saved vs target) |
SAVINGS_TARGET | Recurring savings targets | Progress bar per period |
TRACKING | Balance monitoring, no targets | 30-day sparkline + balance |
Cross-Account Envelopes
account_idis nullable -NULLmeans cross-account- Cross-account envelopes accept transactions from any bank account
- Displayed with globe icon, converted balances using cached exchange rates
Transaction Types & Sources
BudgetTransactionType = 'INCOME' | 'EXPENSE' | 'TRANSFER'
BudgetTransactionSource = 'MANUAL' | 'INVOICE' | 'TIMESHEET' | 'BANK' | 'VENDOR_BILL'extJSONB column stores metadata from external sources (bank imports)budget_transaction_amount_checkconstraint: amount > 0
Mapping Rules
Automatic transaction categorization from bank imports:
| Field | Type | Purpose |
|---|---|---|
account_ids | UUID[] | Which bank accounts this rule applies to (array, GIN-indexed) |
priority | int | Lower = higher priority, deterministic application order |
| conditions | JSONB | Match criteria against transaction ext fields |
| actions | JSONB | Envelope assignment, categorization |
Hooks
useBudgetMappingRules(accountId?)- Fetch rules, ordered by priorityuseAvailableExtKeys(accountId)- Extract distinct keys fromextJSONB (limited to 100 txns)
UI
- Conditions/actions rule builder
- Preview sidebar (
MappingPreviewSidebar.tsx) - Full bilingual support (42 HU translation keys)
Vendor Bills (Billingo Integration)
Synced via billingo-sync-inbound edge function:
Data Flow
Billingo API → edge function → vendor_bill table → budget_transaction (for paid bills)
Field Mappings (learned the hard way)
Our field Billingo API field vendor_name spending.partner?.nametotal_gross spending.total_grossspending_date spending.fulfillment_datepaid_date spending.paid_atstatus Derived from paid_at+due_date
Status Derivation
function deriveStatus(spending): 'PAID' | 'OVERDUE' | 'OUTSTANDING' {
if (spending.paid_at) return 'PAID'
if (spending.due_date && new Date(spending.due_date) < now) return 'OVERDUE'
return 'OUTSTANDING'
}Schema Notes
total_grossis NUMERIC (not INTEGER) - API returns decimals- Budget transactions filtered:
(bill.total_gross ?? 0) > 0to skip zero-amount bills
UI Components
| Component | File | Purpose |
|---|---|---|
| BudgetOverview | BudgetOverview.tsx | Envelope cards grid with type-aware rendering |
| EnvelopeDetailSheet | EnvelopeDetailSheet.tsx | Side panel with transactions and summary |
| VendorBills | VendorBills.tsx | Vendor bills table with sync and status badges |
| MappingPreviewSidebar | MappingPreviewSidebar.tsx | Rule testing preview |
Status Badges
- Color-coded: green=PAID, red=OVERDUE, yellow=OUTSTANDING
- Record-based lookups for status→CSS class mapping (not switch statements)
- Translated labels via
STATUS_LABEL_KEYRecord
Envelope Cards
- Sorted by type group (BUDGET → SAVINGS → TRACKING), then alphabetical
- TRACKING envelopes: 30-day sparkline (daily balance snapshots, computed client-side)
- Cross-account envelopes: globe icon indicator
Exchange Rates
budget_exchange_ratetable:from_currency,to_currency,rate,fetched_at- Fetched by edge function from ECB/exchangerate.host
- Cached client-side via React Query
CurrencyProviderprovides HUF/EUR context
Related
- levandor-crm - Project overview
- agent-context-crm - Quick agent reference
- debugging-log-crm - Billingo sync debugging