# FUJ Management — Comprehensive Documentation > **FUJ = Frisbee Ultimate Jablonec** — a small sports club in the Czech Republic. ## What Is This Project? FUJ Management is a purpose-built financial management system for a small ultimate frisbee club. It automates the tedious process of tracking **who attended practice**, **how much they owe**, **who has paid**, and **who still owes money** — a workflow that would otherwise require manual cross-referencing between attendance spreadsheets and bank statements. The system is built around two Google Sheets (one for attendance, one for payments) and a Fio bank transparent account. A set of Python scripts sync and process the data, while a Flask-based web dashboard provides real-time visibility into fees, payments, and reconciliation status. ### The Problem It Solves Before this system, the club treasurer had to: 1. **Manually count** attendance marks for each member each month 2. **Calculate** whether each person owes 0, 200, or 750 CZK based on how many times they showed up 3. **Cross-reference** bank statements to figure out who paid and for which month 4. **Chase** members who hadn't paid, often losing track of partial payments and advance payments 5. **Handle edge cases** like members paying for multiple months at once, using nicknames in payment messages, or paying via a family member's account This system automates steps 1–4 entirely, and provides tooling for step 5. ## System Overview ``` ┌──────────────────────────┐ ┌──────────────────────────┐ │ Attendance Sheet │ │ Fio Bank Account │ │ (Google Sheets) │ │ (transparent account) │ │ │ │ │ │ Members × Dates × ✓/✗ │ │ Incoming payments with │ │ Tier (A/J/X) │ │ sender, amount, message │ └──────────┬───────────────┘ └──────────┬───────────────┘ │ │ │ CSV export │ API / HTML scraping │ │ ▼ ▼ ┌─────────────────┐ ┌───────────────────────┐ │ attendance.py │ │ sync_fio_to_sheets.py │ │ │ │ │ │ Fetches sheet, │ │ Syncs bank txns to │ │ computes fees │ │ Payments Google Sheet │ └────────┬────────┘ └───────────┬────────────┘ │ │ │ ▼ │ ┌───────────────────────┐ │ │ Payments Sheet │ │ │ (Google Sheets) │ │ │ │ │ │ Date|Amount|Person| │ │ │ Purpose|Sender|etc. │ │ └───────────┬────────────┘ │ │ │ ┌─────────────────────────┤ │ │ │ │ ▼ ▼ │ ┌──────────────┐ ┌──────────────────┐ │ │infer_payments│ │ match_payments.py │ │ │ .py │ │ │ │ │ │ │ Reconciliation │ │ │ Auto-fills │ │ engine: matches │ │ │ Person, │ │ payments against │ │ │ Purpose, │ │ expected fees │ │ │ Amount │ └────────┬──────────┘ │ └──────────────┘ │ │ │ └────────────────┬───────────────┘ │ ▼ ┌──────────────────────┐ │ Flask Web App │ │ (app.py) │ │ │ │ /fees – fee │ │ table │ │ /reconcile – balance │ │ matrix │ │ /payments – ledger │ │ /qr – QR code │ └───────────────────────┘ ``` ## Quick Start ### Prerequisites - **Python 3.13+** - **[uv](https://docs.astral.sh/uv/)** — fast Python package manager - **Google Sheets API credentials** — a service account JSON file placed at `.secret/fuj-management-bot-credentials.json` - *Optional*: `FIO_API_TOKEN` environment variable for Fio REST API access (falls back to transparent page scraping) ### Setup ```bash # Clone and install dependencies git clone cd fuj-management uv sync # Installs all dependencies from pyproject.toml # Place your Google API credentials mkdir -p .secret cp /path/to/your/credentials.json .secret/fuj-management-bot-credentials.json ``` ### Common Operations | Command | Purpose | |---------|---------| | `make web` | Start the web dashboard at `http://localhost:5001` | | `make sync` | Pull new bank transactions into the Google Sheet | | `make infer` | Auto-fill Person/Purpose/Amount for new transactions | | `make reconcile` | Print a CLI balance report | | `make fees` | Print fee calculation table from attendance | | `make test` | Run the test suite | | `make image` | Build the Docker container image | ### Typical Workflow ``` make sync → make infer → (manual review in Google Sheets) → make web ↓ ↓ ↓ ↓ Pull new bank Auto-match Fix any [?] View live transactions payments to flagged rows dashboard into sheet members/months in the sheet ``` ## Documentation Index | Document | Contents | |----------|----------| | [Architecture](architecture.md) | System design, data flow diagrams, module dependency graph | | [Web Application](web-app.md) | Flask app architecture, routes, templates, interactive features | | [User Guide](user-guide.md) | End-user guide for the web dashboard — what each page shows | | [Scripts Reference](scripts.md) | Detailed reference for all CLI scripts and shared modules | | [Data Model](data-model.md) | Google Sheets schemas, fee calculation rules, bank integration | | [Deployment](deployment.md) | Docker containerization, Gitea CI/CD, Kubernetes deployment | | [Testing](testing.md) | Test infrastructure, coverage, how to write new tests | | [Development Guide](development.md) | Local setup, coding conventions, tooling, project history | ## Technology Stack | Layer | Technology | |-------|-----------| | Language | Python 3.13+ | | Web framework | Flask 3.1 | | Package management | uv + pyproject.toml | | Data sources | Google Sheets API, Fio Bank API / HTML scraping | | QR codes | `qrcode` library (PIL backend) | | Containerization | Docker (Alpine-based) | | CI/CD | Gitea Actions | | Deployment target | Self-hosted Kubernetes | | Frontend | Server-rendered HTML/CSS/JS (terminal-aesthetic theme) | ## Project Structure ``` fuj-management/ ├── app.py # Flask web application (4 routes) ├── Makefile # Build automation (13 targets) ├── pyproject.toml # Python dependencies and metadata │ ├── scripts/ │ ├── attendance.py # Shared: attendance data + fee calculation │ ├── calculate_fees.py # CLI: print fee table │ ├── match_payments.py # Core: reconciliation engine + CLI report │ ├── infer_payments.py # Auto-fill Person/Purpose in Google Sheet │ ├── sync_fio_to_sheets.py # Sync Fio bank → Google Sheet │ ├── fio_utils.py # Shared: Fio bank data fetching │ └── czech_utils.py # Shared: diacritics normalization + Czech month parsing │ ├── templates/ │ ├── fees.html # Attendance/fees dashboard │ ├── reconcile.html # Payment reconciliation with modals + QR │ └── payments.html # Payments ledger grouped by member │ ├── tests/ │ ├── test_app.py # Flask route tests (mocked data) │ └── test_reconcile_exceptions.py # Reconciliation with fee exceptions │ ├── build/ │ ├── Dockerfile # Alpine-based container image │ └── entrypoint.sh # Container entry point │ ├── .gitea/workflows/ │ ├── build.yaml # CI: build + push Docker image │ └── kubernetes-deploy.yaml # CD: deploy to K8s cluster │ ├── .secret/ # (gitignored) API credentials ├── docs/ # Project documentation │ ├── project-notes.md # Original brainstorming and design notes │ ├── fee-calculation-spec.md # Fee rules and payment matching spec │ ├── scripts.md # Legacy scripts documentation │ └── spec/ │ └── fio_to_sheets_sync.md # Fio-to-Sheets sync specification │ └── CLAUDE.md # AI assistant context file ``` ## Key Design Decisions 1. **No database** — Google Sheets serves as both the data store and the manual editing interface. This keeps the system simple and accessible to non-technical club members who can review and edit data directly in the spreadsheet. 2. **PII separation** — No member names or personal data are stored in the git repository. All data is fetched at runtime from Google Sheets and the bank account. 3. **Idempotent sync** — The Fio-to-Sheets sync uses SHA-256 hashes as deduplication keys, making re-runs safe and append-only. 4. **Graceful fallbacks** — Bank data can be fetched via the REST API (if a token is available) or by scraping the public transparent account page. The system doesn't break if the API token is missing. 5. **Czech language support** — Payment messages are in Czech and use diacritics. The system normalizes text (strips diacritics) and understands Czech month names in all grammatical declensions. 6. **Terminal aesthetic** — The web dashboard uses a monospace, dark-themed, terminal-inspired design that matches the project's pragmatic, CLI-first philosophy. --- *This documentation was generated on 2026-03-03 by Claude Opus, based on a comprehensive analysis of the complete codebase.*