Budget System
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 Engine
Automatic transaction categorization from bank imports. Rules are applied in priority order against transaction metadata.
| 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, field transforms |
Mutation Hooks (useBudgetMutations.ts)
18 mutation hooks covering the full budget domain:
| Hook | Purpose |
|---|---|
useCreateBudgetMappingRule | Create new mapping rule |
useUpdateBudgetMappingRule | Edit existing rule |
useDeleteBudgetMappingRule | Remove rule |
useReorderBudgetMappingRules | Batch priority update |
useApplyMappingRuleRetroactively | Apply a rule to all existing transactions |
useBulkImportTransactions | CSV bank import with upsert (dedup by source_id) |
useReconcileBankTransactions | Match bank imports to existing manual transactions |
useCreateBudgetTransfer | Linked TRANSFER pair (outgoing + incoming) |
useConfirmPlannedTransaction | Mark planned transaction as confirmed |
Retroactive Rule Application
useApplyMappingRuleRetroactively fetches all transactions for the rule’s accounts, evaluates conditions against each transaction’s ext and metadata fields using evaluateConditionGroup() from lib/mapping-engine.ts, and applies matched actions (envelope assignment, field updates).
Query 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) with focused field highlighting - 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 - Frontend can call without webhook secret (fixed in
eb2c444)
UI Components
| Component | File | Purpose |
|---|---|---|
| BudgetOverview | BudgetOverview.tsx (646 lines) | 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 with field highlighting |
| EnvelopeDialog | EnvelopeDialog.tsx | Create/edit envelope form |
| CreateAccountDialog | CreateAccountDialog.tsx | Bank account creation |
| TransferDialog | TransferDialog.tsx | Inter-account transfer form |
| Sparkline | Sparkline.tsx | 30-day balance sparkline chart |
Status Badges
- Color-coded: green=PAID, red=OVERDUE, yellow=OUTSTANDING
- Record-based lookups for status to 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
- Bank account cards show bank icons (Erste, Revolut pattern matching) with editable IBAN/SWIFT fields
Exchange Rates
Simplified in March 2026
The dedicated
budget_exchange_ratetable anduseExchangeRateshook were removed in commit853729a. TheCurrencyProvider(using a CDN feed) is the single source of truth for EUR/HUF rates.
CurrencyProvideratweb/src/lib/currency.tsxprovides HUF/EUR/USD context- Rates fetched from CDN, cached via React Query
market-dataedge function provides commodity prices (Gold, Brent Oil, EU Gas TTF) from Yahoo Finance — this is for the dashboard display, not for currency conversion
Related
- levandor-crm - Project overview
- agent-context-crm - Quick agent reference
- integrations - Billingo integration details
- debugging-log-crm - Billingo sync debugging
- tech-debt - BudgetOverview at 646 lines