meldestelle/docs/03_Domain/02_Reference/OETO_Regelwerk/Warn-Logik-Spezifikation-competition-context.md
Stefan Mogeritsch 354bd49de6 feat: integrate new desktop shell and extend backend & ADRs
- 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>
2026-03-24 18:22:15 +01:00

16 KiB
Raw Blame History

type status owner last_update related
Specification ACTIVE ÖTO/FEI Rulebook Expert 2026-03-24
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 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, 46-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 95110 cm / Dressurpferdeprüfung Klasse A

Regel: 4-jährige in eigener Abteilung, getrennt von 56-jährigen.

Prüfung Bedingung für WARN_STRUKTURELLE_TEILUNG_FEHLT
Abt. „4-jährige" vorhanden? Keine Abteilung mit teilnehmerkreisBeschreibung ~ „4-jährig"
Abt. „56-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 / 56-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)