Commit Graph

51 Commits

Author SHA1 Message Date
siddharthd 5df164e9a5 feat(secrets): add SOPS+age encryption for .env
.env encrypted to .env.sops using shared Unraid age key. .gitignore
updated to allow .env.sops while still blocking plain .env.
2026-05-15 19:28:13 +10:00
siddharthd b8cd1b0f89 fix(reconcile): prevent split/tag double-counting on reconciled transactions
Move splits, tags and overrides from manual to statement side on reconcile
(delete from manual after copying) instead of just copying. Add read-time
filter to exclude reconciled manual transactions from balance and shared
transaction queries. Also adds participant filter to shared expenses page.
2026-05-11 19:15:41 +10:00
siddharthd ce67e38d77 docs: update CLAUDE.md and README to reflect recent changes
- Add reconciled_with_id and created_at columns to transactions table docs
- Document split_payments, expense_metadata, rule_apply_runs tables
- Update /api/transactions route docs with has_split filter and all sort options
- Add /api/transactions/reconcile and /api/split-payments to API table
- Document import date (created_at) behaviour and reconciliation caveat
- Add Prisma regeneration note to CLAUDE.md
- Note schema drift for tables added without migration files
2026-05-10 16:52:55 +10:00
siddharthd b8296b6e29 fix(prisma): sync schema with DB
Add missing models: statements, transactions, expense_metadata, rule_apply_runs.
Fixes pre-existing type errors on split_payments and my_share_percent which
were caused by a stale generated client (regenerate with npx prisma generate).
2026-05-10 16:13:40 +10:00
siddharthd 0c1f88ed9c feat(transactions): add imported date column, split filter, and sortable columns
- Show created_at as "Imported" column in transactions and shared views
- For reconciled transactions, show original CSV import date (not statement processing date) via LEFT JOIN on reconciled_with_id
- Add has_split filter (all/split only/unsplit only) to transactions page
- Transactions table: sortable by imported date; split filter dropdown
- Shared table: client-side sort by date, imported, and amount
2026-05-10 16:04:54 +10:00
siddharthd 4a49add277 feat: CSV import and batch reconciliation UI
- Add reconciled_with_id column to transactions (links manual → statement tx)
- CSV import wizard: 4-step modal (upload → map columns → review → done)
  - Handles any bank format via column mapping with localStorage presets
  - Single signed or separate debit/credit column modes
  - Editable preview table before committing
  - Auto-tags all imported rows with 'csv-import'
- Batch reconcile page: shows all unreconciled manual transactions with
  potential statement matches (date ±3 days, amount ±1%) pre-fetched
  - Select matches across multiple rows, apply all at once
  - Copies overrides/tags/splits from manual → statement tx atomically
  - Manual tx marked reconciled (linked), hidden from main transactions view
  - Transactions with no matches shown separately
- Import CSV button on transactions page
- Reconcile nav item in sidebar
2026-04-13 06:23:08 +10:00
siddharthd 07b8c1ef16 fix(shared): exclude payments from balance when tag filter is active 2026-04-13 05:41:00 +10:00
siddharthd 1b561af9e9 feat(filters): add No tags filter to transactions and shared pages 2026-04-13 05:20:49 +10:00
siddharthd 1296555f17 test: add unit and integration test suites
- Extract evaluateCondition + rule types into src/lib/rules.ts for testability
- 48 unit tests for evaluateCondition (all fields/operators) and formatCategory
- 21 integration tests for getTransactions filters and getParticipantBalances
- Vitest configs for unit (vitest.config.ts) and integration (vitest.integration.config.ts)
- setup-test-db.sh creates personal_test DB from production schema via pg_dump
- Use vi.doMock + dynamic import pattern to isolate test DB from Prisma singleton
2026-04-01 19:59:29 +11:00
siddharthd 7491e70a15 fix(participants): show Me contextually per logged-in user
Participant id=1 was named "Me" in the DB, causing Sonu and other users
to see "Me" referring to Siddharth when viewing splits and shared expenses.

- Rename participant id=1 from "Me" to "Siddharth" in the DB
- /api/participants now substitutes "Me" for whichever participant matches
  the current user, so the label is always relative to the viewer
- split-modal: default split uses currentUser.id instead of name === "Me"
- transactions/page: filter and display logic uses participant ID not name
- shared/page: split chips show "Me" when participant_id === current user

Also includes add-transaction-modal tags support (pre-existing staged change).
2026-04-01 18:36:29 +11:00
siddharthd 0a1f6b48a2 feat(ui): mobile-responsive sidebar + rules improvements
- Sidebar: hidden on mobile, opens as slide-out drawer with hamburger
  toggle; auto-closes on navigation; desktop layout unchanged
- Layout: responsive padding accounting for mobile header bar
- Rules: add tag as a condition field (has/not-has tag)
- Rules: apply a single rule via per-rule Apply button
- Rules: splits-from defaults to 2026-01-09
2026-03-21 08:33:08 +11:00
siddharthd ef73a9cea0 fix(payment): participantId stuck as empty string when participants load async 2026-03-14 21:33:25 +11:00
siddharthd d53d3106f2 fix(shared): tag filter SQL precedence, balance cards filter by tag 2026-03-14 21:30:33 +11:00
siddharthd 02ac136e19 fix(payment): crash on open due to amount.toFixed on numeric string
feat(shared): tag filter on shared transactions list
2026-03-14 21:27:08 +11:00
siddharthd 084b8764e3 feat(transactions): Payment button to record existing transaction as debt payment 2026-03-14 21:20:25 +11:00
siddharthd 281f0d3782 fix(shared): show full description and notes in split transactions table 2026-03-14 21:11:34 +11:00
siddharthd 85e7801407 feat(shared): replace settle buttons with payment ledger
- New split_payments table records actual payments between participants
- Balance = total split obligations - total payments (splits never marked settled)
- Record Payment modal per participant: direction toggle, amount pre-filled with balance, date, notes
- Payment history inline on each balance card with +/- display and delete
- Per-transaction Settle button removed; Action column removed from shared table
- Splits always show the true cost breakdown regardless of payment state
2026-03-14 21:09:00 +11:00
siddharthd 5206388958 feat(filters): smart query bar with amount operators and multi-select dropdowns
- Query bar parses >500, >=500, <500, <=500, 500-1500 into amount_min/max filters
- Parsed tokens shown as dismissable chips below the query bar
- Category, Bank, Tag, Type filters upgraded from single-select to multi-select
- MultiSelect dropdown component with checkbox list and active-state border
- Backend: TransactionFilters uses string[] for categories/bank_names/tag_ids/transaction_types
- SQL: ANY($n::text[]) / ANY($n::int[]) for array filters
2026-03-14 20:39:28 +11:00
siddharthd 8076d1a949 docs: update README and add CLAUDE.md for finance app 2026-03-14 20:06:37 +11:00
siddharthd aeaca84cc7 feat(edit-transaction): edit modal with notes, inline tags, and split management
- New EditTransactionModal with scrollable body (sticky header/footer)
- Statement transactions: read-only core fields; manual transactions: editable date/amount/description
- Override fields for all: merchant, category, type, notes (textarea)
- InlineTags sub-component: add/remove tags without dropdown clipping issues
- Live split display via useTransactionSplits, opens SplitModal for editing
- PATCH /api/transactions/:id extended for description/amount/transaction_date (manual only)
- Transactions page: edit button per row, notes shown below description in italic
2026-03-14 20:06:32 +11:00
siddharthd 278e57354c feat(insights): analytics drill-down, fee tracking, and category improvements
- Monthly spend chart with category breakdown drill-down
- Merchant frequency and spend analytics with per-merchant history
- Subscription detection and recurring charge tracking
- Fee and interest analytics endpoint
- Expanded category list with formatCategory display helper
2026-03-14 20:06:24 +11:00
siddharthd 9f90d8726f feat(rules): apply_split rules with run history and revert
- POST /api/rules/apply — run all enabled rules against unmatched transactions
- POST /api/rules/apply/:id — apply a single rule by id
- DELETE /api/rules/apply/:id — revert a rule run (remove applied splits)
- Rules page: show run history with revert button, apply individual rules
2026-03-14 20:06:19 +11:00
siddharthd 859043f5a5 feat(shared): bidirectional split balance, credit direction, and multi-user view
- Rewrite participant balance to UNION both directions (they owe me + I owe them)
- Credits/refunds subtract from owed amount for correct net balance
- Allow secondary users to see transactions split with them
- Add participant balance cards with colour-coded owe direction
- Add inline AddParticipantForm with name + optional email
2026-03-14 20:06:13 +11:00
siddharthd fc22a61a43 feat(transactions): manual transaction support and multi-owner query infrastructure
- Add POST /api/transactions to create manual transactions (statement_id=NULL, owner_id set directly)
- Queries switch from JOIN to LEFT JOIN statements so manual transactions are visible
- COALESCE(t.owner_id, s.owner_id) throughout for owner resolution
- Add "Manual" bank filter option in getTransactions
- Search extended to include merchant_normalized override
- Split data fetched via lateral subquery on every transaction row
- getParticipantBalances rewritten as UNION for bidirectional net balances
  (credits/refunds negate, split from either side of the relationship)
- getSharedTransactions: remove my_share_percent from SELECT (fixes GROUP BY error),
  WHERE rewritten as two distinct cases (owner with others split vs participant on others' txn)
- getTransactions: OR EXISTS condition so split participants see shared transactions
- add-transaction-modal component for creating manual transactions with splits
- 0008_my_share_percent migration adds my_share_percent to transaction_overrides
2026-03-14 20:04:00 +11:00
siddharthd 0985c38be8 fix(rules): save-as-rule uses full merchant name with equals, not first description word 2026-03-11 12:46:16 +11:00
siddharthd af4c64bba7 feat(splits): save split as rule from split modal
- Checkbox in split modal: 'Also save as rule for <merchant>'
- Creates a rule with apply_split action storing the participant shares
- Rules engine now handles apply_split: deletes existing splits and re-applies
- Bulk split mode hides the checkbox (rule wouldn't make sense for ad-hoc bulk)
2026-03-11 12:40:03 +11:00
siddharthd a8743ba7df feat(transactions): save-as-rule prompt after merchant/category edit
After changing a merchant name or category inline, a toast-style prompt
appears offering to save it as a rule. Shows a preview of the condition
and action before saving. Dismissable without creating a rule.
2026-03-11 12:05:35 +11:00
siddharthd 7b3fd4b65f fix(merchants): net spend accounting for refunds/credits
- Merchant totals now show net spend (gross debits minus refunds)
- Refund count and amount shown in profile drawer and table
- Scatter plot Y-axis uses net_spend, X-axis uses debit_count
- Per-merchant transaction history includes refunds (shown as negative)
- Monthly trend chart reflects net spend per month
2026-03-10 00:43:58 +11:00
siddharthd dd11019fdf fix(transactions): editable transaction type, fee/interest counted as spend, fees category
- TypeBadge is now clickable — opens inline select to change debit/credit/fee/interest/etc.
- PATCH /api/transactions/[id] now accepts transaction_type, updates transactions table directly
- Analytics monthly query includes fee and interest types as spend (not just debit)
- fee and interest amounts show red in transaction list (same as debit)
- Add fees category to taxonomy
2026-03-10 00:24:42 +11:00
siddharthd 714c5a9b25 feat(merchants): scatter plot, merchant profiles, and per-merchant transaction history
- /merchants page with spend-vs-frequency scatter chart (click to open profile)
- Merchant profile drawer: stats, monthly trend line, full transaction history
- /api/analytics/merchants: split-adjusted merchant aggregates + monthly trends
- /api/analytics/merchants/[merchant]: per-merchant transaction list
- Add Merchants nav item to sidebar
2026-03-10 00:05:48 +11:00
siddharthd 2a10450c3e feat(analytics): replace charts with category trend lines, Pareto chart, and cumulative spend
- Category spend trend lines (top 8 categories, 6-month view) replacing stacked bar chart
- Pareto chart showing 80/20 spend concentration with cumulative % line
- Cumulative spend chart tracking actual vs typical monthly pace
- Fix: add amount_aud to TransactionRow interface
2026-03-09 23:59:07 +11:00
siddharthd e72d3ad9e5 feat(categories): add home_goods and home_maintenance categories 2026-03-09 23:37:28 +11:00
siddharthd a7461ff83b docs: replace boilerplate README with full data model and architecture reference 2026-03-09 23:12:20 +11:00
siddharthd c1d031511a feat(insights): committed/discretionary chart, recurring charge detection, fees & interest audit 2026-03-09 23:04:52 +11:00
siddharthd 7379437cc3 feat(statements): add bank/type/owner/year filters and row numbering 2026-03-09 22:27:21 +11:00
siddharthd 8bd7d77a8a fix(statements): owner assignment dropdown, fix Wise CC false positive, remove amount label
- Add owner <select> dropdown per row using useUpdateStatement + useParticipants
- Detect CC by statement_type.includes('card') instead of credit_limit/payment_due_date
  (Wise multi-currency account had payment_due_date set but is not a CC)
- Amount: remove 'due'/'balance' label; color green for positive bank balances, red for CC/overdraft
- Add statement_type to StatementRow type
2026-03-09 21:05:34 +11:00
siddharthd f90ba332bd feat(statements): table layout + statement-scoped transaction view
- Statements page: replace card grid with compact table showing bank,
  account, period, due date, currency, amount (due for CC / balance for
  bank), transaction count, and View button
- Transactions page: wrap in Suspense, read statement_id from URL search
  params on load; show a dismissible indigo banner with bank name and
  billing period when filtering by statement; × Clear filter removes it
2026-03-09 12:03:39 +11:00
siddharthd e3aa17acdd fix(analytics): cast tx.amount to Number before formatting (PG returns string) 2026-03-08 21:06:14 +11:00
siddharthd 1eff0f9337 fix(analytics): use React.Fragment with key for expandable category rows 2026-03-08 21:03:53 +11:00
siddharthd 3cf67f6e2a feat(analytics): stacked category chart, savings rate line, expandable rows
- Replace grouped cashflow BarChart with ComposedChart: expense categories
  as colour-coded stacked bars + amber savings-rate % line on right Y-axis
- Add category colour legend below chart (matches stacked bars)
- Horizontal category bar chart now uses per-category colours
- Breakdown table: click any category row to expand/collapse individual
  transactions; each transaction has an inline category dropdown that
  calls PATCH /api/transactions/:id → transaction_overrides, then
  invalidates analytics query so totals update immediately
2026-03-08 20:53:55 +11:00
siddharthd 90d8db4abe chore: add migration 0007 for amount_aud + exchange_rate_to_aud columns 2026-03-08 19:16:51 +11:00
siddharthd d1a0eedf03 feat(analytics): cashflow view with income/investment/net — split-adjusted + multi-currency
- Add 'investment' category (shares, ETFs, super)
- Analytics API: separate income, investment, expense queries; use amount_aud for FX-aware sums
- Analytics page: cashflow summary (income/expenses/invested/net cash), grouped bar chart,
  income + invested rows in 6-month trend table
- MonthlyAnalytics interface: add income, investments, net fields to totals
- DB: amount_aud + exchange_rate_to_aud columns added and backfilled (in prior migration)
2026-03-08 19:15:20 +11:00
siddharthd 5dbeb0cb87 chore: commit previously untracked runtime files (splits, auth, participants, shared) 2026-03-08 18:00:46 +11:00
siddharthd 30a7857d13 feat(analytics): replace budget page with spending analytics + split-adjusted amounts
- Rename 'Budget' → 'Analytics' in sidebar
- Rewrite /budget page: summary cards, recharts bar charts (monthly trend + category breakdown), 6-month trend table
- Fix analytics API to count only user's share for split transactions (CASE WHEN ts.share_percent IS NOT NULL THEN amount * share_percent / 100 ELSE amount END)
- Install recharts
2026-03-08 17:58:33 +11:00
siddharthd 1e79ada6d8 feat(finance): implement Shared Expenses page
Show split transactions with per-participant balance cards and settle buttons.
2026-03-08 17:24:04 +11:00
siddharthd be85822cc7 merge: Phase 5 (Rules Engine) + Phase 6 (Budget & Analytics)
Resolve additive conflicts in schema.prisma and hooks.ts — both models and all hooks retained.
2026-03-08 17:09:57 +11:00
siddharthd d455738732 feat(finance): Phase 6 — Budget & Analytics
Add monthly budgets per category with spend-vs-budget dashboard and 6-month trend table.
Includes upsert budget API, monthly analytics endpoint, inline budget editing, and route auth fixes.
2026-03-08 16:57:33 +11:00
siddharthd 31cffbe1bb feat(finance): Phase 5 — Rules Engine
Add rules engine with CRUD API, condition/action evaluation, and apply-all endpoint.
UI: rule builder form with field/operator/value conditions, tag multi-select, apply button with result stats.
2026-03-08 16:56:13 +11:00
siddharthd 93450f7caa feat(finance): Phase 4 — Tags
- tags table: name, color; transaction_tags junction table
- GET/POST /api/tags; DELETE /api/tags/:id
- POST/DELETE /api/transactions/:id/tags for per-transaction tagging
- Bulk tag/untag via /api/transactions/bulk (action: tag/untag)
- Tags returned inline with transaction list via LATERAL join
- Tag filter on Transactions page
- Bulk "Tag as..." in bulk action bar
- Tag pills + "+" picker on each transaction row
- /tags page: create with color picker, list with counts, delete
2026-03-08 16:28:03 +11:00
siddharthd 35a5be97b0 feat: initial finance SPA — phases 1 & 2
Next.js 16 personal finance dashboard connected to postgres-personal.

Phase 1 (Foundation):
- API routes: GET /api/transactions (paginated, filterable, sortable),
  GET /api/statements, GET /api/merchants
- Transactions data table with date/category/bank/search filters, pagination, sort
- Statements card grid with period, due date, amount, transaction count
- Sidebar layout with nav for all planned sections

Phase 2 (Normalisation):
- PATCH /api/transactions/[id] — upsert transaction_overrides
- POST /api/transactions/bulk — bulk categorize/normalize
- Inline click-to-edit category (22 options) and merchant name
- Blue dot override indicator, bulk action bar
- Effective values via COALESCE(override, llm_value) pattern

Stack: Next.js 16 (App Router, standalone), Prisma 7.x + @prisma/adapter-pg,
TanStack Query, Tailwind CSS. Auth via Traefik chain-oauth@file.
2026-03-07 23:31:40 +11:00