Compare against current YYYY-MM to exclude future months from the
from/to selectors, default selection, and table column display.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add from/to combobox selectors and Apply/All buttons to filter
which month columns are displayed. Defaults to last 5 months on load.
Co-Authored-By: Claude Sonnet 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 docs/operation-manual.md describing how to add per-month fee
overrides for adults and juniors. Also adds the March 2026 junior
fee override (250 CZK) to JUNIOR_MONTHLY_RATE to match the existing
adult override.
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>
Format is now "Name: MM/YYYY+MM/YYYY" instead of "Name / MM/YYYY+MM/YYYY"
for clearer readability when multiple months are included.
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>
Mirror the junior fee override mechanism (JUNIOR_MONTHLY_RATE) for adults
via ADULT_FEE_MONTHLY_RATE. Set 2026-03 override to 350 CZK. Rename
FEE_FULL/FEE_SINGLE to ADULT_FEE_DEFAULT/ADULT_FEE_SINGLE for consistency.
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>
- Users can now navigate between members in the details popup using Up/Down arrows.
- Fixed 0 attendance count in member popup by preserving count in reconciliation.
- Updated uv.lock following dependency changes.
Co-authored-by: Antigravity <antigravity@google.com>
This fixes the 'ModuleNotFoundError: No module named qrcode' error in the container.
Updated pyproject.toml version to 0.10.
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>
Capture HTTP status code and full response body separately so failures
show the actual error from the server instead of silently dying.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gitea doesn't implement Actions OIDC tokens yet. Drop the experimental
id_token steps and use VAULT_ROLE_ID/VAULT_SECRET_ID/K8S_CA_CERT as
standard Gitea repo secrets.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use ${VAR:-} default-empty syntax so set -u doesn't abort when
ACTIONS_ID_TOKEN_REQUEST_TOKEN/URL are absent (stock Gitea runners
don't set them).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch VAULT_ROLE_ID, VAULT_SECRET_ID, and K8S_CA_CERT from Gitea repo
secrets to shell env vars, which are injected via the runner host's
systemd EnvironmentFile — keeping credentials off Gitea entirely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>