Document tenant-aware database schema, multi-tenant strategy, and API references:
- Add database schema documentation: `Database_Schema_V1-V009.md` for tenant-isolated entities (`veranstaltungen`, `turniere`, `bewerbe`, etc.). - Draft initial Kassa API reference: `Kassa_API.md` (status: DRAFT). - Finalize Stammdaten API reference: `API_Uebersicht_Stammdaten.md` (status: ACTIVE). - Summarize tenant isolation and multi-tenant strategy in `Multi_Tenant_Kurz.md`. - Update `README.md` with links to new references. Mark B-2 roadmap tasks as partially complete.
This commit is contained in:
parent
6e484ee9a1
commit
dbe7c74a9c
|
|
@ -42,11 +42,11 @@
|
||||||
- [x] Alle Roadmaps: abgeschlossene Aufgaben korrekt als `[x]` markiert
|
- [x] Alle Roadmaps: abgeschlossene Aufgaben korrekt als `[x]` markiert
|
||||||
|
|
||||||
- [ ] **B-2** | `docs/05_Backend/` aktualisieren
|
- [ ] **B-2** | `docs/05_Backend/` aktualisieren
|
||||||
- [ ] Datenbankschema dokumentieren: Tabellen `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen`,
|
- [x] Datenbankschema dokumentieren: Tabellen `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen`,
|
||||||
`teilnehmer_konten`, `turnier_kassa` (Flyway V1–V009)
|
`teilnehmer_konten`, `turnier_kassa` (Flyway V1–V009) → `docs/05_Backend/Schema/Database_Schema_V1-V009.md` (03.04.2026)
|
||||||
- [ ] API-Endpunkte-Übersicht erstellen: Reiter, Pferde, Vereine, Funktionäre (Backend B-1 ✅ abgeschlossen)
|
- [x] API-Endpunkte-Übersicht erstellen: Reiter, Pferde, Vereine, Funktionäre (Backend B-1 ✅ abgeschlossen) → `docs/05_Backend/API/API_Uebersicht_Stammdaten.md` (03.04.2026)
|
||||||
- [ ] Kassa-Endpunkte ergänzen sobald Backend B-2 abgeschlossen (`/kassa/saldo`, `/zahlvorgaenge`)
|
- [ ] Kassa-Endpunkte ergänzen sobald Backend B-2 abgeschlossen (`/kassa/saldo`, `/zahlvorgaenge`) → Platzhalter: `docs/05_Backend/API/Kassa_API.md` (DRAFT)
|
||||||
- [ ] Tenant-Isolation (ADR-0021) und Multi-Tenant-Architektur kurz beschreiben
|
- [x] Tenant-Isolation (ADR-0021) und Multi-Tenant-Architektur kurz beschreiben → `docs/05_Backend/Multi_Tenant_Kurz.md` (03.04.2026)
|
||||||
|
|
||||||
- [ ] **B-3** | `docs/06_Frontend/` aktualisieren
|
- [ ] **B-3** | `docs/06_Frontend/` aktualisieren
|
||||||
- [ ] ViewModel-Architektur-Muster (MVVM/UDF) verlinken
|
- [ ] ViewModel-Architektur-Muster (MVVM/UDF) verlinken
|
||||||
|
|
|
||||||
63
docs/05_Backend/API/API_Uebersicht_Stammdaten.md
Normal file
63
docs/05_Backend/API/API_Uebersicht_Stammdaten.md
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
type: Reference
|
||||||
|
status: ACTIVE
|
||||||
|
owner: Backend Developer
|
||||||
|
last_update: 2026-04-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# API-Übersicht Stammdaten
|
||||||
|
|
||||||
|
Abdeckung: Backend Sprint B‑1 (abgeschlossen). Konsolidierte Endpunkte für Reiter, Pferde, Vereine und Funktionäre. Implementiert mit Ktor; DTOs und Validierung in den jeweiligen Services.
|
||||||
|
|
||||||
|
Hinweis Tenant/Headers: Stammdaten sind global bzw. nicht tenant‑spezifisch. Für domain‑spezifische Vorgänge (Entries, Kassa) gilt ADR‑0021/`X-Event-Id`.
|
||||||
|
|
||||||
|
## Reiter (`/reiter`)
|
||||||
|
|
||||||
|
- GET `/reiter` — Liste (optional Filter: `lizenzKlasse`, `vereinId`; Pagination: `limit`, `offset`)
|
||||||
|
- GET `/reiter/search?q=...` — Suche nach Name oder Satznummer
|
||||||
|
- GET `/reiter/{id}` — Einzelabruf
|
||||||
|
- GET `/reiter/satznummer/{nr}` — Lookup per Satznummer
|
||||||
|
- POST `/reiter` — Anlegen
|
||||||
|
- PUT `/reiter/{id}` — Aktualisieren (partiell)
|
||||||
|
- DELETE `/reiter/{id}` — Löschen
|
||||||
|
|
||||||
|
Quelle: `backend/services/masterdata/masterdata-api/.../ReiterController.kt`
|
||||||
|
|
||||||
|
## Pferde (`/horse`)
|
||||||
|
|
||||||
|
- GET `/horse` — Liste (optional Filter: `jahrgang`, `besitzerId`; Pagination: `limit`, `offset`)
|
||||||
|
- GET `/horse/search?q=...` — Suche nach Name/Nummern
|
||||||
|
- GET `/horse/{id}` — Einzelabruf
|
||||||
|
- POST `/horse` — Anlegen
|
||||||
|
- PUT `/horse/{id}` — Aktualisieren (partiell)
|
||||||
|
- DELETE `/horse/{id}` — Löschen
|
||||||
|
|
||||||
|
Quelle: `backend/services/masterdata/masterdata-api/.../HorseController.kt`
|
||||||
|
|
||||||
|
## Vereine (`/verein`)
|
||||||
|
|
||||||
|
- GET `/verein` — Liste (optional Filter: `verband`/Bundesland; Pagination: `limit`, `offset`)
|
||||||
|
- GET `/verein/search?q=...` — Suche nach Name/Kurzname
|
||||||
|
- GET `/verein/{id}` — Einzelabruf
|
||||||
|
- POST `/verein` — Anlegen
|
||||||
|
- PUT `/verein/{id}` — Aktualisieren (partiell)
|
||||||
|
- DELETE `/verein/{id}` — Löschen
|
||||||
|
|
||||||
|
Quelle: `backend/services/masterdata/masterdata-api/.../VereinController.kt`
|
||||||
|
|
||||||
|
## Funktionäre (`/funktionaer`)
|
||||||
|
|
||||||
|
- GET `/funktionaer` — Liste (optional Filter: `rolle`; Pagination: `limit`, `offset`)
|
||||||
|
- GET `/funktionaer/search?q=...` — Suche nach Name
|
||||||
|
- GET `/funktionaer/{id}` — Einzelabruf
|
||||||
|
- POST `/funktionaer` — Anlegen
|
||||||
|
- PUT `/funktionaer/{id}` — Aktualisieren (partiell)
|
||||||
|
- DELETE `/funktionaer/{id}` — Löschen
|
||||||
|
|
||||||
|
Quelle: `backend/services/masterdata/masterdata-api/.../FunktionaerController.kt`
|
||||||
|
|
||||||
|
## Standards
|
||||||
|
|
||||||
|
- Auth: Standard-Bearer optional je nach Deploymentprofil (siehe Service-Konfig)
|
||||||
|
- Content-Type: `application/json; charset=utf-8`
|
||||||
|
- Fehlerfälle: 400 (Validierung), 404 (nicht gefunden)
|
||||||
37
docs/05_Backend/API/Kassa_API.md
Normal file
37
docs/05_Backend/API/Kassa_API.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
type: Reference
|
||||||
|
status: DRAFT
|
||||||
|
owner: Backend Developer
|
||||||
|
last_update: 2026-04-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# Kassa-API (Entwurf)
|
||||||
|
|
||||||
|
Abhängigkeit: Backend Sprint B‑2 (Kassa-Service) — laut Curator‑Roadmap noch offen. Dieses Dokument reserviert die Endpunkte und beschreibt die Erwartungen. Die Implementierungsdetails werden ergänzt, sobald der Service fertiggestellt ist.
|
||||||
|
|
||||||
|
Mandantenkontext: Erfordert `X-Event-Id` (ADR‑0021). Alle Kassa‑Operationen sind tenant‑lokal.
|
||||||
|
|
||||||
|
## Endpunkte (geplant)
|
||||||
|
|
||||||
|
- GET `/kassa/saldo` — Liefert den aktuellen Saldo der Veranstaltung und optional pro Turnier.
|
||||||
|
- Query: `turnierId` (optional, UUID)
|
||||||
|
- Response: `{ saldoCents: long, currency: "EUR", scope: "veranstaltung"|"turnier", turnierId?: UUID }`
|
||||||
|
|
||||||
|
- GET `/zahlvorgaenge` — Listet Kassa‑Buchungen/Zahlvorgänge (paginiert, filterbar).
|
||||||
|
- Query: `turnierId?`, `teilnehmerId?`, `typ?` (EINZAHLUNG|AUSZAHLUNG|KORREKTUR), `limit`, `offset`
|
||||||
|
- Response: Liste von Transaktionen mit Betrag, Zeit, Verweis (Teilnehmer/Turnier), Notiz
|
||||||
|
|
||||||
|
- POST `/zahlvorgaenge` — Erstellt einen neuen Zahlungsvorgang (Ein-/Auszahlung/Korrektur).
|
||||||
|
- Body: `{ typ, betragCents, currency, referenz: { teilnehmerId?|turnierId? }, notiz? }`
|
||||||
|
- Wirkung: Aktualisiert `teilnehmer_konten` bzw. `turnier_kassa` atomar.
|
||||||
|
|
||||||
|
## Validierung & Regeln (voraussichtlich)
|
||||||
|
|
||||||
|
- Atomare Konsistenz: Transaktion + Saldo‑Update innerhalb einer DB‑Transaktion.
|
||||||
|
- Negativsaldo optional durch Feature‑Flag blockierbar.
|
||||||
|
- Audit‑Trail pro Vorgang (who/when), Idempotenz‑Key optional.
|
||||||
|
|
||||||
|
## Implementierungsstand
|
||||||
|
|
||||||
|
- Datenstrukturen `teilnehmer_konten` und `turnier_kassa` sind per Flyway angelegt (siehe [Datenbankschema](../Schema/Database_Schema_V1-V009.md)).
|
||||||
|
- Endpunkte folgen nach Abschluss Backend B‑2. Dieses Dokument wird dann auf `status: ACTIVE` gesetzt.
|
||||||
28
docs/05_Backend/Multi_Tenant_Kurz.md
Normal file
28
docs/05_Backend/Multi_Tenant_Kurz.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
type: Reference
|
||||||
|
status: ACTIVE
|
||||||
|
owner: Lead Architect
|
||||||
|
last_update: 2026-04-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tenant-Isolation & Multi‑Tenant (Kurzfassung)
|
||||||
|
|
||||||
|
Vollständige Entscheidung: [ADR‑0021: Tenant‑Resolution‑Strategie (Schema‑per‑Tenant)](../01_Architecture/adr/0021-tenant-resolution-strategy-de.md).
|
||||||
|
|
||||||
|
## Kernaussagen
|
||||||
|
|
||||||
|
- Eine Veranstaltung = ein Tenant = ein Datenbankschema (Schema‑per‑Tenant).
|
||||||
|
- Requests tragen `X-Event-Id`; Backend validiert gegen `control.tenants` und schaltet das Schema je Request.
|
||||||
|
- Flyway führt Migrationen je Tenant‑Schema aus (eigene `flyway_schema_history`).
|
||||||
|
- Stammdaten (Reiter/Pferde/Vereine/Funktionäre) sind global und nicht tenant‑spezifisch; Entries/Kassa sind tenant‑lokal.
|
||||||
|
|
||||||
|
## Umsetzung (Kurz)
|
||||||
|
|
||||||
|
- Web‑Layer: `TenantWebFilter` (Spring) bzw. Plugin (Ktor) liest `X-Event-Id` und legt `TenantContext` ab.
|
||||||
|
- Persistence: SCHEMA‑Multitenancy (`SET search_path`) oder Hibernate‑`MultiTenantConnectionProvider`.
|
||||||
|
- Registry: `control.tenants(event_id, schema_name, status, db_url?, version, created_at)`.
|
||||||
|
|
||||||
|
## Betroffene Bereiche
|
||||||
|
|
||||||
|
- Datenmodell tenant‑lokal: `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen`, `teilnehmer_konten`, `turnier_kassa` (siehe [Datenbankschema](./Schema/Database_Schema_V1-V009.md)).
|
||||||
|
- Services: Der Entries‑Service arbeitet mandantenfähig; andere Services bleiben Single‑Tenant/global (vgl. Backend‑Roadmap).
|
||||||
|
|
@ -11,9 +11,15 @@ Dieses Verzeichnis enthält die spezifische Dokumentation für alle Backend-Komp
|
||||||
## Struktur
|
## Struktur
|
||||||
|
|
||||||
* `Services/`: Enthält pro Service eine dedizierte Markdown-Datei, die dessen Zweck, API, Datenmodell und Konfiguration beschreibt.
|
* `Services/`: Enthält pro Service eine dedizierte Markdown-Datei, die dessen Zweck, API, Datenmodell und Konfiguration beschreibt.
|
||||||
|
* `API/`: Querliegende API-Referenzen und Übersichten (Stammdaten, Kassa, usw.).
|
||||||
|
* `Schema/`: Datenbankschemata und Migrationsübersichten (Flyway).
|
||||||
* `Integration/`: Dokumentation zur Interaktion zwischen den Services (z.B. Event-Flows).
|
* `Integration/`: Dokumentation zur Interaktion zwischen den Services (z.B. Event-Flows).
|
||||||
|
|
||||||
## Wichtige Einstiegspunkte
|
## Wichtige Einstiegspunkte
|
||||||
|
|
||||||
* **[Ping-Service](./Services/PingService_Reference.md):** Dient als technischer Blueprint und einfachstes Beispiel für einen Service.
|
* **[Ping-Service](./Services/PingService_Reference.md):** Dient als technischer Blueprint und einfachstes Beispiel für einen Service.
|
||||||
* **[API-Gateway](../07_Infrastructure/api-gateway.md):** Beschreibung des zentralen Einstiegspunkts für alle externen Anfragen.
|
* **[API-Gateway](../07_Infrastructure/api-gateway.md):** Beschreibung des zentralen Einstiegspunkts für alle externen Anfragen.
|
||||||
|
* **[Stammdaten-APIs (Reiter, Pferde, Vereine, Funktionäre)](./API/API_Uebersicht_Stammdaten.md):** Konsolidierte Endpunkt-Übersicht (Backend B‑1 abgeschlossen).
|
||||||
|
* **[Datenbankschema V1–V009](./Schema/Database_Schema_V1-V009.md):** Tabellen und Constraints (veranstaltungen, turniere, bewerbe, abteilungen, teilnehmer_konten, turnier_kassa).
|
||||||
|
* **[Kassa-API](./API/Kassa_API.md):** Platzhalter für Saldo/Transaktionen; wird ergänzt, sobald Backend B‑2 Kassa fertig ist.
|
||||||
|
* **[Tenant-Isolation & Multi‑Tenant kurz](./Multi_Tenant_Kurz.md):** Zusammenfassung gem. ADR‑0021.
|
||||||
|
|
|
||||||
130
docs/05_Backend/Schema/Database_Schema_V1-V009.md
Normal file
130
docs/05_Backend/Schema/Database_Schema_V1-V009.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
---
|
||||||
|
type: Reference
|
||||||
|
status: ACTIVE
|
||||||
|
owner: Backend Developer
|
||||||
|
last_update: 2026-04-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# Datenbankschema V1–V009 (Tenant‑Schema)
|
||||||
|
|
||||||
|
Quelle: Flyway‑Migrationen im Entries‑Service (`backend/services/entries/entries-service/src/main/resources/db/tenant/`), insbesondere `V2__domain_hierarchy.sql`.
|
||||||
|
|
||||||
|
Hinweis zur Architektur: Je Veranstaltung (Tenant) existiert ein eigenes Datenbankschema (ADR‑0021). Alle untenstehenden Tabellen werden pro Tenant‑Schema angelegt und sind somit mandantengetrennt.
|
||||||
|
|
||||||
|
## Tabellenübersicht
|
||||||
|
|
||||||
|
- `veranstaltungen` — Eine Veranstaltung (Singleton im Tenant‑Schema)
|
||||||
|
- `turniere` — Turniere einer Veranstaltung (1:N zu `veranstaltungen`)
|
||||||
|
- `bewerbe` — Bewerbe/Prüfungen eines Turniers (1:N zu `turniere`)
|
||||||
|
- `abteilungen` — Abteilungen/Heats eines Bewerbs (1:N zu `bewerbe`)
|
||||||
|
- `teilnehmer_konten` — Aggregierte Salden eines Teilnehmers über alle Turniere der Veranstaltung
|
||||||
|
- `turnier_kassa` — Kassa‑Saldo pro Turnier
|
||||||
|
|
||||||
|
## Detaillierte Definitionen (aus V2__domain_hierarchy.sql)
|
||||||
|
|
||||||
|
### veranstaltungen
|
||||||
|
Primärschlüssel: `id (UUID)`
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
- `id UUID PRIMARY KEY`
|
||||||
|
- `created_at TIMESTAMPTZ NOT NULL`
|
||||||
|
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||||
|
|
||||||
|
### turniere
|
||||||
|
Primärschlüssel: `id (UUID)`
|
||||||
|
Fremdschlüssel: `veranstaltung_id → veranstaltungen(id) ON DELETE CASCADE`
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
- `id UUID PRIMARY KEY`
|
||||||
|
- `veranstaltung_id UUID NOT NULL`
|
||||||
|
- `oeps_turniernummer VARCHAR(50) NOT NULL`
|
||||||
|
- `created_at TIMESTAMPTZ NOT NULL`
|
||||||
|
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||||
|
|
||||||
|
Indizes/Constraints:
|
||||||
|
- `UNIQUE (oeps_turniernummer)` → `uq_turniere_oeps_nr`
|
||||||
|
- `INDEX (veranstaltung_id)` → `idx_turniere_veranstaltung_id`
|
||||||
|
|
||||||
|
### bewerbe
|
||||||
|
Primärschlüssel: `id (UUID)`
|
||||||
|
Fremdschlüssel: `turnier_id → turniere(id) ON DELETE CASCADE`
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
- `id UUID PRIMARY KEY`
|
||||||
|
- `turnier_id UUID NOT NULL`
|
||||||
|
- `klasse VARCHAR(50) NOT NULL`
|
||||||
|
- `hoehe_cm INTEGER NULL`
|
||||||
|
- `bezeichnung TEXT NOT NULL`
|
||||||
|
- `created_at TIMESTAMPTZ NOT NULL`
|
||||||
|
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||||
|
|
||||||
|
Indizes:
|
||||||
|
- `INDEX (turnier_id)` → `idx_bewerbe_turnier_id`
|
||||||
|
- `INDEX (klasse)` → `idx_bewerbe_klasse`
|
||||||
|
|
||||||
|
### abteilungen
|
||||||
|
Primärschlüssel: `id (UUID)`
|
||||||
|
Fremdschlüssel: `bewerb_id → bewerbe(id) ON DELETE CASCADE`
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
- `id UUID PRIMARY KEY`
|
||||||
|
- `bewerb_id UUID NOT NULL`
|
||||||
|
- `nr INTEGER NOT NULL`
|
||||||
|
- `bezeichnung TEXT NOT NULL`
|
||||||
|
- `typ VARCHAR(32) NOT NULL` (Werte: `SEPARATE_SIEGEREHRUNG`, `ORGANISATORISCH`)
|
||||||
|
- `created_at TIMESTAMPTZ NOT NULL`
|
||||||
|
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||||
|
|
||||||
|
Constraints/Indizes:
|
||||||
|
- `CHECK (typ IN ('SEPARATE_SIEGEREHRUNG','ORGANISATORISCH'))` → `chk_abteilungen_typ`
|
||||||
|
- `UNIQUE (bewerb_id, nr)` → `uq_abteilungen_bewerb_nr`
|
||||||
|
- `INDEX (bewerb_id)` → `idx_abteilungen_bewerb_id`
|
||||||
|
- `INDEX (typ)` → `idx_abteilungen_typ`
|
||||||
|
|
||||||
|
### teilnehmer_konten
|
||||||
|
Primärschlüssel: `id (UUID)`
|
||||||
|
Fremdschlüssel: `veranstaltung_id → veranstaltungen(id) ON DELETE CASCADE`
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
- `id UUID PRIMARY KEY`
|
||||||
|
- `veranstaltung_id UUID NOT NULL`
|
||||||
|
- `teilnehmer_id UUID NOT NULL`
|
||||||
|
- `saldo_cents BIGINT NOT NULL DEFAULT 0`
|
||||||
|
- `currency CHAR(3) NOT NULL DEFAULT 'EUR'`
|
||||||
|
- `created_at TIMESTAMPTZ NOT NULL`
|
||||||
|
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||||
|
|
||||||
|
Indizes/Constraints:
|
||||||
|
- `UNIQUE (veranstaltung_id, teilnehmer_id)` → `uq_tkonten_veranstaltung_teilnehmer`
|
||||||
|
- `INDEX (veranstaltung_id)` → `idx_tkonten_veranstaltung_id`
|
||||||
|
- `INDEX (teilnehmer_id)` → `idx_tkonten_teilnehmer_id`
|
||||||
|
|
||||||
|
### turnier_kassa
|
||||||
|
Primärschlüssel: `id (UUID)`
|
||||||
|
Fremdschlüssel: `turnier_id → turniere(id) ON DELETE CASCADE`
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
- `id UUID PRIMARY KEY`
|
||||||
|
- `turnier_id UUID NOT NULL`
|
||||||
|
- `saldo_cents BIGINT NOT NULL DEFAULT 0`
|
||||||
|
- `currency CHAR(3) NOT NULL DEFAULT 'EUR'`
|
||||||
|
- `created_at TIMESTAMPTZ NOT NULL`
|
||||||
|
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||||
|
|
||||||
|
Indizes/Constraints:
|
||||||
|
- `UNIQUE (turnier_id)` → `uq_turnier_kassa_turnier`
|
||||||
|
- `INDEX (turnier_id)` → `idx_turnier_kassa_turnier_id`
|
||||||
|
|
||||||
|
## Versionierung / Flyway
|
||||||
|
|
||||||
|
- Die oben dokumentierten Tabellen sind in `V2__domain_hierarchy.sql` definiert.
|
||||||
|
- Weitere Migrationen V1–V009 betreffen Bootstrap/Erweiterungen; diese Seite wird fortlaufend ergänzt, sobald neue fachrelevante Strukturen hinzukommen.
|
||||||
|
|
||||||
|
## Beziehungen (Kurz)
|
||||||
|
|
||||||
|
`veranstaltungen (1) ──< (N) turniere (1) ──< (N) bewerbe (1) ──< (N) abteilungen`
|
||||||
|
|
||||||
|
Separat aggregierend:
|
||||||
|
|
||||||
|
- `teilnehmer_konten` auf Veranstaltungsebene (pro Teilnehmer genau ein Konto)
|
||||||
|
- `turnier_kassa` auf Turnierebene (pro Turnier genau ein Kassa‑Eintrag)
|
||||||
Loading…
Reference in New Issue
Block a user