Private
Public Access
1
0
Commit Graph

48 Commits

Author SHA1 Message Date
Catherine Renelle 713f2bfadc Fix date handling to use DateTimeKind.Utc for Npgsql
ParseDateArg now applies DateTime.SpecifyKind(Utc) since TryParse
returns Unspecified kind. Also fixed the spending report fallback
start date to use the DateTimeKind.Utc constructor overload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:39:56 -05:00
Catherine Renelle 02c1f49db5 Fix crypto.randomUUID not available in all environments
Replace with a simple counter-based temp ID generator for optimistic
UI messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:37:24 -05:00
Catherine Renelle 846edad2a4 Add AI chat with Ollama tool calling for financial queries
Conversational AI chat that uses Ollama's native tool-calling API to query
transactions, spending reports, account balances, dashboard summaries, and
update transaction categories through natural language. Includes persistent
conversation history, a full-page chat UI with sidebar, and rich inline
rendering of tool results (tables, alerts, cards).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:34:16 -05:00
Catherine Renelle 1ceeca0fb6 Add profile management (display name, email, password change, account deletion)
Full-stack profile section: DisplayName column on User model, profile CRUD
endpoints on AuthController, and complete Settings > Profile tab UI with
profile editing, password change, account info, and account deletion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 22:44:52 -05:00
Catherine Renelle 0562bb97b7 Fix SimpleFIN re-sync timeout and 403 rate limiting
Cache SimpleFIN API responses for 60 seconds so multiple accounts in
the same sync cycle reuse a single HTTP call instead of hitting the
API per-account. Pre-load existing transactions by FitId in one batch
query to skip per-duplicate FindAsync calls during memo backfill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:42:55 -05:00
Catherine Renelle f23bba53c0 Backfill memo on re-sync and fix transactions table name in migration
Update duplicate handling during bank sync to populate the memo field
on existing transactions when it's empty. Fix raw SQL table name from
quoted "Transactions" to lowercase "transactions" matching the actual
PostgreSQL schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:30:10 -05:00
Catherine Renelle 8cc40272c7 Fix server-side pagination returning same data on every page
v-data-table-server re-emits update:options when items/totalCount
change, creating a loop that resets to page 1. Guard against
redundant fetches by skipping when page and pageSize haven't
changed. Remove duplicate loadTransactions from onMounted since
the table's initial update:options event handles the first load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:12:41 -05:00
Catherine Renelle 757fcec621 Always store full bank description in memo for SimpleFIN
Use the raw Description field as memo so the full bank text
(e.g. "Card purchase BUYVM VPS* FRANTECH 11 MY.FRANTECH.C WY
02-08-2026") is always visible. The cleaned Payee field is still
used for the payee name and matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:04:39 -05:00
Catherine Renelle b48cb1c48c Mark existing bank-synced transactions as Cleared on startup
One-time migration: UPDATE all transactions that have a FitId
(bank-synced) and Status=Uncleared to Status=Cleared. Tracked
in data_migrations table so it only runs once.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:56:26 -05:00
Catherine Renelle d52c7b53ce Mark bank-synced transactions as Cleared instead of Uncleared
Both SimpleFIN and Plaid only return posted transactions (SimpleFIN
filters out pending, Plaid returns settled). These have already
cleared at the bank so they should be marked Cleared on import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:52:47 -05:00
Catherine Renelle caf109013e Switch transactions to server-side pagination
Replace v-data-table with v-data-table-server so page changes
fetch from the API instead of being limited to the first 50
results. Filters reset to page 1; page/size changes trigger a
new API call with the correct page and pageSize parameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:50:35 -05:00
Catherine Renelle 9a49adf89a Fix navigation drawer: rail expand, icon centering, and tooltips
- Make the entire header clickable to toggle rail mode so users
  can expand the drawer after collapsing it
- Center the cat icon in rail mode via scoped CSS
- Add tooltips on all nav items and logout when in rail mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:44:39 -05:00
Catherine Renelle 5811588698 Add Re-sync button for SimpleFIN connections
Resets LastSyncAt on all linked accounts so the next sync
re-fetches the full 90-day transaction window. Duplicate
detection prevents re-importing existing transactions.

- POST /api/bank-sync/connections/{id}/reset-sync endpoint
- Re-sync button shown only on SimpleFIN connection cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:29:15 -05:00
Catherine Renelle 8b2f5aad56 Normalize all DateTime values to UTC across the codebase
Npgsql requires DateTimeKind.Utc for timestamp with time zone
columns. This fixes errors in transaction create/edit, budgets,
reports, loans, scheduled transactions, and file imports.

Changes:
- TransactionService: SpecifyKind on request.Date for create,
  update, and transfer
- BudgetService: UTC kind on all new DateTime(year, month, 1)
- ReportService: UTC kind on endOfMonth; rewrite spending-by-
  category GroupBy to avoid untranslatable navigation properties
- LoanService: UTC kind on payoff scenario date construction
- ScheduledTransactionService: UTC kind on SemiMonthly date
- ImportService: SpecifyKind on parsed.Date when persisting
- OFX/CSV/QIF parsers: AssumeUniversal + AdjustToUniversal on
  all date parsing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 01:07:26 -05:00
Catherine Renelle bcb4a74158 Fix Dashboard top categories query translation error
EF Core cannot translate GroupBy with a navigation property
(Category.Name) joined via nullable FK into SQL. Split into two
queries: group by CategoryId server-side, then look up category
names in a separate query.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 00:34:52 -05:00
Catherine Renelle edc32367d4 Fix Dashboard query: replace Math.Abs with negation in GroupBy
Math.Abs() inside a GroupBy().Select() cannot be translated to
SQL by EF Core. Since the query already filters for Amount < 0,
negating the sum produces the same result and translates cleanly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 00:22:34 -05:00
Catherine Renelle 10bbca84be Make SimpleFIN split migration run only once
Track completion in a `data_migrations` table. On startup the
migration checks if it has already been applied and skips the
SimpleFIN API calls entirely, avoiding the slow startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 00:03:10 -05:00
Catherine Renelle 8ca2dbd763 Fix transaction action buttons layout and Dashboard UTC error
Wrap edit/delete buttons in a flex container so they render
side by side instead of stacking. Fix Dashboard DateTime to use
UTC kind, which Npgsql requires for timestamp comparisons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 23:58:28 -05:00
Catherine Renelle f92759bef3 Add startup migration to split existing SimpleFIN connections by institution
Runs once at startup after DB migrations. For each SimpleFIN
connection, re-discovers accounts from the API, groups by
institution, and splits connections that contain accounts from
multiple banks. Existing sync state and account mappings are
preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 23:41:11 -05:00
Catherine Renelle a2b12d9abd Split SimpleFIN connections by institution
Group discovered SimpleFIN accounts by institution and create a
separate SyncConnection per institution so each bank appears as
its own card in the UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 23:26:49 -05:00
Catherine Renelle 54a15cf3cf Fix account type not updating on edit
UpdateAccountRequest was missing the Type field, so changes to
account type were silently ignored. Add Type to the DTO and apply
it in the update service method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 21:44:09 -05:00
Catherine Renelle 53f49ba9d8 Run bank sync in background to avoid gateway timeouts
sync-now and sync-all now return 202 Accepted immediately and
dispatch work to a background task. The frontend uses the existing
SignalR BankSyncComplete notification to stop the spinner and
display results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 21:31:59 -05:00
Catherine Renelle fae15ad66b Return clear error when SimpleFIN setup token is already claimed
A 403 from the claim URL means the token was already used or is
invalid. Throw InvalidOperationException so the middleware returns
a 400 with a helpful message instead of a generic 500.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 21:10:07 -05:00
Catherine Renelle 1db0101b5a Fix SimpleFIN integration for beta-bridge.simplefin.org
HttpClient does not extract Basic Auth credentials from URLs with
embedded user:pass@host format. Parse the access URL, build the
Authorization header explicitly, and strip credentials from the
request URI. Also send Content-Length: 0 on token exchange POST
and update setup instructions to point to beta-bridge.simplefin.org.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 20:57:02 -05:00
Catherine Renelle 9497812bf5 Fix Reports 400 errors: normalize DateTime to UTC and restore camelCase JSON
Query-bound DateTimes have Kind=Unspecified which Npgsql rejects for
timestamptz columns, causing InvalidOperationException caught as 400.
Also add explicit CamelCase naming policy lost when AddJsonOptions was
introduced for the enum converter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 20:35:57 -05:00
Catherine Renelle d49ad4aca7 Use editable default values with {placeholders} for AI prompts
Instead of showing defaults as greyed-out placeholder text, populate
textareas with the actual default prompt text so users can directly
edit it. Add {categories}, {examples}, and {transactions} placeholders
so users control where dynamic data is inserted in the prompts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 20:26:07 -05:00
Catherine Renelle e742b0fbe7 Make AI categorization prompts user-configurable
Add SystemPrompt and UserPromptTemplate fields to AI categorization
settings so users can customize the prompts sent to Ollama without
code changes. Null/empty values fall back to built-in defaults.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 19:30:26 -05:00
Catherine Renelle 1c7abb7ffc Add AI categorization with Ollama: settings, auto-classify on sync/import, and manual classify from Transactions
Adds local LLM-based transaction categorization using Ollama:
- Settings UI to configure Ollama URL, model, confidence threshold
- Auto-categorization during bank sync and file import
- Manual "Classify Uncategorized" button on Transactions screen that respects active filters
- Payee default category learning loop for classified transactions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:41:12 -05:00
Catherine Renelle 57d1051213 Add JsonStringEnumConverter for proper enum serialization
Without this, enums serialize as integers (0, 1, 2) instead of
strings ("Debit", "Credit", "Transfer"). This caused synced
transactions to have numeric type values and broke editing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 17:12:08 -05:00
Catherine Renelle 5e7c664896 Fix Unspecified DateTime kind for PostgreSQL timestamptz columns
DateOnly.ToDateTime() produces Kind=Unspecified which Npgsql rejects
for timestamptz. Specify UTC kind on Plaid transaction dates and add
a safety net in BankSyncService for all parsed dates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 17:02:02 -05:00
Catherine Renelle 9d6a006045 Make Plaid credentials per-user, configurable via Settings UI
Plaid ClientId/Secret are now stored per-user in the database
(encrypted) instead of in appsettings.json. Users enter their own
Plaid developer credentials in the Settings > Bank Connections tab.

- New BankSyncSettings model (1:1 with User) for encrypted credentials
- PlaidSyncProvider is now stateless, receives credentials per-call
- Settings UI shows a credentials card with save form
- Connect via Plaid button disabled until credentials are configured
- Removed Plaid section from appsettings.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:50:48 -05:00
Catherine Renelle 849a392d14 Fix Plaid link token error handling and show user-visible errors
Validate Plaid credentials are configured before calling the API,
and validate the returned link token before passing it to Plaid
Link JS. Show error snackbars for all bank sync failures instead
of only logging to console.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:29:16 -05:00
Catherine Renelle 6e80bcf98b Fix Settings view rendering duplicate layout
Remove nested AppLayout wrapper from SettingsView — the layout is
already applied at the App.vue level around router-view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:23:31 -05:00
Catherine Renelle 3e0446c9be Implement Phase 6: automated bank sync (Plaid + SimpleFIN) & settings
Add automated transaction syncing via Plaid and SimpleFIN providers,
a Settings page for managing connections and account mappings,
background scheduled syncing, and sync history logging.

New backend: BankSync plugin project with PlaidSyncProvider and
SimpleFinSyncProvider, SyncConnection/LinkedAccount/SyncLog models,
AES-256 encryption service, BankSyncService orchestration,
BankSyncBackgroundService (15-min tick), and BankSyncController
(13 endpoints). EF migration creates sync_connections,
linked_accounts, and sync_logs tables.

New frontend: Settings view with 3 tabs (Profile, Bank Connections,
Sync History), bankSync API service, BankSyncComplete SignalR
notifications, and Settings nav item in sidebar + avatar dropdown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 15:08:23 -05:00
Catherine Renelle eb49147080 Implement Phase 5: reconciliation, SignalR notifications, payees, splits & plugins
Add full frontend UI for five backend features: bank reconciliation (3-step
stepper workflow), payee management (CRUD with alias chips), transaction splits
(expandable rows + split editor), plugins (card grid), and real-time SignalR
notifications (bell dropdown with badge). Fix backend PayeeResponse to expose
alias IDs for deletion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:14:23 -05:00
Catherine Renelle d0c3e3913c Fix reports view showing error snackbar when no data exists
Wrap each report API call in a fetchReport helper that catches errors
individually and returns null, treating 4xx responses as empty data
instead of surfacing an error message to the user.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 13:15:31 -05:00
Catherine Renelle 6ae90c0c38 Implement Phase 4: reports & investments frontend
Add interactive reports dashboard with 4 chart types (spending by
category donut, income vs expense bar, net worth area, cash flow bar)
using ApexCharts, date range presets, and account filtering. Add
investment portfolio view with holdings table, allocation chart, and
gain/loss color coding. Includes TypeScript types and API services.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 13:02:14 -05:00
Catherine Renelle 23d73736c9 Implement Phase 3: financial features frontend (budgets, scheduled transactions, loans)
Add API service files and replace stub views with full implementations:
- Budgets: month navigator, budget items table with progress bars, create/edit/delete
- Scheduled Transactions: data table with skip/post-now actions, add/edit dialog
- Loans: loan list, detail view with amortization schedule, payoff scenario calculator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:14:43 -05:00
Catherine Renelle 2ace39c544 Implement Phase 2: end-to-end import system
Complete the import flow from file upload through duplicate resolution
to transaction creation. Previously, ResolveDuplicatesAsync was a stub
that only incremented counters, FileWatcherService detected files but
didn't process them, and ImportsView.vue ignored the API response.

Backend:
- Add ParsedDataJson column to ImportBatch to persist parsed data
  between upload and confirm steps
- Rewrite ImportService.ResolveDuplicatesAsync to deserialize parsed
  transactions and create/merge/skip based on user decisions, with
  payee matching, fingerprinting, and account balance updates
- Add GetBatchesAsync for import history
- Complete FileWatcherService: wait for file ready, resolve account by
  filename, call UploadAsync, send SignalR notification, archive file
- Add GET /api/imports/batches endpoint

Frontend:
- Create imports API service (frontend/src/services/imports.ts)
- Add ImportBatch, DuplicateResolution types
- Rewrite ImportsView.vue with 3-step Vuetify stepper wizard:
  upload, review with duplicate handling, completion summary,
  plus import history table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 11:16:39 -05:00
Catherine Renelle 0b6ff8faf1 Add initial EF Core migration
Creates all database tables: users, accounts, transactions,
categories, payees, budgets, scheduled_transactions, loans,
investments, reconciliations, plugins, and import batches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 10:16:48 -05:00
Catherine Renelle 524176faec Fix .gitignore excluding frontend plugins and test files
The broad plugins/ and imports/ patterns were matching directories
at all levels, excluding frontend/src/plugins/vuetify.ts,
frontend/src/views/imports/, frontend/src/views/plugins/, and
src/Purrse.Tests/Plugins/. Changed to /plugins/ and /imports/
to only match root-level directories.

This was the root cause of the Docker build failure - vuetify.ts
was missing from the repo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 10:04:19 -05:00
Catherine Renelle 79821e3e04 Add .dockerignore files and increase Node memory limit
Prevents node_modules/dist from polluting Docker build context.
Sets NODE_OPTIONS max-old-space-size=4096 for Vite builds on
memory-constrained servers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:53:47 -05:00
Catherine Renelle 7ebff12b33 Use full node:22 image for frontend build stage
Alpine was causing native dependency issues with sass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:18:31 -05:00
Catherine Renelle 9959e7dbdc Harden frontend Dockerfile for constrained environments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:17:10 -05:00
Catherine Renelle b46e62731e Separate typecheck from build for faster Docker builds
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:15:02 -05:00
Catherine Renelle 8ee4a7b2c9 Add package-lock.json for reproducible frontend builds
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:12:37 -05:00
Catherine Renelle 3db02f8472 Change frontend port to 9080 to avoid conflicts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:06:29 -05:00
Catherine Renelle 6520ebf221 Initial commit: Purrse personal finance app
Self-hosted, plugin-extensible personal finance manager built with
ASP.NET Core 9.0, Vue 3 + Vuetify 3, and PostgreSQL 17.

Backend (8 .NET projects):
- Core: 19 domain models, 6 enums, 14 DTOs, 12 service interfaces
- Data: EF Core DbContext, 16 entity configurations, category seeder
- API: 14 controllers, 15 services, JWT auth, SignalR, middleware
- Plugins: Abstractions + OFX/CSV/QIF file parsers
- Tests: 28 xUnit tests (fingerprinting, duplicate detection, parsers)

Frontend (Vue 3 + Vuetify 3 + TypeScript):
- 13 views, Pinia stores, Axios API services with JWT interceptors
- Dashboard, accounts, transactions, categories, imports, and more

Deployment:
- Docker Compose (PostgreSQL 17 + .NET API + nginx frontend)
- Auto-migration on startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 08:56:46 -05:00