Files
fuj-management/docs/by-claude-opus

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 14 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 — 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

# Clone and install dependencies
git clone <repo-url>
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 System design, data flow diagrams, module dependency graph
Web Application Flask app architecture, routes, templates, interactive features
User Guide End-user guide for the web dashboard — what each page shows
Scripts Reference Detailed reference for all CLI scripts and shared modules
Data Model Google Sheets schemas, fee calculation rules, bank integration
Deployment Docker containerization, Gitea CI/CD, Kubernetes deployment
Testing Test infrastructure, coverage, how to write new tests
Development Guide 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.