feat(db): add regulation-as-data tables for license height and horse age matrices

- Introduced `license_height_matrix` for mapping minimum license requirements to jump heights (ÖTO § 231).
- Added `horse_min_age_matrix` for defining minimum horse ages by discipline, height, or level (ÖTO § 103, FEI GR Art. 136).
- Populated both tables with ÖTO 2026 and FEI-compliant seed data.
- Updated Flyway V008 to remove incorrect `RD4` entry and annotated corrected enum references.
- Created Flyway V009 for introducing the new tables and their seeds.
- Aligned documentation, validation rules, and roadmaps for backend implementation handover.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-03 11:10:42 +02:00
parent 1f9f528554
commit c696b8c50e
7 changed files with 572 additions and 28 deletions
@@ -1,5 +1,7 @@
-- V008: Seed OETO 2026 Data (Turnierklassen, Lizenz-Matrix, Altersklassen)
-- Basierend auf ÖTO 2026
-- Lizenz-Keys entsprechen LizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3
-- HINWEIS: RD4 existiert NICHT im Enum nur RD1/RD2/RD3 sind gültige Dressur-Lizenzen (ÖTO 2026)
-- 1. Turnierklassen (Springen & Dressur)
INSERT INTO turnierklasse (turnierklasse_id, sparte, code, bezeichnung, max_hoehe, aufgaben_niveau)
@@ -33,8 +35,7 @@ INSERT INTO license_matrix (license_id, sparte, lizenz_klasse, max_turnierklasse
VALUES ('00000000-0000-0000-0002-000000000001', 'DRESSUR', 'LIZENZFREI', 'E'),
('00000000-0000-0000-0002-000000000002', 'DRESSUR', 'RD1', 'L'),
('00000000-0000-0000-0002-000000000003', 'DRESSUR', 'RD2', 'M'),
('00000000-0000-0000-0002-000000000004', 'DRESSUR', 'RD3', 'S'),
('00000000-0000-0000-0002-000000000005', 'DRESSUR', 'RD4', 'S');
('00000000-0000-0000-0002-000000000004', 'DRESSUR', 'RD3', 'S');
-- 3. Altersklassen (Standard ÖTO)
INSERT INTO altersklasse (id, altersklasse_code, bezeichnung, min_alter, max_alter)
@@ -0,0 +1,185 @@
-- V009: Regulation-as-Data Höhen-Lizenz-Matrix (Springen) & Mindestalter-Pferd-Matrix
-- Basierend auf ÖTO 2026 (§ 231 Springen, § 103 Dressur) und FEI GR Art. 136
-- Lizenz-Keys entsprechen LizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3
-- Status: DRAFT wird auf STABLE angehoben nach Fachfreigabe durch ÖTO-Fachreferat
-- ─────────────────────────────────────────────────────────────────────────────
-- 1. Tabelle: license_height_matrix
-- Zweck: Welche Lizenzklassen sind für einen Höhenbereich (Springen) erlaubt?
-- Granularität: Eine Zeile pro (Höhenbereich × erlaubte Lizenzklasse).
-- Abfrage-Pattern: SELECT lizenz_klasse FROM license_height_matrix
-- WHERE sparte = 'SPRINGEN'
-- AND hoehe_von_cm <= :hoehe AND hoehe_bis_cm >= :hoehe
-- AND ist_aktiv = true
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS license_height_matrix
(
id
UUID
PRIMARY
KEY
DEFAULT
gen_random_uuid
(
),
sparte VARCHAR
(
20
) NOT NULL,
hoehe_von_cm INTEGER NOT NULL,
hoehe_bis_cm INTEGER NOT NULL,
lizenz_klasse VARCHAR
(
20
) NOT NULL,
oeto_paragraph VARCHAR
(
50
),
bemerkung VARCHAR
(
255
),
valid_from TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
valid_to TIMESTAMP WITH TIME ZONE,
ist_aktiv BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_license_height_sparte_hoehe ON license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm);
CREATE INDEX idx_license_height_lizenz ON license_height_matrix (lizenz_klasse);
-- ─────────────────────────────────────────────────────────────────────────────
-- 2. Tabelle: horse_min_age_matrix
-- Zweck: Mindestalter des Pferdes (Stichtag 1. Jänner) je Sparte + Klasse/Höhe.
-- Abfrage-Pattern: SELECT min_alter_jahre FROM horse_min_age_matrix
-- WHERE sparte = :sparte
-- AND (:hoehe IS NULL OR (hoehe_von_cm <= :hoehe AND hoehe_bis_cm >= :hoehe))
-- AND (:niveau IS NULL OR aufgaben_niveau = :niveau)
-- AND ist_aktiv = true
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS horse_min_age_matrix
(
id
UUID
PRIMARY
KEY
DEFAULT
gen_random_uuid
(
),
sparte VARCHAR
(
20
) NOT NULL,
hoehe_von_cm INTEGER,
hoehe_bis_cm INTEGER,
aufgaben_niveau VARCHAR
(
20
),
min_alter_jahre INTEGER NOT NULL,
oeto_paragraph VARCHAR
(
50
),
fei_artikel VARCHAR
(
50
),
bemerkung VARCHAR
(
255
),
valid_from TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
valid_to TIMESTAMP WITH TIME ZONE,
ist_aktiv BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_horse_age_sparte ON horse_min_age_matrix (sparte);
-- ─────────────────────────────────────────────────────────────────────────────
-- 3. Seed: license_height_matrix Springen (CSN)
-- Quelle: ÖTO § 231 (Lizenz-Zuordnung nach Hindernishöhe)
-- Klassen: E0=6095 cm, A=100110 cm, L=115120 cm, LM=125130 cm, M=135 cm, S=140160 cm
--
-- Regel: Höhere Lizenz darf immer in niedrigerer Klasse starten (Aufwärts-Kompatibilität).
-- Daher: R2 darf auch in E0/A starten hier werden nur die MINDEST-Anforderungen je Bereich
-- abgebildet (= welche Lizenzen sind ZUGELASSEN, nicht welche sind OPTIMAL).
-- ─────────────────────────────────────────────────────────────────────────────
-- Klasse E0 (Einsteiger): 6095 cm → LIZENZFREI und R1 zugelassen (CSN-C-NEU Zwangsteilung)
INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 60, 95, 'LIZENZFREI', '§ 231 ÖTO', 'Abt. ohne Lizenz (CSN-C-NEU)'),
('SPRINGEN', 60, 95, 'R1', '§ 231 ÖTO', 'Abt. mit Lizenz (CSN-C-NEU)'),
('SPRINGEN', 60, 95, 'R2', '§ 231 ÖTO', 'Aufwärts-kompatibel'),
('SPRINGEN', 60, 95, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'),
('SPRINGEN', 60, 95, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel');
-- Klasse A (Anfänger): 100110 cm → ab R1 (keine LIZENZFREI mehr)
INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 100, 110, 'R1', '§ 231 ÖTO', 'Mindestlizenz ab 100 cm'),
('SPRINGEN', 100, 110, 'R2', '§ 231 ÖTO', 'Aufwärts-kompatibel'),
('SPRINGEN', 100, 110, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'),
('SPRINGEN', 100, 110, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel');
-- Klasse L (Leicht): 115120 cm → ab R2 empfohlen; R1 noch zugelassen (ausschreibungsabhängig)
INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 115, 120, 'R1', '§ 231 ÖTO', 'Zugelassen; Ausschreibung kann R2 vorschreiben'),
('SPRINGEN', 115, 120, 'R2', '§ 231 ÖTO', 'Empfohlene Mindestlizenz'),
('SPRINGEN', 115, 120, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'),
('SPRINGEN', 115, 120, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel');
-- Klasse LM (Leicht-Mittel): 125130 cm → ab R2
INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 125, 130, 'R2', '§ 231 ÖTO', 'Mindestlizenz ab 125 cm'),
('SPRINGEN', 125, 130, 'R3', '§ 231 ÖTO', 'Aufwärts-kompatibel'),
('SPRINGEN', 125, 130, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel');
-- Klasse M (Mittelschwer): 135 cm → ab R3
INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 135, 135, 'R3', '§ 231 ÖTO', 'Mindestlizenz ab 135 cm'),
('SPRINGEN', 135, 135, 'R4', '§ 231 ÖTO', 'Aufwärts-kompatibel');
-- Klasse S (Schwer): 140160 cm → R4
INSERT INTO license_height_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, lizenz_klasse, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 140, 160, 'R4', '§ 231 ÖTO', 'Mindestlizenz ab 140 cm (S-Klasse)');
-- ─────────────────────────────────────────────────────────────────────────────
-- 4. Seed: horse_min_age_matrix Springen (national, ÖTO § 231)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO horse_min_age_matrix (sparte, hoehe_von_cm, hoehe_bis_cm, min_alter_jahre, oeto_paragraph, bemerkung)
VALUES ('SPRINGEN', 60, 100, 4, '§ 231 ÖTO', 'Mindestalter Pferd, Stichtag 1. Jänner'),
('SPRINGEN', 101, 120, 5, '§ 231 ÖTO', 'Mindestalter Pferd, Stichtag 1. Jänner'),
('SPRINGEN', 121, 999, 6, '§ 231 ÖTO', 'Mindestalter Pferd, Stichtag 1. Jänner');
-- ─────────────────────────────────────────────────────────────────────────────
-- 5. Seed: horse_min_age_matrix Dressur (national, ÖTO § 103)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO horse_min_age_matrix (sparte, aufgaben_niveau, min_alter_jahre, oeto_paragraph, bemerkung)
VALUES ('DRESSUR', 'E', 4, '§ 103 ÖTO', 'Einsteiger/Dressurreiter, Stichtag 1. Jänner'),
('DRESSUR', 'A', 4, '§ 103 ÖTO', 'Klasse A, Stichtag 1. Jänner'),
('DRESSUR', 'L', 4, '§ 103 ÖTO', 'Klasse L, Stichtag 1. Jänner'),
('DRESSUR', 'LM', 5, '§ 103 ÖTO', 'Klasse LM/M, Stichtag 1. Jänner'),
('DRESSUR', 'M', 5, '§ 103 ÖTO', 'Klasse M, Stichtag 1. Jänner'),
('DRESSUR', 'S', 6, '§ 103 ÖTO', 'Klasse S, Stichtag 1. Jänner');
-- ─────────────────────────────────────────────────────────────────────────────
-- 6. Seed: horse_min_age_matrix Springen international (FEI GR Art. 136)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO horse_min_age_matrix (sparte, aufgaben_niveau, min_alter_jahre, fei_artikel, bemerkung)
VALUES ('SPRINGEN_FEI', '1_STAR', 6, 'Art. 136 FEI GR', 'FEI Jumping 1*2*'),
('SPRINGEN_FEI', '2_STAR', 6, 'Art. 136 FEI GR', 'FEI Jumping 1*2*'),
('SPRINGEN_FEI', '3_STAR', 7, 'Art. 136 FEI GR', 'FEI Jumping 3*5*'),
('SPRINGEN_FEI', '4_STAR', 7, 'Art. 136 FEI GR', 'FEI Jumping 3*5*'),
('SPRINGEN_FEI', '5_STAR', 7, 'Art. 136 FEI GR', 'FEI Jumping 3*5*');
-- ─────────────────────────────────────────────────────────────────────────────
-- 7. Seed: horse_min_age_matrix Dressur international (FEI GR Art. 136)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO horse_min_age_matrix (sparte, aufgaben_niveau, min_alter_jahre, fei_artikel, bemerkung)
VALUES ('DRESSUR_FEI', 'CDI_SENIOR', 7, 'Art. 136 FEI GR', 'FEI Dressage CDI Senior'),
('DRESSUR_FEI', 'CDI_YH', 4, 'Art. 136 FEI GR', 'FEI Young Horse gem. FEI YH-Regeln');