--- type: BACKEND_SPEC status: DRAFT owner: Rulebook Expert last_update: 2026-04-03 sprint: B-2 --- # B-2 Backend-Übergabe: Regulation-as-Data > **Zweck:** Vollständige Spezifikation der Lizenz-/Altersmatrix als Regulation-as-Data für das Backend ( > Masterdata-SCS). > Dieses Dokument ist die verbindliche Übergabe vom 📜 Rulebook Expert an 👷 Backend Developer. > Nach Fachfreigabe durch das ÖTO-Fachreferat wird der Status von `DRAFT` auf `STABLE` angehoben. --- ## 1. Überblick: Was wird übergeben? | Artefakt | Datei / Ort | Status | |---------------------------------------------------------------------|--------------------------------------------------------------------------|-----------------------------| | `LizenzKlasseE`-Enum (korrigiert, R4 ergänzt) | `core/core-domain/.../Enums.kt` | ✅ Implementiert | | Flyway V008 (Turnierklassen + Lizenz-Matrix + Altersklassen Reiter) | `masterdata-service/.../V008__Seed_OETO_2026_Data.sql` | ✅ Korrigiert (RD4 entfernt) | | Flyway V009 (Höhen-Lizenz-Matrix + Mindestalter-Pferd-Matrix) | `masterdata-service/.../V009__Add_HorseAge_And_LicenseHeight_Matrix.sql` | ✅ Neu angelegt | | Validierungsregeln v0.3 | `docs/03_Domain/02_Reference/Validierungsregeln.md` | DRAFT | | Diese Spezifikation | `docs/.../B2-Backend-Uebergabe-Regulation-as-Data.md` | DRAFT | --- ## 2. Enum-Korrekturen (core-domain) ### 2.1 `LizenzKlasseE` — Vollständiger Katalog ```kotlin enum class LizenzKlasseE { LIZENZFREI, // Keine Lizenz erforderlich (= "ohne Lizenz", CSN-C-NEU) R1, // Reiter-Lizenz Klasse 1 R2, // Reiter-Lizenz Klasse 2 R3, // Reiter-Lizenz Klasse 3 R4, // Reiter-Lizenz Klasse 4 ← NEU ergänzt (war fehlend) RD1, // Dressur-Reiter Klasse 1 RD2, // Dressur-Reiter Klasse 2 RD3, // Dressur-Reiter Klasse 3 JN, // Jugend/Nachwuchs JG, // Junioren YR // Young Rider } ``` > ⚠️ **Wichtig:** `RD4` existiert **nicht** im ÖTO-Regelwerk. Der bisherige Seed V008 enthielt `RD4` fälschlicherweise — > wurde in V008 korrigiert (entfernt). Höchste Dressur-Lizenz ist `RD3`. ### 2.2 Schlüssel-Konvention (SSoT) | Doku-Label | Enum-Key (SSoT) | Hinweis | |------------------------------|------------------|-----------------------------------| | „ohne Lizenz" / „lizenzfrei" | `LIZENZFREI` | Nicht `LZF` — Enum ist maßgeblich | | „mit Lizenz" / „R1" | `R1` | | | „R2 und höher" | `R2`, `R3`, `R4` | Aufwärts-kompatibel | --- ## 3. Datenbank-Tabellen (Regulation-as-Data) ### 3.1 Bestehende Tabellen (V005/V008) | Tabelle | Zweck | |------------------|---------------------------------------------------------------------------| | `turnierklasse` | Klassen-Codes (E, A, L, LM, M, S) je Sparte mit `max_hoehe` | | `license_matrix` | Max. Turnierklasse je Lizenz (1 Zeile pro Lizenz × Sparte) | | `altersklasse` | Altersklassen der **Reiter** (Kinder, Jugend, Junioren, YR, AK, Senioren) | ### 3.2 Neue Tabellen (V009) #### `license_height_matrix` Granulare Höhen-Lizenz-Zuordnung für Springen (eine Zeile pro Höhenbereich × erlaubte Lizenz). ```sql -- Abfrage: Welche Lizenzen sind für 110 cm erlaubt? SELECT lizenz_klasse FROM license_height_matrix WHERE sparte = 'SPRINGEN' AND hoehe_von_cm <= 110 AND hoehe_bis_cm >= 110 AND ist_aktiv = true; -- Ergebnis: R1, R2, R3, R4 ``` #### `horse_min_age_matrix` Mindestalter des **Pferdes** (Stichtag 1. Jänner) je Sparte + Höhenbereich oder Aufgabenniveau. ```sql -- Abfrage: Mindestalter für Springen 125 cm? SELECT min_alter_jahre FROM horse_min_age_matrix WHERE sparte = 'SPRINGEN' AND hoehe_von_cm <= 125 AND hoehe_bis_cm >= 125 AND ist_aktiv = true; -- Ergebnis: 5 ``` --- ## 4. Lizenz × Bewerb-Tabellen (DRAFT → Fachfreigabe erforderlich) ### 4.1 Springen (CSN) — Lizenz-Zuordnung nach Hindernishöhe > Quelle: ÖTO 2026 § 231 | Status: **DRAFT** — Fachfreigabe ausstehend | Klasse | Höhe (cm) | Mindest-Lizenz | Zugelassene Lizenzen | Bemerkung | |--------------------|-----------|----------------|----------------------------|---------------------------------------------------------------| | E0 (Einsteiger) | 60–95 | LIZENZFREI | LIZENZFREI, R1, R2, R3, R4 | CSN-C-NEU: Zwangsteilung in Abt. „ohne Lizenz" + „mit Lizenz" | | A (Anfänger) | 100–110 | R1 | R1, R2, R3, R4 | Keine LIZENZFREI mehr ab 100 cm | | L (Leicht) | 115–120 | R1* | R1, R2, R3, R4 | *Ausschreibung kann R2 vorschreiben | | LM (Leicht-Mittel) | 125–130 | R2 | R2, R3, R4 | | | M (Mittelschwer) | 135 | R3 | R3, R4 | | | S (Schwer) | 140–160 | R4 | R4 | | **Aufwärts-Kompatibilität:** Eine höhere Lizenz berechtigt immer zur Teilnahme in niedrigeren Klassen. ### 4.2 Dressur (CDN) — Lizenz-Zuordnung nach Aufgabenniveau > Quelle: ÖTO 2026 § 103 | Status: **DRAFT** — Fachfreigabe ausstehend | Klasse | Aufgabenniveau | Mindest-Lizenz | Zugelassene Lizenzen | |--------------------|----------------|----------------|---------------------------| | Einsteiger | E | LIZENZFREI | LIZENZFREI, RD1, RD2, RD3 | | A (Anfänger) | A | RD1 | RD1, RD2, RD3 | | L (Leicht) | L | RD1 | RD1, RD2, RD3 | | LM (Leicht-Mittel) | LM | RD2 | RD2, RD3 | | M (Mittelschwer) | M | RD2 | RD2, RD3 | | S (Schwer) | S | RD3 | RD3 | --- ## 5. Mindestalter Pferd (Regulation-as-Data) > Stichtagsregel: Alter = `veranstaltungsjahr - geburtsjahr` (Stichtag 1. Jänner) ### 5.1 Springen national (ÖTO § 231) | Höhe (cm) | Mindestalter Pferd (Jahre) | |-----------|----------------------------| | 60–100 | 4 | | 101–120 | 5 | | 121–999 | 6 | ### 5.2 Dressur national (ÖTO § 103) | Aufgabenniveau | Mindestalter Pferd (Jahre) | |----------------|----------------------------| | E | 4 | | A | 4 | | L | 4 | | LM | 5 | | M | 5 | | S | 6 | ### 5.3 International FEI (GR Art. 136) | Disziplin | Level | Mindestalter Pferd | |-----------|-----------------|------------------------| | Springen | 1*–2* | 6 | | Springen | 3*–5* | 7 | | Dressur | CDI Senior | 7 | | Dressur | CDI Young Horse | 4 (gem. FEI YH-Regeln) | --- ## 6. Serverseitige Validierungslogik (Pseudocode) Das Backend soll die folgenden Prüfungen serverseitig durchführen (analog zur Frontend-Validierung): ### 6.1 Lizenz-Check (Springen) ```kotlin fun isLicenseAllowedForHeight(hoeheCm: Int, lizenz: LizenzKlasseE): Boolean { // Abfrage license_height_matrix val allowed = licenseHeightMatrixRepo.findAllowedLicenses( sparte = "SPRINGEN", hoehe = hoeheCm ) return lizenz.name in allowed } ``` ### 6.2 Lizenz-Check (Dressur) ```kotlin fun isLicenseAllowedForLevel(niveau: String, lizenz: LizenzKlasseE): Boolean { // Abfrage license_matrix (max_turnierklasse_code Vergleich) val maxKlasse = licenseMatrixRepo.findMaxKlasse(sparte = "DRESSUR", lizenz = lizenz.name) return klasseOrdnung.indexOf(niveau) <= klasseOrdnung.indexOf(maxKlasse) } ``` ### 6.3 Mindestalter-Pferd-Check ```kotlin fun isHorseOldEnough( geburtsjahr: Int, veranstaltungsjahr: Int, sparte: String, hoeheCm: Int?, niveau: String? ): Boolean { val alter = veranstaltungsjahr - geburtsjahr // Stichtag 1. Jänner val minAlter = horseMinAgeRepo.findMinAlter(sparte, hoeheCm, niveau) return alter >= minAlter } ``` --- ## 7. REST-Endpunkte (Masterdata-SCS) — Empfehlung | Methode | Pfad | Beschreibung | |---------|-------------------------------------------------------------------|-------------------------------------------------------| | `GET` | `/api/regulation/license-height-matrix?sparte=SPRINGEN&hoehe=110` | Erlaubte Lizenzen für Höhe | | `GET` | `/api/regulation/license-matrix?sparte=DRESSUR&lizenz=RD2` | Max. Klasse für Lizenz | | `GET` | `/api/regulation/horse-min-age?sparte=SPRINGEN&hoehe=125` | Mindestalter Pferd | | `GET` | `/api/regulation/horse-min-age?sparte=DRESSUR&niveau=LM` | Mindestalter Pferd (Dressur) | | `GET` | `/api/fei/resolve/{id}` | FEI Legacy→Numeric Resolver (bereits implementiert ✅) | --- ## 8. Abweichungen Backend ↔ Frontend | Thema | Frontend (KMP) | Backend (Spring) | Handlungsbedarf | |--------------------|---------------------------------|------------------------|-------------------------------------------------------------| | Lizenz-Keys | `LZF` (alter Key in Doku) | `LIZENZFREI` (Enum) | Doku korrigiert; Frontend-Code prüfen | | R4 | Vorhanden in `OetoValidators` | Enum jetzt ergänzt ✅ | Kein weiterer Bedarf | | RD4 | Nicht vorhanden | Seed V008 korrigiert ✅ | Kein weiterer Bedarf | | Höhen-Matrix | `OetoValidators.kt` (hardcoded) | V009 als DB-Tabelle | Backend liest aus DB; Frontend bleibt hardcoded bis Phase 2 | | Mindestalter Pferd | `OetoValidators.kt` (hardcoded) | V009 als DB-Tabelle | Wie oben | --- ## 9. Fachfreigabe-Checkliste (DRAFT → STABLE) Folgende Punkte müssen vor Anhebung auf `STABLE` durch das ÖTO-Fachreferat bestätigt werden: - [ ] Lizenz-Zuordnungstabelle Springen (§ 231): Höhenschwellen und Mindestlizenzen korrekt? - [ ] Lizenz-Zuordnungstabelle Dressur (§ 103): Niveaustufen und Mindestlizenzen korrekt? - [ ] Mindestalter Pferd Springen (§ 231): Schwellen 4/5/6 Jahre korrekt? - [ ] Mindestalter Pferd Dressur (§ 103): Schwellen je Niveau korrekt? - [ ] Aufwärts-Kompatibilität (höhere Lizenz → niedrigere Klasse erlaubt): bestätigt? - [ ] Paragraphen-Nummern (§ 231, § 103) final verifiziert? - [ ] Sonderfall CSN-C-NEU Zwangsteilung (LIZENZFREI + R1 in E0): korrekt? Nach Bestätigung aller Punkte: 1. Status in dieser Datei auf `STABLE` setzen 2. `Validierungsregeln.md` → `status: STABLE`, `version: 1.0` 3. Flyway V009 → Kommentar `Status: DRAFT` entfernen 4. Roadmap B-2 als abgeschlossen markieren --- ## 10. Offene Punkte | # | Thema | Verantwortlich | Priorität | |---|-----------------------------------------------------------------|--------------------------|------------| | 1 | Fachfreigabe Lizenz×Bewerb-Tabellen einholen | 📜 Rulebook Expert | 🔴 Hoch | | 2 | Backend-Endpunkte `/api/regulation/*` implementieren | 👷 Backend | 🔴 Hoch | | 3 | Frontend `LZF`-Key → `LIZENZFREI` prüfen/angleichen | 🎨 Frontend | 🟠 Mittel | | 4 | `AltersklasseRechner` (C-1) implementieren | 👷 Backend + 📜 Rulebook | 🟠 Mittel | | 5 | FEI-Mindestalter-Tabellen aus Disziplinregelwerken finalisieren | 📜 Rulebook Expert | 🟡 Niedrig |