Skip to content

Finman overhaul: money integrity, AI CFO, real OCR, FX net worth, bank sync, deploy, 137 tests#2

Open
ctmakc wants to merge 2 commits into
mainfrom
feat/finman-overhaul
Open

Finman overhaul: money integrity, AI CFO, real OCR, FX net worth, bank sync, deploy, 137 tests#2
ctmakc wants to merge 2 commits into
mainfrom
feat/finman-overhaul

Conversation

@ctmakc

@ctmakc ctmakc commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Finman overhaul — make it real, honest, tested & deployable

Implements the multi-agent audit roadmap. Additive — no existing feature removed; main-era functionality preserved.

Foundation

  • Shared libs: lib/money.js (decimal-safe), lib/respond.js, lib/logger.js (pino), lib/validateEnv.js, lib/ai/provider.js (provider-agnostic: anthropic/openai/ollama, env-gated)
  • Middleware: AppError+errorHandler+notFound, requireOwnership, requireTier
  • Migration system (lib/migrate.js + migrations/) + schema_migrations
  • Test harness (Jest+Supertest, isolated temp DB) — 0 → 137 tests passing
  • server.js wired additively; unknown /api/* now hits 404 instead of being swallowed by the SPA catch-all

Fixes & features

  • Fixed live crash Split.getExpenses (500 on every call) + cent-exact settlement math
  • Fixed pre-existing 500 on GET /api/transactions (query var shadowed the db helper)
  • Money integrity: lib/money rounding across core models; debt-overpay rejection; investment P&L includes fees
  • Net worth is FX-aware (was summing RUB+UAH+USD naively) + daily snapshots
  • Real AI CFO (/api/ai/*, grounded in the user's own data, requireTier('pro'), graceful 503 without key)
  • Real OCR on tesseract.js (replaced the random-data fake) + receipt→transaction flow
  • Background bank auto-sync (node-cron over the existing real Monobank/Revolut/Tinkoff integration, de-dup, SYNC_ENABLED)
  • Forecast upgraded to seasonality + trend + confidence band; anomaly/duplicate-charge detection
  • Frontend: i18n (uk/en/ru), a11y helpers, reusable CRUD/modal lib
  • Deploy: Dockerfile (non-root, healthcheck) + compose (persistent data/ volume) + GitHub Actions CI + README
  • Billing scaffold (Stripe behind env) + tier gating + DEMO flag

Status

  • Functional now: split, money, net-worth FX, OCR, forecast/anomaly, i18n, deploy artifacts, 137 tests
  • 🔑 Scaffolded, needs keys (degrade safely to 503/disabled): AI (AI_API_KEY), Stripe (STRIPE_SECRET_KEY), live bank sync (SYNC_ENABLED + tokens)
  • Before prod: set JWT_SECRET/SESSION_SECRET/ENCRYPTION_KEY

🤖 Generated with Claude Code

ctmakc and others added 2 commits June 19, 2026 20:36
- lib/money, lib/respond, lib/logger, lib/validateEnv, lib/ai/provider
- middleware/error (AppError+errorHandler+notFound), authorize, requireTier
- lib/migrate + migrations/ (subscription_tier, stripe ids, ai_conversations, ai_messages)
- routes/health (+ready); stub routers ai/billing/sync/anomalies
- server.js: pino-http, validateEnv, new mounts, SPA skips /api/*, notFound+errorHandler, exports app
- test harness (jest.config, test/helpers/app, test/foundation.test) — 11 tests green
- index.html: i18n.js + ai.js script hooks

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, real OCR, bank sync scheduler, forecast/anomaly, i18n/a11y, deploy, billing

Integrator pass: wire feature streams together, fix cross-cutting bugs, all 137 tests green.

Fixes applied during integration:
- test/helpers/app.js: call jest.resetModules() in makeApp() so multi-makeApp test
  files get a fresh DB module instead of a closed sqlite handle (was SQLITE_MISUSE:
  Database is closed across billing/ai/money/networth/sync suites).
- models/transaction.js: rename local SQL-builder var `query` -> `sql` in
  findByUserId and the stats aggregator; the local var shadowed the imported
  db `query()` function, causing "query is not a function" 500s on GET /api/transactions.
- test/split.test.js: correct the settlement zero-convergence assertion to
  balance - net (creditor's positive balance is cleared by receiving), matching the
  model's documented sign convention and the test's own net[] assertions.
- server.js: mount express.raw on /api/billing/webhook before express.json (Stripe
  signature needs the raw body); start the bank sync scheduler at boot only when
  SYNC_ENABLED (inside require.main === module, so tests never auto-start it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3712c36e9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +243 to +246
} else {
// Без секрета — только в dev: доверяем телу как есть.
try {
event = typeof rawBody === 'string' ? JSON.parse(rawBody) : JSON.parse(rawBody.toString('utf8'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require Stripe webhook signatures before tier changes

When STRIPE_SECRET_KEY is configured but STRIPE_WEBHOOK_SECRET is left unset, this public webhook path accepts unsigned JSON and still processes checkout.session.completed / subscription events that call applyTier. Because resolveUserId trusts event metadata/customer data, an attacker can POST a fabricated event to /api/billing/webhook and upgrade arbitrary users to pro/family; treat a missing webhook secret as disabled/error outside an explicit local dev/test mode.

Useful? React with 👍 / 👎.

Comment thread routes/forecast.js
const expense = Number(r.expense) || 0;
return {
t: dayIndex,
net: income - expense,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize expense signs before forecasting

For users with at least 5 history days, the new seasonal model uses this net value, but expense transactions are stored as negative amounts in the app, so income - expense adds spending to income. A day with 100 income and -40 expense is modeled as +140 net instead of +60, making projected balances and confidence bands overly optimistic; convert expense rows to positive spend with ABS/-SUM(amount) or add the signed expense directly.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant