--- type: RULE_SPEC status: DRAFT owner: Rulebook Expert last_update: 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): 1) Mit Präfix: `^OEPS-[0-9]{6,8}$` 2) Ohne Präfix: `^[0-9]{6,8}$` 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:` oder `Oeps-`). - Führende Nullen sind erlaubt, zählen jedoch zur Gesamtlänge (z. B. `00123456`). ### 1.4 Beispiele - Gültig: - `123456` - `7654321` - `00123456` - `OEPS-1234567` - `OEPS-00123456` - Ungültig (mit Begründung): - `12345` — zu kurz (min. 6 Ziffern) - `123456789` — zu lang (max. 8 Ziffern) - `12A4567` — Buchstaben in der Nummer nicht erlaubt - `OEPS1234567` — fehlender Bindestrich nach Präfix - `OEPS-12 34567` — Leerzeichen nicht erlaubt - `oeps-1234567` — falsche Groß/Kleinschreibung im Präfix (nur `OEPS-` 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 `7` ist, 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 ```kotlin 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`). - 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). 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 ```kotlin 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 }` - 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 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 `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): `LIZENZFREI` oder `RD1`. - Ab definiertem Schwierigkeitsgrad: `RD1+`, höhere Klassen `RD2/RD3` gemäß Ausschreibung. 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): ```kotlin 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: ```kotlin 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)