feat(db): add regulation-as-data tables for license height and horse age matrices

- Introduced `license_height_matrix` for mapping minimum license requirements to jump heights (ÖTO § 231).
- Added `horse_min_age_matrix` for defining minimum horse ages by discipline, height, or level (ÖTO § 103, FEI GR Art. 136).
- Populated both tables with ÖTO 2026 and FEI-compliant seed data.
- Updated Flyway V008 to remove incorrect `RD4` entry and annotated corrected enum references.
- Created Flyway V009 for introducing the new tables and their seeds.
- Aligned documentation, validation rules, and roadmaps for backend implementation handover.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
Stefan Mogeritsch 2026-04-03 11:10:42 +02:00
parent 1f9f528554
commit c696b8c50e
7 changed files with 572 additions and 28 deletions

View File

@ -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)

View File

@ -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=6095 cm, A=100110 cm, L=115120 cm, LM=125130 cm, M=135 cm, S=140160 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): 6095 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): 100110 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): 115120 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): 125130 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): 140160 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');

View File

@ -172,6 +172,8 @@ enum class LizenzKlasseE {
/** Reiter-Lizenz Klasse 3 */
R3,
/** Reiter-Lizenz Klasse 4 */
R4,
/** Dressur-Reiter Klasse 1 */
RD1,

View File

@ -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) | 6095 | LIZENZFREI | LIZENZFREI, R1, R2, R3, R4 | CSN-C-NEU: Zwangsteilung in Abt. „ohne Lizenz" + „mit Lizenz" |
| A (Anfänger) | 100110 | R1 | R1, R2, R3, R4 | Keine LIZENZFREI mehr ab 100 cm |
| L (Leicht) | 115120 | R1* | R1, R2, R3, R4 | *Ausschreibung kann R2 vorschreiben |
| LM (Leicht-Mittel) | 125130 | R2 | R2, R3, R4 | |
| M (Mittelschwer) | 135 | R3 | R3, R4 | |
| S (Schwer) | 140160 | 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) |
|-----------|----------------------------|
| 60100 | 4 |
| 101120 | 5 |
| 121999 | 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 |

View File

@ -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 (R1R4, RD1RD3, LZF)
## 3. Lizenzklassen (R1R4, RD1RD3, LIZENZFREI)
Status: Draft basierend auf ÖTOPraxis und ZNSLizenzdaten. Detaillierte ParagraphenZitate werden nachgereicht (A2/A3 Arbeiten verknüpft).
### 3.1 Katalog gültiger Lizenzklassen
- Reiten Springen (RKlassen): `R1`, `R2`, `R3`, `R4`
- Dressur Reiten (RDKlassen): `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 FahrerLizenzen 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 A2 (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 ÖTOParagraphen 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 LizenzZuordnungstabelle (DRAFT, final mit ParagraphenVerweisen)
- 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 |
| 105110 | R1, R2+ (Empf. R2) | § 231 |
| 115120 | R2+ | § 231 |
| 125135 | 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 |
| 105110 | R1, R2+ (Empf. R2) | § 231 |
| 115120 | R2+ | § 231 |
| 125135 | 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 FEITabellen sind pro Disziplinregelwerk verbindlich zu übern
Meta:
- status: DRAFT (wird auf STABLE angehoben nach Fachfreigabe)
- version: 0.3 (20260402)
- version: 0.4 (20260403)

View File

@ -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)

View File

@ -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.