perf(api,web): bound transactions query by date, add /transactions/months - #21
Open
aafre wants to merge 24 commits into
Open
perf(api,web): bound transactions query by date, add /transactions/months#21aafre wants to merge 24 commits into
aafre wants to merge 24 commits into
Conversation
…X rates
Implements Phase 1 (narrow currency core) and the remaining Phase 2 defects from the
v0.2 plan, on top of work already staged on this branch.
Currency core:
- Settings.base_currency, an ISO-4217 SUPPORTED_CURRENCIES dict replacing the GBP-only
reject, and per-currency minor-unit exponents (JPY=0) applied consistently through a
single domain.money.minor_units helper.
- fx_rates table (migration 0004), FxRateRepository with at-or-before/inverse lookup,
domain.fx.to_base, and `pfa fx set/fetch/list` plus /fx/* endpoints. Rates cross every
boundary (API request/response, Frankfurter fetch) as decimal strings, never float.
- Analytics is per-currency throughout (monthly_summary, category/merchant totals,
cashflow, budgets, current_cash), with a mixed GBP/INR regression test.
- A row's currency is validated against its destination account's at preview time
(blocking issue, not a crash) and account currency lookup during preview no longer
creates an account as a side effect.
Statement extraction:
- Fixed: an AMEX payment's own-line "CR" marker was silently dropped during extraction,
so the statement's default debit-positive convention flipped it to spending. The
marker now attaches to its row and marks the direction explicit, which the sign
convention now respects everywhere (PDF debit/credit columns, CSV, inline/own-line CR).
- Fixed: parse_amount used a naive `* 100` instead of the currency's minor-unit exponent
and rounding, corrupting non-2dp currencies (JPY) and truncating instead of rounding.
- Fixed: year-less dates ("Jul31") always took today's year, even during a replay of an
old statement. Batches now infer the statement's year from any other year-bearing date
in the same file and normalize every date up front, with a visible warning when no
date carries a year at all.
- AMEX's duplicate "Date" column no longer leaks into the transaction description.
- Barclaycard two-column layout clustering and Money Out/Money In PDF header coverage.
Also fixes: ruff/format/mypy clean (was failing all three), restores a deleted PDF
header-alias regression test, and adds fetch_and_store_fx_rates coverage that proves
Frankfurter rates are parsed to Decimal rather than round-tripped through float.
pfa.config.get_settings() is @lru_cache'd. Any PdfStatementExtractor() built with
default settings (many unit tests do this) primes that cache with the real
sqlite:///data/pfa.db - and once cached, CliRunner(env={"PFA_DATABASE_URL": ...})
has no effect for the rest of the process, so test_cli_fx_commands ran against
whatever DB happened to be cached first instead of its own tmp_path DB.
Reproduced locally by forcing collection order:
pytest tests/unit/test_pdf_extractor.py tests/integration/test_cli.py
and confirmed the autouse cache-clear fixture fixes it regardless of order.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8foThd2rk578JQmV3JHLQ
…ion WIP Snapshot of in-progress statement-upload work: HDFC India delimited adapter, PDF extractor hardening, reconciliation coverage, amount-sign handling, and dashboard wiring for the new batch fields. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxBdKWe1SS9LP5ZBCT7m4M
The dashboard opened on the calendar-current month (almost always empty), loaded only a single month, and hard-coded a single currency, so a real ledger looked broken: £0.00 everywhere, self-referential "vs last month" deltas, an empty 3-month cashflow chart, and every non-default-currency account and month unreachable. - Land on the most recent month that actually has transactions, in the currency of the latest activity (bootstrapDashboard / latestMonthWithData). - Load the prior two months' summaries alongside the active month so the deltas and the 3-month cashflow chart have real comparison data. - Add a currency switcher (shown only when the ledger holds >1 currency). - Replace the fake hard-coded "Emergency Fund" goal placeholder with a real empty state. - Soften the "Zero Hallucination Guarantee" label to an honest claim. - Serve index.html with an mtime-based asset cache-buster so edited app.js/styles.css actually reach the browser. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxBdKWe1SS9LP5ZBCT7m4M
The statement-upload WIP left 43 ruff errors and 4 mypy errors. Fixes, no behaviour change: - Real bugs: B023 loop-variable capture in the HDFC fixed-width parser (bind col_spans/line as defaults); F821 undefined `Any` in the extraction-timeout cleanup callback (type it Future[ExtractionResult], rename the shadowing loop var); `pdf._merge_continuations` could assign None to fields["date"]. - Lint: drop unused imports, wrap over-long f-strings/messages/docstrings, noqa the fixed-width fixture rows that can't wrap. - mypy: ignore missing stubs for the stub-less `xlrd` dependency. Gate now fully green: ruff check, ruff format --check, mypy src, pytest (149 passed). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxBdKWe1SS9LP5ZBCT7m4M
…mmits Two gaps that made a real import unusable after commit: #1 Uncategorised rows were a dead end. Commit runs only deterministic rules (no model), so statements rules can't match (e.g. Indian UPI) stayed permanently uncategorised with no way to fix them in the browser. - Add GET /categories and PATCH /transactions/{id} (a user correction: confidence 1.0 + a narrow exact-description merchant rule), sharing one correct_transaction() helper with the CLI. - Activity Ledger: the category cell is now an editable dropdown that PATCHes and flips provenance to "user". #2 amount_sign guard had holes. An all-positive generic statement could commit booked as income once an account was assigned (assigning an account cleared the sign check), and no-adapter batches skipped it entirely. The convention is now required for every generic all-positive batch regardless of account; the batch is blocked in the preview itself. Gate: ruff, ruff format, mypy, pytest (151 passed). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxBdKWe1SS9LP5ZBCT7m4M
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FxBdKWe1SS9LP5ZBCT7m4M
…nths Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATghuAGL4B1rYcAk97LmjE
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATghuAGL4B1rYcAk97LmjE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bounds the transactions query by date instead of pulling an unbounded page, and adds
GET /transactions/monthsso the dashboard can discover available months per currency./transactions?limit=1,/transactions/months?currency=…,/transactions?month=…) and lands on the newest currency's latest month. No morelimit=500.account_id/ malformedmonthnow return 422 instead of silently matching nothing.Verified live on 8010 against the real
data/pfa.db(429 rows, GBP + INR).Note: this branch also carries the previously unpushed HDFC import / transfers / currency work that had accumulated on local
main.Known limitation (predates this change): the ledger's
limit=200per month silently truncates a month with more rows. Handoff:docs/plans/2026-09-05-transactions-query-handoff.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01ATghuAGL4B1rYcAk97LmjE