- Added `meldestelle-desktop` module using JVM/Compose Desktop, registered in `settings.gradle.kts`. - Integrated new screens and desktop navigation into core: `Veranstaltungen`, `TurnierDetail`, etc. - Expanded backend with `ExposedFunktionaerRepository` in `officials-infrastructure`. - Completed ADRs for bounded context mapping (`ADR-0014`) and context map (`ADR-0015`). - Updated and extended project documentation with session logs and architecture decisions. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
16 KiB
| type | status | owner | last_update | related | ||
|---|---|---|---|---|---|---|
| Specification | ACTIVE | ÖTO/FEI Rulebook Expert | 2026-03-24 |
|
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 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<DomAbteilung> |
WARN_STRUKTURELLE_TEILUNG_UNVOLLSTAENDIG |
Strukturelle Pflicht | Abteilungs-Struktur vorhanden, aber Teilnehmerkreis falsch/unvollständig | DomBewerb + List<DomAbteilung> |
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<DomAbteilung>)
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)
// In DomBewerb – bereits vorhanden, vollständig implementieren:
fun validateAbteilungsSchwellenwerte(aktuelleStarterAnzahl: Int): List<AbteilungsWarnung>
// In DomBewerb – NEU zu implementieren:
fun validateStrukturellesTeilung(abteilungen: List<DomAbteilung>): List<AbteilungsWarnung>
// In DomAbteilung – bereits vorhanden, vollständig implementieren:
fun validateStarterLimit(): List<AbteilungsWarnung>
5.2 AbteilungsWarnung – Value Object
Statt roher Strings soll ein typisiertes Value Object verwendet werden:
@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:
@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:
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)