The data model is intentionally flat and narrow. Four tables are all that’s needed to represent a multi-department company’s income, expenses, and monthly budgets across several years. Departments and categories are small, static lookup tables that give every transaction a human-readable label without duplicating strings. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/astrxnomo/financial-analytics-agent/llms.txt
Use this file to discover all available pages before exploring further.
transactions table is the central fact table — every dollar that moved is one row there. The budgets table records what each department planned to spend each month, enabling the budget-vs-actual comparisons that getBudgetStatus powers. Keeping the model this lean means every analytic query is a straightforward GROUP BY with a couple of joins, which makes the SQL in agent/lib/finance.ts easy to read and fast to run.
Full Schema
Tables
departments
A fixed lookup of the five business units in the seeded dataset: Sales, Marketing, Engineering, Operations, and Finance. Each row is just an integer primary key and a unique name. The small, bounded set makes pie-chart breakdowns, budget rows, and anomaly filters predictable — every department always appears, even when it has no transactions in a given range (the profitability query uses aLEFT JOIN for exactly this reason).
categories
Each category is tagged with akind of either 'revenue' or 'expense'. This single column drives filtering across most analytics queries — getTrend, getAnomalies, and getCategoryBreakdown all use it to separate income sources from cost lines.
Revenue categories:
- Product Revenue
- Services Revenue
- Subscription Revenue
- Payroll
- SaaS
- Advertising
- Travel
- Office
- Cloud Infrastructure
- Contractors
- Recruiting
kind column has a CHECK constraint enforcing that only 'expense' or 'revenue' are valid values, so new categories can never be inserted with an ambiguous classification.
transactions
The main fact table — every income or expense event is one row here. Key columns:| Column | Type | Notes |
|---|---|---|
date | date | The calendar date of the transaction |
amount | numeric(12, 2) | Stored as exact decimal; never a float |
department_id | integer | FK → departments.id |
category_id | integer | FK → categories.id |
type | text | 'income' or 'expense' (CHECK-constrained) |
description | text | Human-readable label generated at seed time |
date ranges and aggregate amount. The type column mirrors the categories.kind of the linked category and is the primary split used by getSummary, getTrend, and getCashflow.
budgets
One row per department per month, recording the planned spend for that department in that calendar month. Themonth column always holds the first-of-month date (e.g. 2025-01-01). getBudgetStatus joins budgets against the actual expense total from transactions for the same month and department, then computes variance (actual − budget) and pctUsed (actual ÷ budget).
The seed script deliberately sets Engineering’s budget cushion tight so it shows up as the chronically over-budget department — a realistic story for budget charts to surface.
Indexes
Three indexes cover the query patterns used by every function inagent/lib/finance.ts:
| Index | Column(s) | Purpose |
|---|---|---|
idx_tx_date | transactions(date) | Date-range filters appear in every query |
idx_tx_dept | transactions(department_id) | Department joins and GROUP BYs |
idx_tx_cat | transactions(category_id) | Category joins and GROUP BYs |
(date, department_id) would benefit the trend-by-department query most.
Important Constraints
budgetsuniqueness —UNIQUE (department_id, month)enforces that at most one budget row exists per department per month. The seed script’sINSERT INTO budgetsrelies on this: attempting to reseed without a priorTRUNCATEwould fail on duplicate key violations rather than silently doubling budget amounts.transactions.type—CHECK (type IN ('income', 'expense'))prevents any value other than those two strings. The analytics library never needs to guard against unexpected type values.categories.kind—CHECK (kind IN ('expense', 'revenue'))mirrors the same constraint at the category level, ensuringkind-based filters in SQL queries are always exhaustive.