Files
fuj-management/CLAUDE.md
Jan Novak cf0f176d3f feat: Go rewrite M1 — skeleton, tooling, and hello server
Stand up the Go project alongside the Python backend so both run
independently during migration. `make web-go` builds and serves on :8080;
`make web-py` (alias: `make web`) keeps the Python side on :5001.

- go/: new module `fuj-management/go` (Go 1.26)
  - cmd/fuj: stdlib-flag dispatcher; `server` + `version` work,
    fees/reconcile/sync/infer stubbed for M2/M4
  - internal/config: env loader mirroring scripts/config.py
  - internal/logging: slog setup, level taken from config
  - internal/web: net/http ServeMux + request-timer middleware
  - build/Dockerfile: golang:1.26 → alpine:3 multi-stage image
  - .golangci.yml: govet, staticcheck, errcheck, gofumpt, unused
- Makefile: web→web-py alias; go-build/go-test/go-run/go-lint/web-go
- CI: parallel build-go job in .gitea/workflows/build.yaml (<tag>-go image)
- docs/plans/: M1 kickoff plan + progress tracker (M1 complete)
- .claude/settings.json: gofumpt + golangci-lint permissions

Gate: make go-build ✓  make go-lint ✓  make go-test ✓  curl :8080 ✓

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:05:46 +02:00

5.7 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Status

Flask-based financial management system for FUJ (Frisbee Ultimate Jablonec). Handles attendance-based fee calculation, Fio bank transaction sync, payment reconciliation, and a web dashboard.

Key Constraints

  • PII separation: Member data (names, emails, payment info) must never be committed to git. Enforce config/data separation from day one.
  • Configuration: External service IDs, credentials, and tunable parameters are centralized in scripts/config.py. Domain-specific constants (fees, merged months) stay in their respective modules.

Development Setup

This project uses uv for dependency management.

uv venv && uv sync
source .venv/bin/activate

Set PYTHONPATH=scripts:. when running scripts directly (the Makefile does this automatically).

Commands

make web          # Start dashboard at http://localhost:5001
make web-debug    # Same with FLASK_DEBUG=1
make test         # Run all tests (unittest discover)
make test-v       # Tests with verbose output
make fees         # Print fee report from attendance sheet
make sync-2026    # Sync Fio bank transactions for 2026 to Google Sheets
make infer        # Auto-fill Person/Purpose/Amount columns in payments sheet
make reconcile    # Print balance report from Google Sheets data
make image        # Build Docker image

Run a single test:

PYTHONPATH=scripts:. python -m unittest tests.test_app.TestWebApp.test_adults_route

Architecture

Data flow

Google Sheets (attendance) ──► attendance.py ──► reconcile() ──► Flask routes ──► templates/
Google Sheets (payments)   ──► match_payments.py ──┘
Fio Bank API               ──► sync_fio_to_sheets.py ──► Google Sheets (payments)

Key modules

  • app.py — Flask app; routes for /adults, /juniors, /payments, /sync-bank, /qr, /flush-cache
  • scripts/attendance.py — Fetches attendance CSV from Google Sheets, computes per-member per-month fees. Contains fee rate constants (ADULT_FEE_DEFAULT, JUNIOR_FEE_DEFAULT) and ADULT_MERGED_MONTHS / JUNIOR_MERGED_MONTHS dicts.
  • scripts/match_payments.pyreconcile() matches transactions to members/months. fetch_sheet_data() reads the payments sheet. fetch_exceptions() reads the exceptions tab.
  • scripts/cache_utils.py — Invalidation via Google Drive API modifiedTime; falls back to 5-minute TTL buckets when Drive API is unavailable. Cache files live in tmp/.
  • scripts/sync_fio_to_sheets.py — Pulls Fio bank transactions and appends them to the payments Google Sheet.
  • scripts/infer_payments.py — Fills in Person/Purpose/Inferred Amount columns using name-matching heuristics.
  • scripts/config.py — All external IDs, paths, and tunable TTLs. Override via env vars (CREDENTIALS_PATH, BANK_ACCOUNT, CACHE_TTL_SECONDS).

Member tiers

Tiers are set in column B of the attendance sheet:

  • A — Adult, pays fees (750 CZK/month for 2+ sessions, 200 CZK for exactly 1)
  • J — Junior attending adult practices; their attendance is merged with the junior sheet
  • X — Excluded from junior fee calculation (coaches, etc.)

Fee calculation

  • Adults: 0 sessions → 0, 1 session → 200 CZK, 2+ sessions → monthly rate (default 750 CZK)
  • Juniors: 0 → 0, 1 → "?" (manual review required), 2+ → monthly rate (default 500 CZK)
  • Per-member per-month overrides live in the exceptions tab of the payments sheet (columns: Name, Period YYYY-MM, Amount, Note). Exceptions are keyed by (normalize(name), normalize(period)).

Merged months

ADULT_MERGED_MONTHS / JUNIOR_MERGED_MONTHS in attendance.py map a source month to a target month (e.g., "2025-12": "2026-01" merges December into January billing). The target month accumulates attendance from both months.

Caching

get_cached_data() in app.py checks the Drive API modifiedTime before each request and serves a JSON file from tmp/ when the sheet hasn't changed. Cache is warmed up at startup (warmup_cache()). Flush via /flush-cache (POST) or flush_cache().

Payments sheet columns

Date | Amount | manual fix | Person | Purpose | Inferred Amount | Sender | VS | Message | Bank ID | Sync ID

Person and Purpose are written by infer_payments.py and can be manually corrected. manual fix column presence disables re-inference for that row. Multiple people or months are comma-separated in Person/Purpose.

QR codes

/qr?account=…&amount=…&message=… generates a Czech QR Platba PNG (SPD format).

Git Commits

When making git commits, always append yourself as co-author trailer to the end of the commit message to indicate AI assistance

Changelog

Maintain a running changelog in CHANGELOG.md at the repo root. After every significant change, fix, or update — once the user confirms it works — append a new entry at the top of the file in this format:

## YYYY-MM-DD HH:MM TZ — short title

- One-line summary of what changed and why.
- Key files touched (optional, only if useful for traceability).

Get the timestamp with date "+%Y-%m-%d %H:%M %Z". Skip trivial edits (typos, formatting, comment tweaks); only log changes a future reader would care about.

Plans

When Claude Code's plan mode is used, save the plan file inside the repo at docs/plans/YYYY-MM-DD-HHMM-<slug>.md instead of the default ~/.claude/plans/ location. Get the timestamp with date "+%Y-%m-%d-%H%M" (matches the changelog convention). The <slug> should be a short kebab-case summary of the plan's topic.

Create the docs/plans/ directory on first use. Plan files are committed to the repo so other contributors can review historical decisions.