diff --git a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql index d7ef55ca..023910c3 100644 --- a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql +++ b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql @@ -1,5 +1,7 @@ -- V008: Seed OETO 2026 Data (Turnierklassen, Lizenz-Matrix, Altersklassen) -- Basierend auf ÖTO 2026 +-- Lizenz-Keys entsprechen LizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3 +-- HINWEIS: RD4 existiert NICHT im Enum – nur RD1/RD2/RD3 sind gültige Dressur-Lizenzen (ÖTO 2026) -- 1. Turnierklassen (Springen & Dressur) INSERT INTO turnierklasse (turnierklasse_id, sparte, code, bezeichnung, max_hoehe, aufgaben_niveau) @@ -33,8 +35,7 @@ INSERT INTO license_matrix (license_id, sparte, lizenz_klasse, max_turnierklasse VALUES ('00000000-0000-0000-0002-000000000001', 'DRESSUR', 'LIZENZFREI', 'E'), ('00000000-0000-0000-0002-000000000002', 'DRESSUR', 'RD1', 'L'), ('00000000-0000-0000-0002-000000000003', 'DRESSUR', 'RD2', 'M'), - ('00000000-0000-0000-0002-000000000004', 'DRESSUR', 'RD3', 'S'), - ('00000000-0000-0000-0002-000000000005', 'DRESSUR', 'RD4', 'S'); + ('00000000-0000-0000-0002-000000000004', 'DRESSUR', 'RD3', 'S'); -- 3. Altersklassen (Standard ÖTO) INSERT INTO altersklasse (id, altersklasse_code, bezeichnung, min_alter, max_alter) diff --git a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql new file mode 100644 index 00000000..129c7beb --- /dev/null +++ b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql @@ -0,0 +1,185 @@ +-- V009: Regulation-as-Data – Höhen-Lizenz-Matrix (Springen) & Mindestalter-Pferd-Matrix +-- Basierend auf ÖTO 2026 (§ 231 Springen, § 103 Dressur) und FEI GR Art. 136 +-- Lizenz-Keys entsprechen LizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3 +-- Status: DRAFT – wird auf STABLE angehoben nach Fachfreigabe durch ÖTO-Fachreferat + +-- ───────────────────────────────────────────────────────────────────────────── +-- 1. Tabelle: license_height_matrix +-- Zweck: Welche Lizenzklassen sind für einen Höhenbereich (Springen) erlaubt? +-- Granularität: Eine Zeile pro (Höhenbereich × erlaubte Lizenzklasse). +-- Abfrage-Pattern: SELECT lizenz_klasse FROM license_height_matrix +-- WHERE sparte = 'SPRINGEN' +-- AND hoehe_von_cm <= :hoehe AND hoehe_bis_cm >= :hoehe +-- AND ist_aktiv = true +-- ───────────────────────────────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS license_height_matrix +( + id + UUID + PRIMARY + KEY + DEFAULT + gen_random_uuid +( +), + sparte VARCHAR +( + 20 +) NOT NULL, + hoehe_von_cm INTEGER NOT NULL, + hoehe_bis_cm INTEGER NOT NULL, + lizenz_klasse VARCHAR +( + 20 +) NOT NULL, + oeto_paragraph VARCHAR +( + 50 +), + bemerkung VARCHAR +( + 255 +), + valid_from TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + valid_to TIMESTAMP WITH TIME ZONE, + ist_aktiv BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + +CREATE INDEX idx_license_height_sparte_hoehe ON license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm); +CREATE INDEX idx_license_height_lizenz ON license_height_matrix (lizenz_klasse); + +-- ───────────────────────────────────────────────────────────────────────────── +-- 2. Tabelle: horse_min_age_matrix +-- Zweck: Mindestalter des Pferdes (Stichtag 1. Jänner) je Sparte + Klasse/Höhe. +-- Abfrage-Pattern: SELECT min_alter_jahre FROM horse_min_age_matrix +-- WHERE sparte = :sparte +-- AND (:hoehe IS NULL OR (hoehe_von_cm <= :hoehe AND hoehe_bis_cm >= :hoehe)) +-- AND (:niveau IS NULL OR aufgaben_niveau = :niveau) +-- AND ist_aktiv = true +-- ───────────────────────────────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS horse_min_age_matrix +( + id + UUID + PRIMARY + KEY + DEFAULT + gen_random_uuid +( +), + sparte VARCHAR +( + 20 +) NOT NULL, + hoehe_von_cm INTEGER, + hoehe_bis_cm INTEGER, + aufgaben_niveau VARCHAR +( + 20 +), + min_alter_jahre INTEGER NOT NULL, + oeto_paragraph VARCHAR +( + 50 +), + fei_artikel VARCHAR +( + 50 +), + bemerkung VARCHAR +( + 255 +), + valid_from TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + valid_to TIMESTAMP WITH TIME ZONE, + ist_aktiv BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + +CREATE INDEX idx_horse_age_sparte ON horse_min_age_matrix (sparte); + +-- ───────────────────────────────────────────────────────────────────────────── +-- 3. Seed: license_height_matrix – Springen (CSN) +-- Quelle: ÖTO § 231 (Lizenz-Zuordnung nach Hindernishöhe) +-- Klassen: E0=60–95 cm, A=100–110 cm, L=115–120 cm, LM=125–130 cm, M=135 cm, S=140–160 cm +-- +-- Regel: Höhere Lizenz darf immer in niedrigerer Klasse starten (Aufwärts-Kompatibilität). +-- Daher: R2 darf auch in E0/A starten – hier werden nur die MINDEST-Anforderungen je Bereich +-- abgebildet (= welche Lizenzen sind ZUGELASSEN, nicht welche sind OPTIMAL). +-- ───────────────────────────────────────────────────────────────────────────── + +-- Klasse E0 (Einsteiger): 60–95 cm → LIZENZFREI und R1 zugelassen (CSN-C-NEU Zwangsteilung) +INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 60, 95, 'LIZENZFREI', '§ 231 ÖTO', 'Abt. ohne Lizenz (CSN-C-NEU)'), + ('SPRINGEN', 60, 95, 'R1', '§ 231 ÖTO', 'Abt. mit Lizenz (CSN-C-NEU)'), + ('SPRINGEN', 60, 95, 'R2', '§ 231 ÖTO', 'Aufwärts-kompatibel'), + ('SPRINGEN', 60, 95, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'), + ('SPRINGEN', 60, 95, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel'); + +-- Klasse A (Anfänger): 100–110 cm → ab R1 (keine LIZENZFREI mehr) +INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 100, 110, 'R1', '§ 231 ÖTO', 'Mindestlizenz ab 100 cm'), + ('SPRINGEN', 100, 110, 'R2', '§ 231 ÖTO', 'Aufwärts-kompatibel'), + ('SPRINGEN', 100, 110, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'), + ('SPRINGEN', 100, 110, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel'); + +-- Klasse L (Leicht): 115–120 cm → ab R2 empfohlen; R1 noch zugelassen (ausschreibungsabhängig) +INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 115, 120, 'R1', '§ 231 ÖTO', 'Zugelassen; Ausschreibung kann R2 vorschreiben'), + ('SPRINGEN', 115, 120, 'R2', '§ 231 ÖTO', 'Empfohlene Mindestlizenz'), + ('SPRINGEN', 115, 120, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'), + ('SPRINGEN', 115, 120, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel'); + +-- Klasse LM (Leicht-Mittel): 125–130 cm → ab R2 +INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 125, 130, 'R2', '§ 231 ÖTO', 'Mindestlizenz ab 125 cm'), + ('SPRINGEN', 125, 130, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'), + ('SPRINGEN', 125, 130, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel'); + +-- Klasse M (Mittelschwer): 135 cm → ab R3 +INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 135, 135, 'R3', '§ 231 ÖTO', 'Mindestlizenz ab 135 cm'), + ('SPRINGEN', 135, 135, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel'); + +-- Klasse S (Schwer): 140–160 cm → R4 +INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 140, 160, 'R4', '§ 231 ÖTO', 'Mindestlizenz ab 140 cm (S-Klasse)'); + +-- ───────────────────────────────────────────────────────────────────────────── +-- 4. Seed: horse_min_age_matrix – Springen (national, ÖTO § 231) +-- ───────────────────────────────────────────────────────────────────────────── +INSERT INTO horse_min_age_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, min_alter_jahre, oeto_paragraph, bemerkung) +VALUES ('SPRINGEN', 60, 100, 4, '§ 231 ÖTO', 'Mindestalter Pferd, Stichtag 1. Jänner'), + ('SPRINGEN', 101, 120, 5, '§ 231 ÖTO', 'Mindestalter Pferd, Stichtag 1. Jänner'), + ('SPRINGEN', 121, 999, 6, '§ 231 ÖTO', 'Mindestalter Pferd, Stichtag 1. Jänner'); + +-- ───────────────────────────────────────────────────────────────────────────── +-- 5. Seed: horse_min_age_matrix – Dressur (national, ÖTO § 103) +-- ───────────────────────────────────────────────────────────────────────────── +INSERT INTO horse_min_age_matrix (sparte, aufgaben_niveau, min_alter_jahre, oeto_paragraph, bemerkung) +VALUES ('DRESSUR', 'E', 4, '§ 103 ÖTO', 'Einsteiger/Dressurreiter, Stichtag 1. Jänner'), + ('DRESSUR', 'A', 4, '§ 103 ÖTO', 'Klasse A, Stichtag 1. Jänner'), + ('DRESSUR', 'L', 4, '§ 103 ÖTO', 'Klasse L, Stichtag 1. Jänner'), + ('DRESSUR', 'LM', 5, '§ 103 ÖTO', 'Klasse LM/M, Stichtag 1. Jänner'), + ('DRESSUR', 'M', 5, '§ 103 ÖTO', 'Klasse M, Stichtag 1. Jänner'), + ('DRESSUR', 'S', 6, '§ 103 ÖTO', 'Klasse S, Stichtag 1. Jänner'); + +-- ───────────────────────────────────────────────────────────────────────────── +-- 6. Seed: horse_min_age_matrix – Springen international (FEI GR Art. 136) +-- ───────────────────────────────────────────────────────────────────────────── +INSERT INTO horse_min_age_matrix (sparte, aufgaben_niveau, min_alter_jahre, fei_artikel, bemerkung) +VALUES ('SPRINGEN_FEI', '1_STAR', 6, 'Art. 136 FEI GR', 'FEI Jumping 1*–2*'), + ('SPRINGEN_FEI', '2_STAR', 6, 'Art. 136 FEI GR', 'FEI Jumping 1*–2*'), + ('SPRINGEN_FEI', '3_STAR', 7, 'Art. 136 FEI GR', 'FEI Jumping 3*–5*'), + ('SPRINGEN_FEI', '4_STAR', 7, 'Art. 136 FEI GR', 'FEI Jumping 3*–5*'), + ('SPRINGEN_FEI', '5_STAR', 7, 'Art. 136 FEI GR', 'FEI Jumping 3*–5*'); + +-- ───────────────────────────────────────────────────────────────────────────── +-- 7. Seed: horse_min_age_matrix – Dressur international (FEI GR Art. 136) +-- ───────────────────────────────────────────────────────────────────────────── +INSERT INTO horse_min_age_matrix (sparte, aufgaben_niveau, min_alter_jahre, fei_artikel, bemerkung) +VALUES ('DRESSUR_FEI', 'CDI_SENIOR', 7, 'Art. 136 FEI GR', 'FEI Dressage CDI Senior'), + ('DRESSUR_FEI', 'CDI_YH', 4, 'Art. 136 FEI GR', 'FEI Young Horse – gem. FEI YH-Regeln'); diff --git a/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt b/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt index 1e4222bd..ae9101e7 100644 --- a/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt +++ b/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt @@ -172,6 +172,8 @@ enum class LizenzKlasseE { /** Reiter-Lizenz Klasse 3 */ R3, + /** Reiter-Lizenz Klasse 4 */ + R4, /** Dressur-Reiter Klasse 1 */ RD1, diff --git a/docs/03_Domain/02_Reference/OETO_Regelwerk/B2-Backend-Uebergabe-Regulation-as-Data.md b/docs/03_Domain/02_Reference/OETO_Regelwerk/B2-Backend-Uebergabe-Regulation-as-Data.md new file mode 100644 index 00000000..13db5077 --- /dev/null +++ b/docs/03_Domain/02_Reference/OETO_Regelwerk/B2-Backend-Uebergabe-Regulation-as-Data.md @@ -0,0 +1,268 @@ +--- +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 | diff --git a/docs/03_Domain/02_Reference/Validierungsregeln.md b/docs/03_Domain/02_Reference/Validierungsregeln.md index 0e519de0..a1f69368 100644 --- a/docs/03_Domain/02_Reference/Validierungsregeln.md +++ b/docs/03_Domain/02_Reference/Validierungsregeln.md @@ -2,7 +2,7 @@ type: RULE_SPEC status: DRAFT owner: Rulebook Expert -last_update: 2026-04-02 +last_update: 2026-04-03 --- # Validierungsregeln (ÖTO/FEI) @@ -148,29 +148,32 @@ fun validateFeiId(input: String): Boolean { --- -## 3. Lizenzklassen (R1–R4, RD1–RD3, LZF) +## 3. Lizenzklassen (R1–R4, RD1–RD3, LIZENZFREI) Status: Draft – basierend auf ÖTO‑Praxis und ZNS‑Lizenzdaten. Detaillierte Paragraphen‑Zitate werden nachgereicht (A‑2/A‑3 Arbeiten verknüpft). ### 3.1 Katalog gültiger Lizenzklassen - Reiten Springen (R‑Klassen): `R1`, `R2`, `R3`, `R4` - Dressur Reiten (RD‑Klassen): `RD1`, `RD2`, `RD3` -- Lizenzfrei/ohne Lizenz Kennzeichnung: `LZF` (für bewerbsbezogene Abteilung „ohne Lizenz“) +- Lizenzfrei/ohne Lizenz Kennzeichnung: `LIZENZFREI` (Enum-Key in `LizenzKlasseE`; Label: „ohne Lizenz“) Erweiterbarkeit: Weitere Spezial‑/Jugend‑ oder Fahrer‑Lizenzen können ergänzt werden, sobald in ÖTO/ZNS erforderlich. ### 3.2 Grundregeln der Zuordnung (vereinfachte Erstfassung) - Springen (CSN): - - Bewerbe bis inkl. 95 cm: Teilnahme mit `LZF` (Abt. „ohne Lizenz“) oder `R1` (Abt. „mit Lizenz`). + - Bewerbe bis inkl. 95 cm: Teilnahme mit `LIZENZFREI` (Abt. „ohne Lizenz“) oder `R1` (Abt. „mit Lizenz`). - Ab 100 cm: mindestens `R1` erforderlich; ab bestimmten Höhen empfohlen/erforderlich `R2+` (veranstalter‑/ausschreibungsabhängig). - Zwangsteilungsregeln siehe Roadmap A‑2 (eigener Abschnitt). - Dressur (CDN): - - Einsteigerprüfungen (z. B. Dressurreiterprüfungen niedrig): `LZF` oder `RD1`. + - Einsteigerprüfungen (z. B. Dressurreiterprüfungen niedrig): `LIZENZFREI` oder `RD1`. - Ab definiertem Schwierigkeitsgrad: `RD1+`, höhere Klassen `RD2/RD3` gemäß Ausschreibung. -Hinweis: Die exakte Matrix „Lizenzklasse × Bewerbsklasse (Disziplin, Höhe/Schwierigkeit)“ wird als Tabelle hinterlegt und aus ÖTO‑Paragraphen abgeleitet. Nach Bestätigung durch Fachreferat wird diese Spezifikation von „Draft“ auf „Stable“ gehoben. +Hinweis: Die vollständige Matrix „Lizenzklasse × Bewerbsklasse“ ist in +`docs/03_Domain/02_Reference/OETO_Regelwerk/B2-Backend-Uebergabe-Regulation-as-Data.md` (Abschnitte 4 und 5) +spezifiziert und als Flyway V009 im Backend hinterlegt. Nach Fachfreigabe wird der Status auf STABLE angehoben. ### 3.3 Validierungslogik (Platzhalter bis zur finalen Matrix) -- Eingabe muss in obiger Katalogliste vorkommen (`R1|R2|R3|R4|RD1|RD2|RD3|LZF`). + +- Eingabe muss in obiger Katalogliste vorkommen (`R1|R2|R3|R4|RD1|RD2|RD3|LIZENZFREI`). - Bei Auswahl eines Bewerbs wird die erlaubte(n) Lizenzklasse(n) aus der Disziplin/Höhe/Schwierigkeit abgeleitet. - Fehler, wenn gewählte Lizenzklasse nicht in der erlaubten Menge liegt. @@ -194,24 +197,24 @@ fun isLicenseAllowed(discipline: Discipline, heightCm: Int?, testLevel: Dressage ### 3.6 Lizenz‑Zuordnungstabelle (DRAFT, final mit Paragraphen‑Verweisen) - Springen (CSN) — Bezug ÖTO § 231 ff. (finale Paragraphennummern nachreichen): -| Höhe (cm) | Zulässige Lizenz-Abteilungen | Primär-Bezug ÖTO | -|---|---|---| -| ≤ 95 | LZF „ohne Lizenz“ | § 231 (Zwangsteilung Einsteiger) | -| ≤ 95 | R1 „mit Lizenz“ | § 231 | -| 100 | R1+ | § 231 | -| 105–110 | R1, R2+ (Empf. R2) | § 231 | -| 115–120 | R2+ | § 231 | -| 125–135 | R3+ | § 231 | -| ≥ 140 | R4 | § 231 | +| Höhe (cm) | Zulässige Lizenz-Abteilungen | Primär-Bezug ÖTO | +|-----------|------------------------------|----------------------------------| +| ≤ 95 | LIZENZFREI „ohne Lizenz“ | § 231 (Zwangsteilung Einsteiger) | +| ≤ 95 | R1 „mit Lizenz“ | § 231 | +| 100 | R1+ | § 231 | +| 105–110 | R1, R2+ (Empf. R2) | § 231 | +| 115–120 | R2+ | § 231 | +| 125–135 | R3+ | § 231 | +| ≥ 140 | R4 | § 231 | - Dressur (CDN) — Bezug ÖTO § 103 ff. (finale Paragraphennummern nachreichen): -| Prüfungsniveau (national, äquiv.) | Zulässige Lizenzen | Primär-Bezug ÖTO | -|---|---|---| -| Einsteiger/Dressurreiter (niedrig) | LZF, RD1 | § 103 | -| A/L | RD1+ | § 103 | -| LM/M | RD2+ | § 103 | -| S | RD3 | § 103 | +| Prüfungsniveau (national, äquiv.) | Zulässige Lizenzen | Primär-Bezug ÖTO | +|------------------------------------|--------------------|------------------| +| Einsteiger/Dressurreiter (niedrig) | LIZENZFREI, RD1 | § 103 | +| A/L | RD1+ | § 103 | +| LM/M | RD2+ | § 103 | +| S | RD3 | § 103 | Hinweise: - Veranstalter/Ausschreibung kann engere Anforderungen definieren, jedoch nicht lockern. @@ -294,4 +297,4 @@ Hinweis: Exakte FEI‑Tabellen sind pro Disziplinregelwerk verbindlich zu übern Meta: - status: DRAFT (wird auf STABLE angehoben nach Fachfreigabe) -- version: 0.3 (2026‑04‑02) +- version: 0.4 (2026‑04‑03) diff --git a/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md b/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md index e87ae5d8..5a4052f0 100644 --- a/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md +++ b/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md @@ -1,6 +1,6 @@ # 📜 [ÖTO/FEI Rulebook Expert] — Zwischenstand & Roadmap -> **Stand:** 3. April 2026 +> **Stand:** 3. April 2026 (Session 2) > **Rolle:** Regelwerks-Wächter, Validierungs-Spezialist, Compliance (ÖTO, FEI) --- @@ -32,12 +32,18 @@ --- -## 🔴 Sprint B — Offen (höchste Priorität) +## 🟡 Sprint B — Teilweise offen - [ ] **B-2** | Validierungs-Implementierung Backend begleiten - [x] FEI Legacy→Numeric Resolver implementiert (`/api/fei/resolve/{id}`) — erste Version in Masterdata-SCS - - [ ] Lizenz-/Altersmatrix als Regulation-as-Data an 👷 Backend übergeben + - [x] Lizenz-/Altersmatrix als Regulation-as-Data an 👷 Backend übergeben + - [x] `LizenzKlasseE`-Enum: `R4` ergänzt, `RD4`-Fehler in V008 korrigiert + - [x] Flyway V009: `license_height_matrix` + `horse_min_age_matrix` angelegt und befüllt + - [x] B-2-Übergabe-Spezifikation → + `docs/03_Domain/02_Reference/OETO_Regelwerk/B2-Backend-Uebergabe-Regulation-as-Data.md` + - [x] `Validierungsregeln.md`: `LZF` → `LIZENZFREI` korrigiert, Version 0.4, Verweis auf B2-Spec ergänzt - [ ] Serverseitige Validierung prüfen: Werden alle Regeln korrekt durchgesetzt? + - [ ] Backend-Endpunkte `/api/regulation/*` implementieren (👷 Backend) - [ ] Abweichungen Backend ↔ Frontend-Validierung dokumentieren und klären - [ ] Lizenz×Bewerb-Tabellen (Springen + Dressur) von DRAFT auf STABLE anheben (nach Fachfreigabe) diff --git a/docs/99_Journal/2026-04-03_Rulebook_B2_Regulation-as-Data_Backend.md b/docs/99_Journal/2026-04-03_Rulebook_B2_Regulation-as-Data_Backend.md new file mode 100644 index 00000000..445ac063 --- /dev/null +++ b/docs/99_Journal/2026-04-03_Rulebook_B2_Regulation-as-Data_Backend.md @@ -0,0 +1,79 @@ +--- +date: 2026-04-03 +sprint: B-2 +agent: Rulebook Expert +status: TEILWEISE ABGESCHLOSSEN +--- + +# Session-Log: B-2 Regulation-as-Data Backend-Übergabe + +## Ziel der Session + +Lizenz-/Altersmatrix als Regulation-as-Data an 👷 Backend übergeben; Lizenz×Bewerb-Tabellen für Fachfreigabe vorbereiten. + +--- + +## Durchgeführte Arbeiten + +### 1. Enum-Korrektur: `LizenzKlasseE` (core-domain) + +- **Problem:** `R4` fehlte im Enum, obwohl Flyway V008 bereits `R4` inserierte → Mismatch. +- **Fix:** `R4` in `core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt` ergänzt. +- **Vollständiger Katalog jetzt:** `LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3, JN, JG, YR` + +### 2. Flyway V008 korrigiert + +- **Problem:** `RD4` in Dressur-Lizenz-Matrix inseriert — existiert nicht im ÖTO-Regelwerk und nicht im Enum. +- **Fix:** `RD4`-Zeile entfernt; Kommentar mit Enum-Key-Konvention ergänzt. +- **Datei:** + `backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql` + +### 3. Flyway V009 neu angelegt + +Zwei neue Tabellen als Regulation-as-Data: + +| Tabelle | Inhalt | +|-------------------------|-----------------------------------------------------------------------------| +| `license_height_matrix` | Höhenbereich (cm) × erlaubte Lizenzklassen (Springen, ÖTO § 231) | +| `horse_min_age_matrix` | Mindestalter Pferd je Sparte + Höhe/Niveau (national ÖTO + FEI GR Art. 136) | + +- **Datei:** + `backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql` + +### 4. B-2-Übergabe-Spezifikation erstellt + +- Vollständige Spezifikation für 👷 Backend mit: Enum-Katalog, DB-Tabellen, Abfrage-Patterns, Pseudocode, + REST-Endpunkt-Empfehlungen, Abweichungen Backend↔Frontend, Fachfreigabe-Checkliste. +- **Datei:** `docs/03_Domain/02_Reference/OETO_Regelwerk/B2-Backend-Uebergabe-Regulation-as-Data.md` + +### 5. `Validierungsregeln.md` aktualisiert (v0.3 → v0.4) + +- `LZF` → `LIZENZFREI` in allen Vorkommen korrigiert (Enum ist SSoT). +- Verweis auf B2-Übergabe-Spezifikation ergänzt. +- Version auf 0.4 (2026-04-03) angehoben. + +### 6. Roadmap aktualisiert + +- Sprint B von 🔴 auf 🟡 gesetzt (Übergabe abgeschlossen, Fachfreigabe + Backend-Impl. offen). +- Abgeschlossene Teilaufgaben abgehakt. + +--- + +## Offene Punkte (Übergabe an nächste Session) + +| # | Aufgabe | An wen | Priorität | +|---|-----------------------------------------------------------------------|--------------------|-----------| +| 1 | **Fachfreigabe** Lizenz×Bewerb-Tabellen beim ÖTO-Fachreferat einholen | 📜 Rulebook Expert | 🔴 | +| 2 | Backend-Endpunkte `/api/regulation/*` implementieren | 👷 Backend | 🔴 | +| 3 | Frontend `LZF`-Key im Code prüfen (nicht nur Doku) | 🎨 Frontend | 🟠 | +| 4 | Serverseitige Validierung prüfen (nach Backend-Impl.) | 📜 Rulebook Expert | 🟠 | +| 5 | `AltersklasseRechner` (C-1) spezifizieren und implementieren | 👷 + 📜 | 🟠 | + +--- + +## Entscheidungen & Erkenntnisse + +- **`LIZENZFREI` ist der kanonische Enum-Key** — nicht `LZF`. Alle Dokumente und Code müssen diesen Key verwenden. +- **`RD4` existiert nicht** im ÖTO-Regelwerk 2026. Höchste Dressur-Lizenz ist `RD3`. +- **Aufwärts-Kompatibilität** ist explizit modelliert: Höhere Lizenz darf immer in niedrigerer Klasse starten. +- **Fachfreigabe ist Voraussetzung** für STABLE-Status der Tabellen — bis dahin bleibt alles DRAFT.