---
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<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)

```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:

```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)*
