diff --git a/docs/03_Domain/01_Core_Model/Domain_Model_Veranstaltung_Turnier_Bewerb_Abteilung.md b/docs/03_Domain/01_Core_Model/Domain_Model_Veranstaltung_Turnier_Bewerb_Abteilung.md new file mode 100644 index 00000000..d0b29520 --- /dev/null +++ b/docs/03_Domain/01_Core_Model/Domain_Model_Veranstaltung_Turnier_Bewerb_Abteilung.md @@ -0,0 +1,179 @@ +--- +type: DOMAIN_SPEC +status: ACTIVE +owner: Lead Architect +last_update: 2026-04-02 +--- + +# Domänen‑Modell: Veranstaltung → Turnier → Bewerb → Abteilung + +Ziel: Dieses Dokument fixiert das offizielle Kern‑Modell für die Event‑Struktur sowie Kassa/Konten. Es ist die Single Source of Truth für Backend‑Schema, Frontend‑ViewModels und Schnittstellen. + +Quellen/Verweise: +- Ubiquitous Language: `docs/03_Domain/01_Glossary/Ubiquitous_Language.md` +- ÖTO/FEI Referenz: `docs/03_Domain/02_Reference/` (insb. Abteilungs‑Schwellenwerte) +- ADR‑0021 Tenant‑Resolution (Event‑Isolation): `docs/01_Architecture/adr/0021-tenant-resolution-strategy-de.md` + +## 1. Struktur und Kardinalitäten + +Hierarchie und Identifikatoren (kanonisch): + +``` +Veranstaltung (event_id) + ├─ Turnier (tournament_id) [1:N pro Veranstaltung] + │ └─ Bewerb (class_id) [1:N pro Turnier] + │ └─ Abteilung (division_id) [1:N pro Bewerb] + └─ TeilnehmerKonto (account_id) [1:N pro Veranstaltung, referenziert Teilnehmer] + └─ Veranstaltungs‑Kassa (event_cashbox_id = event_id) [1:1] +``` + +Leitlinien: +- Jede Veranstaltung ist ein eigener Tenant (Schema‑per‑Tenant gemäß ADR‑0021). +- IDs sind innerhalb des Tenants eindeutig; globale Adressen entstehen durch `{event_id}/{local_id}`. + +## 2. Entitäten und Aggregate + +### 2.1 Veranstaltung +- Schlüssel: `event_id` (Slug, z. B. `2026-moc-open`) +- Aggregate‑Grenze: umfasst Metadaten der Veranstaltung, Kassa, TeilnehmerKonto‑Katalog. +- Invarianten: + - `status ∈ {draft, active, archived}` + - Archivierte Veranstaltungen sind read‑only. + +### 2.2 Turnier +- Schlüssel: `tournament_id` (innerhalb Veranstaltung eindeutig) +- Attribute (Auszug): Titel, Datum(e), Ort, Status. +- Invarianten: + - Ein Turnier gehört genau zu einer Veranstaltung. + - Löschen nur erlaubt, wenn keine Nennungen/Ergebnisse bestätigt sind. + +### 2.3 Bewerb +- Schlüssel: `class_id` +- Attribute: Disziplin, Klasse, Lizenzanforderungen, max Starter, Wertungsmodus. +- Invarianten: + - Ein Bewerb gehört genau zu einem Turnier. + - Abteilungsbildung erfolgt gemäß Regelwerk/Schwellenwerten. + +### 2.4 Abteilung +- Schlüssel: `division_id` +- Attribute: Lauf‑/Startzeit, Parcours/Bahn, Typ (siehe unten), Ergebnisstatus. +- Invarianten: + - Eine Abteilung gehört genau zu einem Bewerb. + - Typen steuern UI, Zeitplan und Preisgeld-/Siegerehrungslogik. + +### 2.5 TeilnehmerKonto (auf Veranstaltungsebene) +- Zweck: Vereinheitlichte finanzielle Sicht je Teilnehmer über mehrere Turniere derselben Veranstaltung (Multi‑Turnier). +- Schlüssel: `account_id` +- Beziehungen: + - `Teilnehmer` (z. B. Reiter, Verein, Team) 1:1 ↔ TeilnehmerKonto (pro Veranstaltung) + - Buchungen entstehen aus Nennungen, Startgeldern, Gebühren, Gutschriften, Rückzahlungen turnierübergreifend. +- Invarianten: + - Ein Teilnehmer hat höchstens ein Konto pro Veranstaltung. + - Saldo ist Summe aller bestätigten Buchungen innerhalb des Tenants. + +### 2.6 Veranstaltungs‑Kassa (Turnier‑übergreifender Saldo) +- Zweck: Aggregierte Kasse der gesamten Veranstaltung; spiegelt Einzahlungen/Auszahlungen und Summen über alle Turniere. +- Schlüssel: `event_cashbox_id` = `event_id` +- Komponenten: + - Journal (Belege): Ein/Auszahlungen, Umbuchungen, Korrekturen. + - Summen: aktueller Bestand, Reserven, offene Posten (aggregiert aus TeilnehmerKonten). +- Invarianten: + - Jede Buchung betrifft genau ein Gegenkonto (TeilnehmerKonto oder internes Konto). + - Journal ist unveränderlich; Korrekturen erfolgen als Gegenbuchung. + +## 3. Abteilungs‑Typen + +Definiert als `enum DivisionType`: +- `STANDARD`: Normale Abteilung mit regulärer Siegerehrung innerhalb des Bewerbs. +- `SEPARATE_SIEGEREHRUNG`: Abteilung, deren Siegerehrung separat organisiert wird (z. B. zusammengelegt/zeitlich entkoppelt) — STATUS: vorläufig, Detailregeln folgen durch 📜 Rulebook Expert. +- `ORGANISATORISCH`: Rein organisatorische Abteilung (z. B. Aufteilung aus Zeit/Platz‑Gründen), ohne eigenständige sportliche Wertung/Preisgeldlogik. + +Hinweis: Die genaue Ausgestaltung von `SEPARATE_SIEGEREHRUNG` (Preisgeld‑Aggregation, Ranking‑Anzeige, Protokoll) wird im Rulebook‑Dokument ergänzt und kann weitere Felder/Beziehungen erfordern (z. B. Verweis auf „gemeinsame Siegerehrung für Bewerbe X/Y“). + +## 4. Datenmodell‑Skizze (relationale Sicht je Tenant) + +```sql +-- Veranstaltung (im Tenant‑Schema) +CREATE TABLE event ( + event_id TEXT PRIMARY KEY, + title TEXT NOT NULL, + status TEXT NOT NULL CHECK (status IN ('draft','active','archived')) +); + +CREATE TABLE tournament ( + tournament_id TEXT PRIMARY KEY, + event_id TEXT NOT NULL REFERENCES event(event_id), + title TEXT NOT NULL, + start_date DATE, + end_date DATE, + status TEXT NOT NULL +); + +CREATE TABLE class ( + class_id TEXT PRIMARY KEY, + tournament_id TEXT NOT NULL REFERENCES tournament(tournament_id), + discipline TEXT NOT NULL, + level TEXT NOT NULL, + max_starters INT, + scoring_mode TEXT NOT NULL +); + +CREATE TABLE division ( + division_id TEXT PRIMARY KEY, + class_id TEXT NOT NULL REFERENCES class(class_id), + type TEXT NOT NULL CHECK (type IN ('STANDARD','SEPARATE_SIEGEREHRUNG','ORGANISATORISCH')), + scheduled_at TIMESTAMP, + status TEXT NOT NULL +); + +-- TeilnehmerKonto (veranstaltungsweit) +CREATE TABLE participant_account ( + account_id TEXT PRIMARY KEY, + event_id TEXT NOT NULL REFERENCES event(event_id), + participant_ref TEXT NOT NULL, -- verweist auf Teilnehmer‑Stammdatensatz im Tenant + UNIQUE(event_id, participant_ref) +); + +CREATE TABLE participant_ledger_entry ( + entry_id TEXT PRIMARY KEY, + account_id TEXT NOT NULL REFERENCES participant_account(account_id), + booking_ts TIMESTAMP NOT NULL, + amount_cents BIGINT NOT NULL, + currency TEXT NOT NULL DEFAULT 'EUR', + source TEXT NOT NULL, -- z. B. Nennung, Startgeld, Rückzahlung + tournament_id TEXT NULL REFERENCES tournament(tournament_id) +); + +-- Veranstaltungs‑Kassa +CREATE TABLE event_cashbox ( + event_cashbox_id TEXT PRIMARY KEY REFERENCES event(event_id), + created_at TIMESTAMP NOT NULL +); + +CREATE TABLE cashbox_journal ( + journal_id TEXT PRIMARY KEY, + event_cashbox_id TEXT NOT NULL REFERENCES event_cashbox(event_cashbox_id), + booking_ts TIMESTAMP NOT NULL, + amount_cents BIGINT NOT NULL, + direction TEXT NOT NULL CHECK (direction IN ('IN','OUT')), + counterparty TEXT NOT NULL, -- account_id oder internes Konto + memo TEXT +); +``` + +## 5. Invarianten und Geschäftsregeln (Auszug) +- Abteilungs‑Typ `ORGANISATORISCH` darf keine eigenständige Preisgeldlogik auslösen. +- `SEPARATE_SIEGEREHRUNG` kann Ergebnisse bündeln/verschieben; Detailregeln werden im Rulebook spezifiziert. Bis dahin bleiben API‑Felder stabil, Verhalten konservativ (keine automatische Zusammenlegung ohne explizite Verknüpfung). +- TeilnehmerKonto‑Saldo = Summe aller bestätigten `participant_ledger_entry.amount_cents`. +- Event‑Kassa‑Bestand = Summe `IN` − Summe `OUT`; regelmäßige Abstimmung mit Summe aller Teilnehmer‑Offenen Posten. + +## 6. API/DTO Richtlinien (High‑Level) +- Alle API‑Ressourcen werden unterhalb des Tenants adressiert (Header `X-Event-Id`). +- DTOs tragen stabile `*_id` Felder entsprechend diesem Modell; Referenzen sind per ID, keine eingebetteten Aggregate außer Read‑Views. +- Enum `DivisionType` wird exakt wie oben benannt; neue Typen erfordern Versionserhöhung des Schemas. + +## 7. ToDos und Folgearbeiten +- 📜 Rulebook Expert: Detail‑Spezifikation `SEPARATE_SIEGEREHRUNG` (Preisgeld, Ranking, UI‑Hinweise) ergänzen. +- 🧹 Curator: `Ubiquitous_Language.md` um obige Begriffe/Definitionen erweitern. +- 👷 Backend: Schema‑Migrationen pro Tenant gemäß obiger Tabellen; Repositories/Services entsprechend zuschneiden. +- 🎨 Frontend: ViewModels/Stores entlang dieser Struktur aktualisieren (Navigation: Veranstaltung → Turnier → Bewerb → Abteilung). diff --git a/docs/03_Domain/02_Reference/Validierungsregeln.md b/docs/03_Domain/02_Reference/Validierungsregeln.md new file mode 100644 index 00000000..cefa32a6 --- /dev/null +++ b/docs/03_Domain/02_Reference/Validierungsregeln.md @@ -0,0 +1,99 @@ +--- +type: RULE_SPEC +status: DRAFT +owner: Rulebook Expert +last_update: 2026-04-02 +--- + +# 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 +ToDo: Wird in A‑1 (weiterer Unterpunkt) spezifiziert. + +--- + +## 3. Lizenzklassen (R1–R4, RD1–RD3, LZF) +ToDo: Vollständige Liste und Zuordnung in A‑1 (weiterer Unterpunkt). + +--- + +## 4. Altersklassen Pferd +ToDo: Mindestalter je Bewerbsklasse / Höhe und Stichtagsregel (1. Jänner) – folgt in A‑1 (weiterer Unterpunkt). diff --git a/docs/04_Agents/Roadmaps/Architect_Roadmap.md b/docs/04_Agents/Roadmaps/Architect_Roadmap.md index c5dffd39..44c25b71 100644 --- a/docs/04_Agents/Roadmaps/Architect_Roadmap.md +++ b/docs/04_Agents/Roadmaps/Architect_Roadmap.md @@ -13,12 +13,12 @@ - [x] ADR-0021 in `docs/01_Architecture/adr/` ablegen - [x] Backend Developer informieren (A-3 ist Blocker) -- [ ] **A-2** | Domänen-Modell formal präzisieren - - [ ] Hierarchie `Veranstaltung → Turnier → Bewerb → Abteilung` als offizielles Modell festschreiben - - [ ] `TeilnehmerKonto` auf Veranstaltungsebene (Multi-Turnier) ins Modell aufnehmen - - [ ] Veranstaltungs-Kassa mit Turnier-übergreifendem Saldo modellieren - - [ ] Abteilungs-Typen `SEPARATE_SIEGEREHRUNG` und `ORGANISATORISCH` ins Modell aufnehmen - - [ ] Curator beauftragen: `Ubiquitous_Language.md` aktualisieren +- [x] **A-2** | Domänen-Modell formal präzisieren + - [x] Hierarchie `Veranstaltung → Turnier → Bewerb → Abteilung` als offizielles Modell festschreiben + - [x] `TeilnehmerKonto` auf Veranstaltungsebene (Multi-Turnier) ins Modell aufnehmen + - [x] Veranstaltungs-Kassa mit Turnier-übergreifendem Saldo modellieren + - [x] Abteilungs-Typen `SEPARATE_SIEGEREHRUNG` (vorläufig) und `ORGANISATORISCH` ins Modell aufnehmen + - [x] Curator beauftragen: `Ubiquitous_Language.md` aktualisieren --- diff --git a/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md b/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md index 599cfe40..b4e7e6f9 100644 --- a/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md +++ b/docs/04_Agents/Roadmaps/Rulebook_Roadmap.md @@ -8,9 +8,10 @@ ## 🔴 Sprint A — Sofort (diese Woche) - [ ] **A-1** | Validierungsregeln schriftlich spezifizieren — Grundlage für alle anderen Teams - - [ ] **OEPS-Mitgliedsnummer** - - [ ] Gültiges Format definieren (Länge, erlaubte Zeichen, Präfixe) - - [ ] Ungültige Beispiele dokumentieren + - [x] **OEPS-Mitgliedsnummer** + - [x] Gültiges Format definieren (Länge, erlaubte Zeichen, Präfixe) + - [x] Ungültige Beispiele dokumentieren + - Ergebnis: siehe `docs/03_Domain/02_Reference/Validierungsregeln.md` Abschnitt „OEPS‑Mitgliedsnummer“ - [ ] **FEI-ID** - [ ] Gültiges Format definieren - [ ] Wann ist FEI-ID Pflicht? (Turnierkategorie-abhängig) diff --git a/docs/temp/ToDos und Folgearbeiten.md b/docs/temp/ToDos und Folgearbeiten.md new file mode 100644 index 00000000..d21f3824 --- /dev/null +++ b/docs/temp/ToDos und Folgearbeiten.md @@ -0,0 +1,6 @@ +## ToDos und Folgearbeiten +- 📜 Rulebook Expert: Detail‑Spezifikation `SEPARATE_SIEGEREHRUNG` (Preisgeld, Ranking, UI‑Hinweise) ergänzen. +- 🧹 Curator: `Ubiquitous_Language.md` um obige Begriffe/Definitionen erweitern. +- 👷 Backend: Schema‑Migrationen pro Tenant gemäß obiger Tabellen; Repositories/Services entsprechend zuschneiden. +- 🎨 Frontend: ViewModels/Stores entlang dieser Struktur aktualisieren (Navigation: Veranstaltung → Turnier → Bewerb → Abteilung). +