Files
fuj-management/docs/plans/2026-10-08-1103-api-parity-vs-months-to-show.md
T
kacerrandClaude Opus 5.5 82406935ed
Deploy to K8s / deploy (push) Successful in 14s
docs: add pre-feature cleanup task list and API parity plan
Task list from the 2026-10-08 repo audit, plus the plan for the two
parity fixes found while validating the legacy/ move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:34:20 +02:00

5.3 KiB

Fix the two Go/Python parity differences

Context

Running make parity against both containers on 2026-10-08 reported 0/3 routes match. Two causes, neither related to the move to legacy/:

  1. VS rendered in scientific notation by Go. The payments sheet is read with UNFORMATTED_VALUE, so a numeric VS cell arrives as float64. Go stringifies cells with fmt.Sprint, which prints any float ≥ 1e6 in exponent form (2.609767878e+09). Python already converts integer floats to plain digits for VS (get_str in fetch_sheet_data). VS must always be a plain string.
  2. months_to_show missing from the Python JSON API. Go returns it in /api/adults and /api/juniors (and it is required in the committed schemas). Python already has the feature in its HTML pages (MONTHS_TO_SHOW is passed to the templates) but never added the key to the API responses. Backport the key so the API contract matches.

Outcome: make parity reports 3/3 routes matching.

Branching

Both are bug fixes that touch legacy/ paths, so they depend on the uncommitted feat/python-to-legacy work. Commit that branch first (still awaiting confirmation), then branch fix/api-parity-vs-months-to-show from it, with one commit per task. Open the MR with tea after the legacy move is merged, or target it at that branch.

Task 1 — VS (and other text cells) always a plain string in Go

Root cause is getVal in parseTransactionRows (go/internal/services/membership/sources.go, the fmt.Sprint(row[i]) closure). The same pattern exists in get in go/internal/services/banksync/infer.go.

  1. Add CellString(v any) string to go/internal/io/sheets (new small file cell.go; both services already import this package):
    • nil → ""
    • float64 → strconv.FormatFloat(x, 'f', -1, 64) (never exponent form; 2609767878.0 → "2609767878", 12.5 → "12.5")
    • string → unchanged
    • anything else → fmt.Sprint(v)
  2. Use it in getVal in sources.go. This fixes VS and protects the other text columns (Message, Bank ID, Sender, …) from the same failure.
    • getVal currently returns "<nil>" for nil cells and the inferred-amount code checks for that literal; simplify that check to iv != "".
    • Leave getRaw/FormatDate and parseFloat for Date and Amount as they are — those must stay numeric.
  3. Use it in get in banksync/infer.go (keeping the TrimSpace), so inference sees the same text the dashboard shows.
  4. Tests:
    • go/internal/io/sheets/cell_test.go: table test for CellString (large integer float, small integer float, fractional, string with leading zeros, nil).
    • Extend TestLoadTransactions in go/internal/services/membership/sources_test.go with a row whose VS is 2609767878.0 and whose Bank ID is an 11-digit float; assert plain digit strings.
  5. Python: no code change needed — get_str already does this for VS. Add one regression test for fetch_sheet_data in legacy/tests/test_match_payments.py (mock the Sheets service, numeric VS cell → "2609767878") so the two sides are pinned to the same behaviour.

Not included (note for later): VS is written to the sheet with USER_ENTERED by both sync implementations, so Sheets stores it as a number and drops leading zeros. Fixing that means writing VS as text and is a separate change to the sheet's data.

Task 2 — backport months_to_show to the Python JSON API

legacy/app.py only; MONTHS_TO_SHOW is already imported there.

  1. In api_adults() and api_juniors(), add the key to the response: build out = _unwrap_view_model_for_api(vm), set out["months_to_show"] = MONTHS_TO_SHOW, return jsonify(out).
    • Do not add it inside build_*_view_model or _unwrap_view_model_for_api: the HTML routes already pass months_to_show= to render_template alongside **vm, and a duplicate key would raise TypeError.
    • /api/payments is unchanged; Go does not return the key there.
  2. Tests in legacy/tests/test_app.py:
    • Add "months_to_show" to the expected key list used by test_api_adults / test_api_juniors (the tuple near the top of the file), and assert the value is an int.
  3. Go: no change. The key and schemas (go/tests/fixtures/api-schema/{adults,juniors}.schema.json) already contain it.

Wrap-up

  • CHANGELOG.md entry at the top (after confirmation it works), per repo convention.
  • Copy this plan to docs/plans/2026-10-08-HHMM-api-parity-vs-months-to-show.md, per the repo's plan-location rule.
  • Add both items to docs/plans/2026-10-08-1026-pre-feature-cleanup-tasks.md as done tasks so the audit list stays complete.

Verification

  1. make go-test-all — unit tests (including the new ones) and parity fixtures pass.
  2. make test — Python tests pass, including the new API key and VS tests.
  3. cd go && go test ./internal/web/api/... — schema golden test still matches (no schema change expected).
  4. End to end: start make web-py and make web-go, then make parity; expect 3/3 routes match. Alternatively repeat the two-container run used to find the bugs (make image, Go image from go/build/Dockerfile, then go run ./cmd/parity -py … -go …).
  5. Open /payments on :8080 and check the affected transaction's VS shows as plain digits.