--- type: Specification status: ACTIVE owner: ÖTO/FEI Rulebook Expert last_update: 2026-03-24 related: - Abteilungs-Trennungs-Schwellenwerte.md - docs/01_Architecture/adr/0016-api-design-acl-de.md --- # Warn-Logik-Spezifikation: `competition-context` – Abteilungs-Schwellenwerte 📜 **[ÖTO/FEI Rulebook Expert]** | 24. März 2026 Dieses Dokument ist die **verbindliche Implementierungs-Spezifikation** für die Warn-Logik im `competition-context` bezüglich Abteilungs-Schwellenwerte. Es baut auf der [Schwellenwert-Referenz](./Abteilungs-Trennungs-Schwellenwerte.md) auf und definiert präzise, **wann**, **was** und **wie** gewarnt wird. > ⚠️ **Grundprinzip (ADR-0007):** Das System gibt **niemals** harte Fehler bei > Schwellenwert-Überschreitungen. Jede Warnung ist **overridebar** per Override-Event > (TBA-Entscheidung). Warnungen werden gespeichert und sind auditierbar. --- ## 1. Warn-Typen: Übersicht | Warn-Code | Typ | Auslöser | Betrifft | |--------------------------------------------|-----------------------|--------------------------------------------------------------------------|------------------------------------| | `WARN_PFLICHT_TEILUNG_UEBERSCHRITTEN` | Starter-Schwellenwert | Starterzahl > Pflicht-Schwellenwert | `DomBewerb` | | `WARN_KANN_TEILUNG_EMPFOHLEN` | Starter-Schwellenwert | Starterzahl > Kann-Schwellenwert, keine Teilung konfiguriert | `DomBewerb` | | `WARN_ABTEILUNG_ZU_GROSS` | Abteilungs-Limit | Abteilung nach Teilung > 80 Starter | `DomAbteilung` | | `WARN_ABTEILUNG_MAX_UEBERSCHRITTEN` | Konfigurations-Limit | Starter > konfiguriertes `maxStarter`-Limit | `DomAbteilung` | | `WARN_STRUKTURELLE_TEILUNG_FEHLT` | Strukturelle Pflicht | Vorgeschriebene Abteilungs-Struktur nicht vorhanden | `DomBewerb` + `List` | | `WARN_STRUKTURELLE_TEILUNG_UNVOLLSTAENDIG` | Strukturelle Pflicht | Abteilungs-Struktur vorhanden, aber Teilnehmerkreis falsch/unvollständig | `DomBewerb` + `List` | --- ## 2. Warn-Typ 1: Starter-Schwellenwerte (`DomBewerb`) ### 2.1 Pflicht-Teilung überschritten **Auslöser:** `DomBewerb.validateAbteilungsSchwellenwerte(aktuelleStarterAnzahl)` | Bedingung | Warn-Code | Schwellenwert | |----------------------------------------------------------------------------------------------------------------------|---------------------------------------|---------------| | `pruefungsTyp` ∈ {`STIL_SPRINGEN`, `SPRINGPFERDE`, `DRESSURPFERDE`} UND `starterAnzahl > 30` UND `!istMeisterschaft` | `WARN_PFLICHT_TEILUNG_UEBERSCHRITTEN` | 30 | | `pruefungsTyp == VIELSEITIGKEIT` UND `starterAnzahl > 40` UND `!istMeisterschaft` | `WARN_PFLICHT_TEILUNG_UEBERSCHRITTEN` | 40 | | `pruefungsTyp == SPRINGEN_UEBRIG` UND `starterAnzahl > 80` UND `!istMeisterschaft` | `WARN_PFLICHT_TEILUNG_UEBERSCHRITTEN` | 80 | **Warn-Nachricht (Format):** ``` WARN_PFLICHT_TEILUNG_UEBERSCHRITTEN: Bewerb: [bewerbNummer] – [bezeichnung] Prüfungstyp: [pruefungsTyp] Starter: [N] > Schwellenwert [S] Empfehlung: Teilung nach [teilungsTyp] (Standard: NACH_LIZENZ) Referenz: ÖTO § 39 Abs. 2 Override möglich (TBA-Entscheidung erforderlich) ``` ### 2.2 Kann-Teilung empfohlen **Auslöser:** `DomBewerb.validateAbteilungsSchwellenwerte(aktuelleStarterAnzahl)` | Bedingung | Warn-Code | Schwellenwert | |-------------------------------------------------------------------------------------------------------|-------------------------------|---------------| | `pruefungsTyp == DRESSUR` UND `starterAnzahl > 30` UND `teilungsTyp == KEINE` UND `!istMeisterschaft` | `WARN_KANN_TEILUNG_EMPFOHLEN` | 30 | **Warn-Nachricht (Format):** ``` WARN_KANN_TEILUNG_EMPFOHLEN: Bewerb: [bewerbNummer] – [bezeichnung] Prüfungstyp: DRESSUR Starter: [N] > 30 Empfehlung: Kann-Teilung nach NACH_LIZENZ möglich (§ 39 Abs. 2) Override möglich (TBA-Entscheidung) ``` --- ## 3. Warn-Typ 2: Abteilungs-Größe nach Teilung (`DomAbteilung`) ### 3.1 Abteilung nach Teilung zu groß **Auslöser:** `DomAbteilung.validateStarterLimit()` | Bedingung | Warn-Code | |----------------------|---------------------------| | `starterAnzahl > 80` | `WARN_ABTEILUNG_ZU_GROSS` | **Warn-Nachricht (Format):** ``` WARN_ABTEILUNG_ZU_GROSS: Abteilung: [abteilungsNummer] – [bezeichnung] Starter: [N] > 80 Erneute Teilung verpflichtend (§ 39 Abs. 2) Override möglich (TBA-Entscheidung erforderlich) ``` ### 3.2 Konfiguriertes Starter-Limit überschritten **Auslöser:** `DomAbteilung.validateStarterLimit()` | Bedingung | Warn-Code | |---------------------------------------------------|-------------------------------------| | `maxStarter > 0` UND `starterAnzahl > maxStarter` | `WARN_ABTEILUNG_MAX_UEBERSCHRITTEN` | **Warn-Nachricht (Format):** ``` WARN_ABTEILUNG_MAX_UEBERSCHRITTEN: Abteilung: [abteilungsNummer] – [bezeichnung] Starter: [N] > Limit [maxStarter] Override möglich (TBA-Entscheidung erforderlich) ``` --- ## 4. Warn-Typ 3: Strukturelle Pflicht-Teilungen (`DomBewerb` + `List`) Strukturelle Teilungen sind **unabhängig von der Starterzahl** verpflichtend. Sie werden durch `DomBewerb.validateStrukturellesTeilung(abteilungen)` geprüft. ### 4.1 Entscheidungsbaum: Wann greift welche strukturelle Prüfung? ``` DomBewerb ├── sparte == SPRINGEN (CSN) │ ├── pruefungsTyp == STIL_SPRINGEN UND hoeheCm <= 95 │ │ → Prüfung: LIZENZ_OHNE_VS_R1 (§ 200 Abs. 5.3) │ ├── pruefungsTyp == SPRINGPFERDE UND hoeheCm IN [95..110] │ │ → Prüfung: PFERDEALTER_4_VS_5_6 (§ 200 Abs. 6) │ └── turnierkategorie == C_NEU │ ├── hoeheCm <= 95 │ │ → Prüfung: C_NEU_OHNE_VS_MIT_LIZENZ (§ 231) │ └── hoeheCm >= 100 │ → Prüfung: C_NEU_R1_VS_R2PLUS (§ 231) │ ├── sparte == VIELSEITIGKEIT (CCN) │ ├── turnierkategorie == C_NEU │ │ ├── hoeheCm <= 80 │ │ │ → Prüfung: CCN_C_NEU_3_ABT (§ 300 C-NEU) │ │ └── hoeheCm >= 90 │ │ → Prüfung: CCN_C_NEU_2_ABT (§ 300 C-NEU) │ └── pruefungsTyp == VIELSEITIGKEIT UND bezeichnung enthält "Welcome" ODER hoeheCm == 80 │ → Prüfung: CCN_WELCOME_80_R2PLUS_EIGENE_ABT (§ 301 Abs. 1.4) │ ├── sparte == DRESSUR (CDN) │ └── pruefungsTyp == DRESSURPFERDE UND hoeheCm == null (Klasse A, 4–6-jährig) │ → Prüfung: PFERDEALTER_4_VS_5_6 (§ 100 Abs. 5) │ ├── pruefungsTyp == CAPRILLI │ → Prüfung: CAPRILLI_LIZENSFREI_VS_RD1PLUS (§ 803 Abs. 2) │ └── sparte == FAHREN (CAN) UND pruefungsTyp == FAHREN → Prüfung: FAHREN_F1PLUS_EIGENE_ABT (§ 850 Abs. 9) ``` ### 4.2 Strukturelle Prüfungen im Detail #### LIZENZ_OHNE_VS_R1 – CSN Stil-/Idealzeitspringen bis 95 cm **Regel:** Mindestens 2 Abteilungen: Abt. ohne Lizenz + Abt. R1. Unabhängig von Starterzahl. | Prüfung | Bedingung für `WARN_STRUKTURELLE_TEILUNG_FEHLT` | |-------------------------------|-------------------------------------------------------------------| | Abt. „ohne Lizenz" vorhanden? | Keine Abteilung mit `teilnehmerkreisBeschreibung` ~ „ohne Lizenz" | | Abt. „R1" vorhanden? | Keine Abteilung mit `teilnehmerkreisBeschreibung` ~ „R1" | ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CSN, Prüfungstyp: STIL_SPRINGEN, Höhe: ≤ 95 cm Fehlende Abteilung(en): [ohne Lizenz / R1] Referenz: ÖTO B-Teil § 200 Abs. 5.3 Override möglich (TBA-Entscheidung erforderlich) ``` #### PFERDEALTER_4_VS_5_6 – Springpferdeprüfung 95–110 cm / Dressurpferdeprüfung Klasse A **Regel:** 4-jährige in eigener Abteilung, getrennt von 5–6-jährigen. | Prüfung | Bedingung für `WARN_STRUKTURELLE_TEILUNG_FEHLT` | |-------------------------------|-------------------------------------------------------------------------| | Abt. „4-jährige" vorhanden? | Keine Abteilung mit `teilnehmerkreisBeschreibung` ~ „4-jährig" | | Abt. „5–6-jährige" vorhanden? | Keine Abteilung mit `teilnehmerkreisBeschreibung` ~ „5" oder „6-jährig" | ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: [CSN/CDN], Prüfungstyp: [SPRINGPFERDE/DRESSURPFERDE], Höhe: [X] cm Fehlende Abteilung(en): [4-jährige / 5–6-jährige] Referenz: ÖTO B-Teil § 200 Abs. 6 / § 100 Abs. 5 Override möglich (TBA-Entscheidung erforderlich) ``` #### C_NEU_OHNE_VS_MIT_LIZENZ – CSN-C-NEU bis 95 cm **Regel:** Abt. 1 = ohne Lizenz, Abt. 2 = mit Lizenz. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CSN, Kategorie: C-NEU, Höhe: ≤ 95 cm Fehlende Abteilung(en): [ohne Lizenz / mit Lizenz] Referenz: ÖTO B-Teil § 231 Override möglich (TBA-Entscheidung erforderlich) ``` #### C_NEU_R1_VS_R2PLUS – CSN-C-NEU ab 100 cm **Regel:** Abt. 1 = R1, Abt. 2 = R2 und höher. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CSN, Kategorie: C-NEU, Höhe: ≥ 100 cm Fehlende Abteilung(en): [R1 / R2+] Referenz: ÖTO B-Teil § 231 Override möglich (TBA-Entscheidung erforderlich) ``` #### CCN_C_NEU_3_ABT – CCN-C-NEU Gelände bis 80 cm **Regel:** 3 Abteilungen: ohne Lizenz / R1 / R2+. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CCN, Kategorie: C-NEU, Höhe: ≤ 80 cm Fehlende Abteilung(en): [ohne Lizenz / R1 / R2+] Referenz: ÖTO B-Teil § 300 (C-NEU) Override möglich (TBA-Entscheidung erforderlich) ``` #### CCN_C_NEU_2_ABT – CCN-C-NEU Gelände ab 90 cm **Regel:** 2 Abteilungen: ohne Lizenz / R1+. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CCN, Kategorie: C-NEU, Höhe: ≥ 90 cm Fehlende Abteilung(en): [ohne Lizenz / R1+] Referenz: ÖTO B-Teil § 300 (C-NEU) Override möglich (TBA-Entscheidung erforderlich) ``` #### CCN_WELCOME_80_R2PLUS_EIGENE_ABT – CCN Welcome / 80 cm **Regel:** R2+ Reiter müssen in eigener Abteilung gewertet werden. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CCN, Klasse: Welcome / 80 cm R2+-Reiter ohne eigene Abteilung Referenz: ÖTO B-Teil § 301 Abs. 1.4 Override möglich (TBA-Entscheidung erforderlich) ``` #### CAPRILLI_LIZENSFREI_VS_RD1PLUS – Caprilli (§ 803) **Regel:** Mindestens 2 Abteilungen: lizenzfrei / RD1 und höher. Unabhängig von Starterzahl. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Prüfungstyp: CAPRILLI Fehlende Abteilung(en): [lizenzfrei / RD1+] Referenz: ÖTO B-Teil § 803 Abs. 2 Override möglich (TBA-Entscheidung erforderlich) ``` #### FAHREN_F1PLUS_EIGENE_ABT – Fahrertreffen (§ 850) **Regel:** Fahrer mit Lizenz höher als F1 in eigener Abteilung. ``` WARN_STRUKTURELLE_TEILUNG_FEHLT: Bewerb: [bewerbNummer] – [bezeichnung] Sparte: CAN, Prüfungstyp: FAHREN F1+-Fahrer ohne eigene Abteilung Referenz: ÖTO B-Teil § 850 Abs. 9 Override möglich (TBA-Entscheidung erforderlich) ``` --- ## 5. Implementierungs-Vorgaben ### 5.1 Methoden-Signaturen (Kotlin) ```kotlin // In DomBewerb – bereits vorhanden, vollständig implementieren: fun validateAbteilungsSchwellenwerte(aktuelleStarterAnzahl: Int): List // In DomBewerb – NEU zu implementieren: fun validateStrukturellesTeilung(abteilungen: List): List // In DomAbteilung – bereits vorhanden, vollständig implementieren: fun validateStarterLimit(): List ``` ### 5.2 `AbteilungsWarnung` – Value Object Statt roher Strings soll ein typisiertes Value Object verwendet werden: ```kotlin @Serializable data class AbteilungsWarnung( val code: AbteilungsWarnungCodeE, // Maschinenlesbarer Warn-Code val bewerbId: Uuid, // Betroffener Bewerb val abteilungId: Uuid? = null, // Betroffene Abteilung (wenn relevant) val nachricht: String, // Menschenlesbare Beschreibung val oetoParagraph: String, // z.B. "§ 39 Abs. 2" val istOverridebar: Boolean = true, // Immer true (ADR-0007) val timestamp: Instant = Clock.System.now() ) enum class AbteilungsWarnungCodeE { WARN_PFLICHT_TEILUNG_UEBERSCHRITTEN, WARN_KANN_TEILUNG_EMPFOHLEN, WARN_ABTEILUNG_ZU_GROSS, WARN_ABTEILUNG_MAX_UEBERSCHRITTEN, WARN_STRUKTURELLE_TEILUNG_FEHLT, WARN_STRUKTURELLE_TEILUNG_UNVOLLSTAENDIG } ``` ### 5.3 Override-Event Wenn der TBA eine Warnung bestätigt/überschreibt, wird ein `AbteilungsWarnungOverrideEvent` gespeichert: ```kotlin @Serializable data class AbteilungsWarnungOverrideEvent( val overrideId: Uuid = Uuid.random(), val warnungCode: AbteilungsWarnungCodeE, val bewerbId: Uuid, val abteilungId: Uuid? = null, val begruendung: String, // Pflichtfeld – TBA muss Begründung angeben val tbaUserId: Uuid, val timestamp: Instant = Clock.System.now() ) ``` ### 5.4 Konfigurierbare Schwellenwerte Die Schwellenwerte sind **nicht hard-coded**, sondern über `AbteilungsSchwellenwertConfig` konfigurierbar: ```kotlin data class AbteilungsSchwellenwertConfig( val stilSpringpferdPflicht: Int = 30, // § 39 Abs. 2 val vielseitigkeitPflicht: Int = 40, // § 39 Abs. 2 val springenUebrigPflicht: Int = 80, // § 39 Abs. 2 val dressurKann: Int = 30, // § 39 Abs. 2 val abteilungMaxNachTeilung: Int = 80 // § 39 Abs. 2 ) ``` ### 5.5 Aufruf-Zeitpunkte (Trigger) | Ereignis | Aufgerufene Validierung | |-------------------------------------------------------------|----------------------------------------------------------| | Neue Nennung wird einem Bewerb zugeordnet | `DomBewerb.validateAbteilungsSchwellenwerte(neueAnzahl)` | | Abteilung wird erstellt oder geändert | `DomAbteilung.validateStarterLimit()` | | Bewerb wird gespeichert / Abteilungs-Konfiguration geändert | `DomBewerb.validateStrukturellesTeilung(abteilungen)` | | Startliste wird aus ENTWURF → VEROEFFENTLICHT überführt | Alle drei Validierungen als Gesamt-Check | --- ## 6. Ausnahmen (nicht warnen) | Bedingung | Begründung | |--------------------------------------------------------|-----------------------------------------------------| | `istMeisterschaft == true` | § 39 Abs. 4: Meisterschaftsbewerbe sind ausgenommen | | Bewerb mit Geldpreisen > Doppeltes der Gebührenordnung | § 39 Abs. 2: Ausnahme von Pflicht-Teilung | | `turnierkategorie` nicht in {A*, A, B*, B, C, C-NEU} | Schwellenwerte gelten nur für diese Kategorien | --- ## 7. Offene Fragen (Klärungsbedarf) | # | Frage | Status | |---|----------------------------------------------------------------------------------------------|----------| | 1 | Gelten § 39-Schwellenwerte auch für **Reitertreffen** (nicht nur Turniere)? | 🔍 Offen | | 2 | Pflicht-Teilung bei **kombinierten Turnieren** (CDN + CSN, § 4)? | 🔍 Offen | | 3 | **Voltigieren (CVN):** Eigene Abteilungs-Trennungsregeln? (§ 400 ff. nicht ausgewertet) | 🔍 Offen | | 4 | **Fahren (CAN):** Eigene Starter-Schwellenwerte jenseits der Reitertreffen-Regel? | 🔍 Offen | | 5 | Wie wird „Bewerb mit Geldpreisen > Doppeltes der Gebührenordnung" im Datenmodell abgebildet? | 🔍 Offen | --- *Erstellt: 2026-03-24 | Autor: ÖTO/FEI Rulebook Expert (Junie)* *Basiert auf: ÖTO 2026 A-Teil § 39, B-Teil §§ 100, 200, 231, 300, 301, 803, 850* *Implementierungs-Ziel: `competition-context` (PHASE 5)*