Vrijwilligersvergoedingen van intake tot uitbetaling, in één gecontroleerd proces.Volunteer allowances from intake to payment, in one controlled process.

Interne webapplicatie voor een Belgische voetbalclub. Vervangt losse formulieren, mails en handmatig opgestelde betaalbestanden door één flow: goedkeuring per team, HR-validatie, SEPA-betaalbatches en een audit trail. Live sinds augustus 2026, ontworpen en gebouwd door mij.

Internal web application for a Belgian football club. Replaces loose forms, e-mails and hand-made payment files with one flow: per-team approval, HR validation, SEPA payment batches and an audit trail. Live since August 2026, designed and built by me.

  • Python
  • Flask
  • PostgreSQL
  • Docker
  • SEPA
  • Live

De applicatie in 20 secondenThe application in 20 seconds

Lotte PeetersSteward · wedstrijddag · 4,5 uSteward · match day · 4.5 h
€ 40,50
  • IBAN geldigIBAN valid
  • Jaarmaximum: 62 % gebruiktYearly maximum: 62 % used
  • Geen combinatieverbodNo status conflict
  • Adres ontbreekt: HR vult aanAddress missing: HR completes it
  1. TeamleiderTeam leaderYassin El Amrani
  2. DirectieManagementMarie Claes
  3. HRJonas De Wilde
  4. GoedgekeurdApprovedklaar voor betalingready for payment

Welke stappen gelden, is instelling per team. Geen code.Which steps apply is a per-team setting. No code.

NaamNameStatuutStatusNetto
Lotte PeetersVrijwilligerVolunteer€ 40,50
Sam VermeulenVerenigingswerkerAssociation worker€ 296,42
Nour HaddadVrijwilligerVolunteer€ 82,96
Batch 2026-08 · 3 transactiesBatch 2026-08 · 3 transactions€ 419,88
SEPA-bestand aangemaaktSEPA file createdIngediend: enkel met reden terug te draaienSubmitted: reversible only with a reason
Dimona ✓Fiche 281.27 klaarTax form 281.27 ready
  • jonas@ · HRadvanceHR Validatie → Goedgekeurd
  • marie@ · DirectieManagementapproveintake #412
  • yassin@ · TeamleiderTeam leaderadvanceTeamleider → Directie
  • lotte@ · InvoerderEntrycreate∅ → {Lotte Peeters, 4,5 u}

    Nagebouwde fragmenten van de applicatie met verzonnen namen en bedragen.Re-created fragments of the application with invented names and amounts.

    Het probleemThe problem

    Een club werkt met tientallen vrijwilligers, stewards en verenigingswerkers. Elke prestatie moet geregistreerd, goedgekeurd en uitbetaald worden, binnen wettelijke grenzen die per statuut verschillen: een dag- en jaarmaximum voor vrijwilligers, kwartaalplafonds voor verenigingswerkers, Dimona-aangiftes en fiscale fiches 281.27 vóór eind februari. Dat liep via formulieren, mails en Excel, met één persoon als enige die het geheel overzag.

    A club works with dozens of volunteers, stewards and association workers. Every activity has to be registered, approved and paid, within legal limits that differ per status: daily and yearly maxima for volunteers, quarterly ceilings for association workers, Dimona declarations and tax forms 281.27 before the end of February. That ran through forms, e-mails and Excel, with one person as the only one who saw the whole picture.

    De opdracht: één systeem dat de intake vastlegt, de goedkeuring langs de juiste mensen leidt, de wettelijke grenzen zelf bewaakt en op het einde een betaalbestand oplevert dat de bank aanvaardt.

    The brief: one system that records the intake, routes approval past the right people, enforces the legal limits itself and finally produces a payment file the bank accepts.

    Wat de applicatie doetWhat the application does

    IntakeIntake

    Prestaties komen binnen via templates, formulieren en Excel-imports, met fuzzy matching op bestaande personen en een duidelijke lijst van wat ontbreekt.Activities come in through templates, forms and Excel imports, with fuzzy matching on existing persons and a clear list of what is missing.

    Goedkeuring in stappenStep-by-step approval

    Teamleider, directie en HR keuren elk hun deel goed. Welke stap voor wie geldt, is configuratie per team, geen code.Team leader, management and HR each approve their part. Which step applies to whom is per-team configuration, not code.

    Wettelijke grenzenLegal limits

    Jaar- en kwartaalplafonds, het combinatieverbod tussen statuten en de IBAN-controle worden op één plaats afgedwongen, vóór iets goedgekeurd raakt.Yearly and quarterly ceilings, the ban on combining statuses and the IBAN check are enforced in one place, before anything gets approved.

    BetaalbatchesPayment batches

    Goedgekeurde prestaties worden per maand gebundeld in een SEPA-bestand. Indienen vraagt een drievoudige controle en is daarna bewust niet meer stil terug te draaien.Approved activities are bundled per month into a SEPA file. Submitting requires a triple check and is deliberately not silently reversible afterwards.

    Dimona en fichesDimona and tax forms

    Dimona-aangiftes per contract en de jaarlijkse fiches 281.27 komen uit dezelfde gegevens, zonder overtypen.Dimona declarations per contract and the yearly 281.27 tax forms come from the same data, with no retyping.

    Audit trailAudit trail

    Elke wijziging wordt gelogd met oude en nieuwe waarde, wie en wanneer. Verwijderde personen worden nooit echt gewist, wel als inactief gemarkeerd.Every change is logged with old and new value, who and when. Deleted persons are never really erased, only marked inactive.

    Hoe ze gebouwd isHow it was built

    Stack

    • Python · Flask · SQLAlchemy · Jinja
    • PostgreSQL met migratieswith migrations
    • Docker lokaal, systemd achter nginx op de serverlocally, systemd behind nginx on the server
    • Eigen testsuites voor geld, weergave, login, rollen en toegangOwn test suites for money, display, login, roles and access

    BeheerOperations

    • Zeven rollen met een expliciete rechtenmatrixSeven roles with an explicit permission matrix
    • Runbook van wijziging tot productie, inclusief rollbackRunbook from change to production, including rollback
    • Provisioning van de server gedocumenteerd en herhaalbaarServer provisioning documented and repeatable
    • Overdrachtsdossier: architectuur, handoff, stand van zakenHand-over pack: architecture, handoff, current state
    28.000regels codelines of code
    84Python-modulesPython modules
    41schermenscreens
    7rollenroles

    Ontwerpkeuzes die het verschil makenDesign decisions that matter

    Eén knooppunt voor alle controlesOne node for every check
    Elke goedkeuring, uit welk scherm of welke import ook, loopt door dezelfde functie. De IBAN-controle en de wettelijke maxima staan daar één keer, dus geen enkel pad kan ze omzeilen.Every approval, from whichever screen or import, passes through the same function. The IBAN check and the legal maxima live there once, so no path can bypass them.
    Configuratie boven codeConfiguration over code
    Een team krijgt een teamleider-stap door een team toe te kennen in gebruikersbeheer. Geen ontwikkelaar nodig als de organisatie verandert.A team gets a team-leader step by assigning a team in user management. No developer needed when the organisation changes.
    Onomkeerbaar waar het geld al weg isIrreversible once the money has left
    Een nog niet ingediende batch is volledig terug te draaien. Een ingediende batch niet: die vraagt een expliciete reden en bewaart het SEPA-bestand voor het dossier.A batch not yet submitted is fully reversible. A submitted one is not: it requires an explicit reason and keeps the SEPA file for the record.
    Testdata die het domein breektTest data that breaks the domain
    De ontwikkelset bevat bewust de randgevallen: plafonds op exact 100 %, buitenlandse IBAN's, namen met accenten, afrondingen op de cent. Vierhonderd brave rijen testen niets.The development set deliberately contains the edge cases: ceilings at exactly 100 %, foreign IBANs, accented names, rounding to the cent. Four hundred well-behaved rows test nothing.