- 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>
15 KiB
| type | status | owner | last_update |
|---|---|---|---|
| RULE_SPEC | DRAFT | Rulebook Expert | 2026-04-03 |
Validierungsregeln (ÖTO/FEI)
Ziel: Dieses Dokument definiert präzise, testbare Validierungsregeln als Grundlage für Frontend (Live‑Validation), Backend (serverseitige Validation) und QA (Testfälle). Änderungen erfolgen versioniert und mit Beispielen.
Quellen/Verweise:
- Roadmap:
docs/04_Agents/Roadmaps/Rulebook_Roadmap.md - Domänen‑Modell:
docs/03_Domain/01_Core_Model/Domain_Model_Veranstaltung_Turnier_Bewerb_Abteilung.md
1. OEPS‑Mitgliedsnummer (Austria)
Status: Draft – finale Bestätigung durch OEPS/Verbandsdokumente ausstehend. Regel ist bewusst konservativ und maschinenlesbar formuliert.
1.1 Zweck
- Eindeutige Identifikation von Reiter:innen/Vereinsmitgliedern des OEPS in Formularen, Nennungen und Sync‑Schnittstellen.
1.2 Kanonisches Format (Normalform)
- Zeichenraum: Großbuchstaben A–Z, Ziffern 0–9, Bindestrich
-. - Erlaubte Schreibweisen (Regex in PCRE‑Notation):
- Mit Präfix:
^OEPS-[0-9]{6,8}$ - Ohne Präfix:
^[0-9]{6,8}$
- Mit Präfix:
Erläuterungen:
- Länge der numerischen Komponente: 6 bis 8 Ziffern (Spielraum für Altdaten/neuere Nummernkreise). Engführen auf exakt 7 Ziffern ist möglich, sobald offiziell bestätigt.
- Präfix ist optional in der Eingabe, wird bei Speicherung normalisiert (siehe 1.5).
1.3 Verbote / Nicht erlaubt
- Leerzeichen innerhalb der Nummer (z. B.
123 4567). - Punkte, Schrägstriche, Unterstriche oder andere Sonderzeichen (
. / _ + * ? ! …). - Alphanumerische Suffixe oder Buchstaben in der Nummer (z. B.
123456A). - Falsches oder gemischtes Präfix (z. B.
OEPS:oderOeps-). - Führende Nullen sind erlaubt, zählen jedoch zur Gesamtlänge (z. B.
00123456).
1.4 Beispiele
-
Gültig:
123456765432100123456OEPS-1234567OEPS-00123456
-
Ungültig (mit Begründung):
12345— zu kurz (min. 6 Ziffern)123456789— zu lang (max. 8 Ziffern)12A4567— Buchstaben in der Nummer nicht erlaubtOEPS1234567— fehlender Bindestrich nach PräfixOEPS-12 34567— Leerzeichen nicht erlaubtoeps-1234567— falsche Groß/Kleinschreibung im Präfix (nurOEPS-zulässig)OEPS-1234.567— Punkt nicht erlaubt
1.5 Normalisierung (Speicherformat)
- Interne Normalform:
NNNNNNNN(nur Ziffern, links mit Nullen auf 8 Zeichen aufgefüllt), sofern Nummernlänge ≤ 8. - Eingaben mit Präfix werden gespeichert ohne Präfix, jedoch mit Metadatum
source_prefix = OEPS. - Beispiel:
OEPS-1234567→01234567(Normalform),source_prefix=OEPS.
Hinweise:
- Falls sich offiziell herausstellt, dass die Länge fix
7ist, wird die Normalisierung entsprechend angepasst (kein linksseitiges Auffüllen über 7 hinaus). Diese Änderung wäre eine MINOR Schema/Validation‑Version.
1.6 Pseudocode‑Validierung
fun validateOepsId(input: String): Boolean {
val trimmed = input.trim()
val withPrefix = Regex("^OEPS-[0-9]{6,8}$")
val plain = Regex("^[0-9]{6,8}$")
return withPrefix.matches(trimmed) || plain.matches(trimmed)
}
1.7 Fehlermeldungen (UX‑Texte)
- Kurz: "Ungültige OEPS‑Mitgliedsnummer. Erlaubt sind 6–8 Ziffern, optional mit Präfix 'OEPS-'."
- Lang: "Bitte eine gültige OEPS‑Mitgliedsnummer eingeben: 6–8 Ziffern (z. B. 1234567 oder OEPS-1234567). Keine Leerzeichen oder Sonderzeichen."
1.8 Offene Punkte / ToDo
- Verbandsbestätigung: Fixe Länge (6, 7 oder 8) und eventuelle Prüfziffer klären.
- Mapping Altsysteme: Enthalten historische Kartenformat‑Varianten? Falls ja, eigene Legacy‑Regex aufnehmen.
- QA: Testfälle für Grenzwerte (6/8 Ziffern), Präfix‑Varianten, Whitespace‑Trimmung.
2. FEI‑ID
Status: Draft – auf Basis FEI General Regulations (Art. 113–114) und vorhandener Systemdaten. Endgültige Bestätigung via FEI Lookup/API steht aus.
2.1 Zweck
- Eindeutige Identifikation international registrierter Athlet:innen und Pferde bei FEI‑relevanten Bewerben/Kategorien (CI/CIO/CSI/CDI/CCI etc.).
2.2 Gültige Formate (Eingabe)
- Primär (numerisch, aktuell üblich):
- Regex:
^[0-9]{7,8}$
Erläuterung: 7–8‑stellige numerische FEI‑IDs (z. B.10011469).
- Regex:
- Legacy/Referenzcode (in Legacy‑Daten sichtbar):
- Regex:
^[0-9]{3}[A-Z]{2}[0-9]{2}$
Beispiel:104FE22. Diese Codes werden akzeptiert, aber bei Speicherung nach Möglichkeit gegen die numerische FEI‑ID aufgelöst (siehe 2.5).
- Regex:
Nicht erlaubt:
- Leerzeichen, Trennzeichen, gemischte Schreibweisen mit Präfixen (z. B.
FEI-10011469), alphanumerische Mischformen außerhalb des obigen Legacy‑Musters.
2.3 Pflichtfelder‑Regel (Wann ist FEI‑ID erforderlich?)
- International (FEI‑Events: CI/CSI/CDI/CCI/CIO/CH/…):
- Athlet: FEI‑ID Pflicht.
- Pferd: FEI‑ID Pflicht (inkl. FEI‑Pass/Microchip gem. FEI‑Regeln, vgl. Art. 114, 137 FEI GR).
- National (ÖTO‑Events: CN/CSN/CDN/CCN):
- Athlet: FEI‑ID optional (nur wenn FEI‑registriert).
- Pferd: FEI‑ID optional (nur wenn FEI‑registriert).
- Ausnahme: Wenn eine nationale Prüfung als FEI‑qualifikationsrelevant ausgewiesen ist, kann FEI‑ID für Datenexporte empfohlen/erforderlich sein (Veranstalterhinweis).
Hinweis: Die konkrete Pflicht koppeln wir im System an das Feld „Turnierkategorie“ und Disziplin, konfigurierbar per Regel‑Set.
2.4 Beispiele
- Gültig:
10011469,10019075,10028445,104FE22(Legacy). - Ungültig:
FEI10011469(Präfix),10011 469(Leerzeichen),10A11469(Buchstabe in numerischem Format),104F-E22(Sonderzeichen).
2.5 Normalisierung (Speicherformat)
- Bevorzugtes Speicherformat: numerische FEI‑ID (
[0-9]{7,8}) als String ohne Trennzeichen. - Legacy‑Referenzcode wird – sofern möglich – vor Speicherung via Mapping/Lookup in numerische FEI‑ID überführt. Falls kein Mapping möglich, speichern als eingegeben plus
source_format = LEGACY_CODE.
2.6 Pseudocode‑Validierung
fun validateFeiId(input: String): Boolean {
val s = input.trim().uppercase()
val numeric = Regex("^[0-9]{7,8}$")
val legacy = Regex("^[0-9]{3}[A-Z]{2}[0-9]{2}$")
return numeric.matches(s) || legacy.matches(s)
}
2.7 Fehlermeldungen (UX‑Texte)
- Kurz: "Ungültige FEI‑ID. Erlaubt sind 7–8 Ziffern (z. B. 10011469)."
- Lang: "Bitte eine gültige FEI‑ID eingeben: 7–8 Ziffern (z. B. 10011469). Historische Referenzcodes (z. B. 104FE22) werden akzeptiert und – wenn möglich – automatisch aufgelöst."
2.8 Quellen/Verweise
- FEI General Regulations, insbesondere Art. 113 (Registration and Eligibility) und Art. 114 (Horse Identification) —
docs/03_Domain/02_Reference/FEI_Regelwerk/FEI-2026_General-Regulations_…md - Systembeispiele/Fixtures in Frontend‑Stores (FEI‑IDs):
frontend/shells/meldestelle-desktop/.../Stores.kt
2.9 Backend‑Lookup (Masterdata‑SCS)
- Endpoint:
GET /api/fei/resolve/{id}- Eingabe:
{id}numerisch (^[0-9]{7,8}$) oder Legacy‑Code (^[0-9]{3}[A-Z]{2}[0-9]{2}$). - Erfolg 200:
{ input, normalizedNumericId, sourceFormat, wasMapped, found: true } - Nicht gefunden 404:
{ input, normalizedNumericId: null, sourceFormat: null, wasMapped: false, found: false }
- Eingabe:
- Mapping‑Quelle:
backend/services/masterdata/masterdata-service/src/main/resources/data/fei-id-mapping.json(kann später aus DB gespeist werden).
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:
LIZENZFREI(Enum-Key inLizenzKlasseE; 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
LIZENZFREI(Abt. „ohne Lizenz“) oderR1(Abt. „mit Lizenz`). - Ab 100 cm: mindestens
R1erforderlich; ab bestimmten Höhen empfohlen/erforderlichR2+(veranstalter‑/ausschreibungsabhängig). - Zwangsteilungsregeln siehe Roadmap A‑2 (eigener Abschnitt).
- Bewerbe bis inkl. 95 cm: Teilnahme mit
- Dressur (CDN):
- Einsteigerprüfungen (z. B. Dressurreiterprüfungen niedrig):
LIZENZFREIoderRD1. - Ab definiertem Schwierigkeitsgrad:
RD1+, höhere KlassenRD2/RD3gemäß Ausschreibung.
- Einsteigerprüfungen (z. B. Dressurreiterprüfungen niedrig):
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|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.
Pseudocode (vereinfacht):
fun isLicenseAllowed(discipline: Discipline, heightCm: Int?, testLevel: DressageLevel?, license: String): Boolean {
val allowed = allowedLicensesFor(discipline, heightCm, testLevel) // Tabelle/Regel-Engine
return license in allowed
}
3.4 Fehlermeldungen (UX‑Texte)
- Kurz: "Diese Lizenzklasse ist für den ausgewählten Bewerb nicht zugelassen."
- Lang: "Bitte eine für diesen Bewerb zugelassene Lizenz auswählen. Die Zulassung richtet sich nach Disziplin und Höhe/Schwierigkeitsgrad (ÖTO)."
3.5 Quellen/Verweise
- ÖTO (Abschnitte zu Lizenzen, Springen/Dressur Teilnahmevoraussetzungen)
- ZNS‑Lizenzdaten:
docs/OePS/ZNS/LIZENZ01.dat(Datenquelle, strukturierter Export) – Parsing/Anlage in Masterdata‑SCS. - Teilungs-/Warnlogik:
docs/03_Domain/02_Reference/OETO_Regelwerk/Warn-Logik-Spezifikation-competition-context.md
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 | 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) | LIZENZFREI, RD1 | § 103 |
| A/L | RD1+ | § 103 |
| LM/M | RD2+ | § 103 |
| S | RD3 | § 103 |
Hinweise:
- Veranstalter/Ausschreibung kann engere Anforderungen definieren, jedoch nicht lockern.
- Zwangsteilungsregeln für CSN‑C‑NEU sind in A‑2 separat spezifiziert und ergänzen die obige Tabelle.
4. Altersklassen Pferd
Status: Draft – FEI/ÖTO konsolidiert; Detailtabellen pro Disziplin werden ergänzt.
4.1 Stichtagsregel (Altersberechnung)
- Das Pferdealter wird für das gesamte Kalenderjahr mit Stichtag 1. Jänner bestimmt (Jahrgangsregel).
Beispiel: Geburtsdatum 15.06.2020 → Alter 2026 = 6 (ab 01.01.2026).
Pseudocode:
fun horseAgeOnJan1(birthYear: Int, year: Int): Int = year - birthYear
4.2 Mindestalter – Grundregeln (Erstfassung, Disziplin-übergreifend)
- National (ÖTO, typische Praxis):
- Springen bis 100 cm: min. 4 Jahre
- Springen > 100 cm bis 120 cm: min. 5 Jahre
- Springen > 120 cm: min. 6 Jahre (Empfehlung/abhängig von Klasse)
- Dressur Einstieg/leichte Prüfungen: min. 4 Jahre
- Dressur höhere Klassen (z. B. L/M/S‑ähnlich): min. 5–6 Jahre (konkret per Tabelle nachzureichen)
- International (FEI, vgl. Art. 136 GR):
- Disziplinspezifische Mindestalter (werden tabellarisch hinterlegt; Abhängig von Disziplin/Testlevel/Star‑Rating).
Hinweis: Konkrete, rechtssichere Tabellen (Disziplin × Klasse/Höhe × Mindestalter) werden nach Paragraphen‑Sichtung ergänzt und in Masterdata‑SCS versioniert.
4.3 Validierungslogik
- Errechne
age = horseAgeOnJan1(geburtsjahr, veranstaltungsjahr). - Prüfe
age >= minAgeFor(discipline, heightCm?, testLevel?)laut Matrix. - Fehler, wenn Bedingung nicht erfüllt.
Beispiel‑Fehlertext:
- Kurz: "Pferd ist für diesen Bewerb zu jung."
- Lang: "Das Mindestalter für diesen Bewerb ist {X} Jahre (Stichtag 1. Jänner). Dieses Pferd gilt im aktuellen Jahr als {Y} Jahre alt."
4.4 Quellen/Verweise
- FEI General Regulations, Art. 136 (Age of Horses)
- ÖTO (disziplinspezifische Mindestalter nationaler Bewerbe)
4.5 Mindestalter‑Tabellen (DRAFT; Paragraphen‑Verweise finalisieren)
- Springen (national, ÖTO; Bezug § 231, Pferdealter allgemeine Bestimmungen):
| Höhe (cm) | Mindestalter Pferd (Jahre, Stichtag 1.1.) |
|---|---|
| ≤ 100 | 4 |
| 105–120 | 5 |
| ≥ 125 | 6 |
- Dressur (national, ÖTO; Bezug § 103, Pferdealter):
| Prüfungsniveau | Mindestalter Pferd |
|---|---|
| Einsteiger/Dressurreiter (niedrig) | 4 |
| A/L | 4 |
| LM/M | 5 |
| S | 6 |
- International (FEI, GR Art. 136 + Disziplinspezifische Regeln, exemplarisch):
| Disziplin | Prüfungs-/Star‑Level | Mindestalter |
|---|---|---|
| Jumping | 1*–2* | 6 |
| Jumping | 3*–5* | 7 |
| Dressage | CDI‑YH (Young Horses) | gem. FEI YH‑Regeln |
| Dressage | CDI (Senior) | 7 |
Hinweis: Exakte FEI‑Tabellen sind pro Disziplinregelwerk verbindlich zu übernehmen; hier nur Platzhalter bis Paragraphen‑Finalisierung.
5. Offene Punkte & Nächste Schritte
- Lizenz‑Zuordnungstabelle (Springen/Dressur) mit Paragraphen‑Verweisen finalisieren und hier einpflegen. (Status: DRAFT Tabellen vorhanden)
- Mindestalter‑Tabellen je Disziplin und Klasse/Höhe aus ÖTO & FEI präzise ergänzen. (Status: DRAFT Tabellen vorhanden)
- FEI‑Legacy‑Code → numerische ID Mappings in Masterdata‑SCS verankern; Backend‑Lookup implementieren. (Status: erste Version implementiert, JSON‑Mapping, REST‑Endpoint)
Meta:
- status: DRAFT (wird auf STABLE angehoben nach Fachfreigabe)
- version: 0.4 (2026‑04‑03)