Instead of hiding older months entirely, show all months in the from/to
selectors but default the from-select to the last MONTHS_TO_SHOW months
on page load. The "All" button resets to full history as before.
Python: passes months_to_show to render_template, IIFE sets fromSelect.value.
Go: adds MonthsToShow to response structs, data-months-to-show attr in
templates, filters.js reads it and defaults fromSelect after hideFutureMonths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Show only the last MONTHS_TO_SHOW months (default 5) in the fee table columns
so the page fits on screen without horizontal scrolling. Reconciliation still
runs over the full month history so balances, credits, and debts are unaffected.
Set MONTHS_TO_SHOW=0 to show all months. Implemented in both Python and Go.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Four new JSON routes mirror the Go /api/* handlers so the M5.4 parity
tool can diff them: /api/version, /api/adults, /api/juniors,
/api/payments. A small _unwrap_view_model_for_api() helper in app.py
expands the three pre-serialised JSON strings in the view-model dicts
and renames month_labels_json → month_labels and
raw_payments_json → raw_payments to match the Go wire contract.
Tests in test_app.py assert top-level key sets match the Go API schema
and that member_data, month_labels, raw_payments are objects not strings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Accidentally removed when moving group_payments_by_person to views.py;
re.match in qr_code caused a 500 on every QR request.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pull 350+ lines of inline per-row computation out of adults_view,
juniors_view, and payments into three pure builder functions with no
Flask globals or IO dependencies. Route handlers now contain only
cache/IO calls and a single render_template. No behaviour change —
all 27 tests pass.
Also moves get_month_labels, group_payments_by_person, and
adapt_junior_members out of app.py. Prep for /api/* shadow endpoints
(M5 Go parity).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add canonical_member_key() in match_payments.py to normalize names via
NFKD + lowercase + whitespace-collapse before ledger lookup; resolves
payments attributed to e.g. "Maria Maco" to canonical "Mária Maco".
Emits logger.info when a non-canonical cell is rescued so sheet typos
are visible in logs without losing the payment allocation.
- Extend group_payments_by_person() in app.py to accept member_names and
re-key raw-payment groups under the canonical attendance-sheet name so
the modal's Raw Payments debug section also finds the row correctly.
- Add raw payments collapsible section to member detail modal in adults.html
and juniors.html for debugging payment attribution issues.
- Remove 4 obsolete tests targeting routes /fees, /fees-juniors, /reconcile,
/reconcile-juniors that no longer exist; add test_match_payments.py
covering canonical key equivalence and reconcile() tolerance end-to-end.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous calculation derived balance from total_balance (which includes
current/future-month activity and out-of-window credits) plus a one-sided
debt-only adjustment. Current-month surplus leaked through, making the balance
appear less negative than actual past-month debt (e.g. Mauric Daniel -1250 vs
correct -1750). Pay-All is now max(0, -balance) so the two values share a
single source and cannot disagree.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Hide Pay/Pay All buttons for months still in progress, exclude
current month debt from balance column, and show in-progress
month debt in a muted red color.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deleted /fees, /fees-juniors, /reconcile, /reconcile-juniors routes and
their templates. Payment Ledger (/payments) is retained. Nav updated
across all remaining templates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a /flush-cache web page with a button to clear all cached Google
Sheets data and reset refresh timers. Link added to Tools nav across
all templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new GET /sync-bank route that runs sync_to_sheets (2026) + infer_payments + flush_cache,
capturing all output and displaying it on a styled results page. Adds "Tools: [Sync Bank Data]"
nav link to all templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store git tag, commit hash, and build date as OCI-standard labels and a
build_meta.json file inside the Docker image. The Flask app reads this at
startup and displays version info in all page footers. Adds /version JSON
endpoint for programmatic access. Falls back to dev@local outside Docker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a junior attended only once in a month (fee = "?"), the dashboard
cells now display the attendance details (e.g., "? (1:1J)") instead of
a bare "?". Applied to both /juniors and /reconcile-juniors routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pre-fetches all 4 cached datasets (attendance, juniors, payments,
exceptions) at module load time so the first request doesn't block
on Google API calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove insecure SSL verification bypass in attendance.py
- Add gunicorn as production WSGI server (Dockerfile + entrypoint)
- Fix silent data loss in reconciliation (log + surface unmatched members)
- Add required column validation in payment sheet parsing
- Add input validation on /qr route (account format, amount bounds, SPD injection)
- Centralize configuration into scripts/config.py
- Extract credentials path to env-configurable constant
- Hide unmatched transactions from reconcile-juniors page
- Fix test mocks to bypass cache layer (all 8 tests now pass reliably)
- Add pytest + pytest-cov dev dependencies
- Fix typo "Inffering" in infer_payments.py
- Update CLAUDE.md to reflect current project state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add optional serialize/deserialize hooks to get_cached_data() so it
can handle the exceptions dict (tuple keys → JSON-safe lists) without
needing a separate function.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the file mtime check from the API debounce tier in
get_sheet_modified_time(). Previously, the debounce was defeated when
CACHE_TTL_SECONDS differed from CACHE_API_CHECK_TTL_SECONDS because
the file age check would fail even though the API was checked recently.
Also fix cache key mappings (attendance_juniors sheet ID,
payments_transactions rename) and add tmp/ to .gitignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add cache_utils.py with JSON caching for Google Sheets
- Authenticate and cache Drive/Sheets API services globally to reuse tokens
- Use CACHE_SHEET_MAP dict to resolve cache names securely to Sheet IDs
- Change app.py data fetching to skip downloads if modifiedTime matches cache
- Replace global socket timeout with httplib2 to fix Werkzeug timeouts
- Add VS Code attach debugpy configurations to launch.json and Makefile
- Add dual-sheet architecture to pull attendance from both adult and junior spreadsheets.
- Introduce parsing rules to isolate juniors (e.g. above '# Treneri', tier 'J').
- Add new endpoints `/fees-juniors` and `/reconcile-juniors` to track junior attendances and match bank payments.
- Display granular attendance components showing adult vs. junior practices.
- Add fee rule configuration supporting custom pricing exceptions for specific months (e.g. Sep 2025) and merging billing periods.
- Add `make sync-2025` target to the Makefile for convenience.
- Document junior fees implementation logic and rules in prompts/outcomes.
Co-authored-by: Antigravity <antigravity@google.com>
QR codes are now generated locally using the 'qrcode' library for better privacy and reliability.
Updated .agent/rules.md with co-author details and Conventional Commits preference.
Co-authored-by: Antigravity <antigravity@google.com>
- Added a Makefile to easily run project scripts (fees, match, web, image)
- Modified attendance.py to dynamically handle a variable number of header rows from the Google Sheet
- Updated both attendance calculations and calculate_fees terminal output to show actual attendance counts (e.g., '750 CZK (3)')
- Created a Flask web dashboard (app.py and templates/fees.html) to view member fees in an attractive, condensed, terminal-like UI
- Bound the Flask server to port 5000 and added a routing alias from '/' to '/fees'
- Configured Python virtual environment (.venv) creation directly into the Makefile to resolve global pip install errors on macOS
Co-authored-by: Antigravity <antigravity@deepmind.com>