Refactor domain models (DomFunktionaer, DomReiter, DomPferd) to align with ZNS conventions: simplify naming, update properties, and enhance parser logic. Adjust related controllers, repository methods, and tests. Update MASTER_ROADMAP with changes to domain models.

This commit is contained in:
2026-04-06 00:00:20 +02:00
parent 1e5fa3d053
commit f50d4deb16
57 changed files with 811 additions and 532 deletions
@@ -3,7 +3,7 @@
package at.mocode.masterdata.api.rest
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.masterdata.domain.model.DomFunktionaer
import at.mocode.masterdata.domain.model.Funktionaer
import at.mocode.masterdata.domain.repository.FunktionaerRepository
import io.ktor.http.*
import io.ktor.server.request.*
@@ -96,8 +96,8 @@ class FunktionaerController(private val funktionaerRepository: FunktionaerReposi
*/
post {
val req = call.receive<FunktionaerCreateRequest>()
val domFunktionaer = DomFunktionaer(
satzID = req.satzID,
val domFunktionaer = Funktionaer(
satzId = req.satzID,
satzNummer = req.satzNummer,
name = req.name,
qualifikationen = req.qualifikationen,
@@ -139,9 +139,9 @@ class FunktionaerController(private val funktionaerRepository: FunktionaerReposi
private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() }
private fun DomFunktionaer.toDto() = FunktionaerDto(
private fun Funktionaer.toDto() = FunktionaerDto(
funktionaerId = funktionaerId.toString(),
satzID = satzID,
satzID = satzId,
satzNummer = satzNummer,
name = name,
qualifikationen = qualifikationen,
@@ -4,7 +4,7 @@ package at.mocode.masterdata.api.rest
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.model.Pferd
import at.mocode.masterdata.domain.repository.HorseRepository
import io.ktor.http.*
import io.ktor.server.request.*
@@ -110,7 +110,7 @@ class HorseController(private val horseRepository: HorseRepository) {
val req = call.receive<HorseCreateRequest>()
val geschlecht = runCatching { PferdeGeschlechtE.valueOf(req.geschlecht) }.getOrNull()
?: return@post call.respond(HttpStatusCode.BadRequest, "Ungültiges Geschlecht: ${req.geschlecht}")
val domPferd = DomPferd(
val pferd = Pferd(
kopfnummer = req.kopfnummer,
pferdeName = req.pferdeName,
lebensnummer = req.lebensnummer,
@@ -120,7 +120,7 @@ class HorseController(private val horseRepository: HorseRepository) {
satznummer = req.satznummer,
istAktiv = req.istAktiv
)
val saved = horseRepository.save(domPferd)
val saved = horseRepository.save(pferd)
call.respond(HttpStatusCode.Created, saved.toDto())
}
@@ -161,7 +161,7 @@ class HorseController(private val horseRepository: HorseRepository) {
private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() }
private fun DomPferd.toDto() = HorseDto(
private fun Pferd.toDto() = HorseDto(
pferdId = pferdId.toString(),
kopfnummer = kopfnummer,
pferdeName = pferdeName,
@@ -2,10 +2,10 @@
package at.mocode.masterdata.api.rest
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.LocalDateSerializer
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.repository.ReiterRepository
import io.ktor.http.*
import io.ktor.server.request.*
@@ -132,9 +132,9 @@ class ReiterController(private val reiterRepository: ReiterRepository) {
*/
post {
val req = call.receive<ReiterCreateRequest>()
val lizenzKlasse = runCatching { LizenzKlasseE.valueOf(req.lizenzKlasse) }.getOrNull()
val lizenzKlasse = runCatching { ReiterLizenzKlasseE.valueOf(req.lizenzKlasse) }.getOrNull()
?: return@post call.respond(HttpStatusCode.BadRequest, "Ungültige lizenzKlasse: ${req.lizenzKlasse}")
val domReiter = DomReiter(
val domReiter = Reiter(
personId = Uuid.random(),
satznummer = req.satznummer,
nachname = req.nachname,
@@ -165,7 +165,7 @@ class ReiterController(private val reiterRepository: ReiterRepository) {
val existing = reiterRepository.findById(id) ?: return@put call.respond(HttpStatusCode.NotFound)
val req = call.receive<ReiterUpdateRequest>()
val lizenzKlasse = req.lizenzKlasse?.let {
runCatching { LizenzKlasseE.valueOf(it) }.getOrNull()
runCatching { ReiterLizenzKlasseE.valueOf(it) }.getOrNull()
?: return@put call.respond(HttpStatusCode.BadRequest, "Ungültige lizenzKlasse: $it")
} ?: existing.lizenzKlasse
val updated = existing.copy(
@@ -202,7 +202,7 @@ class ReiterController(private val reiterRepository: ReiterRepository) {
private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() }
private fun DomReiter.toDto() = ReiterDto(
private fun Reiter.toDto() = ReiterDto(
reiterId = reiterId.toString(),
satznummer = satznummer,
nachname = nachname,
@@ -3,7 +3,7 @@
package at.mocode.masterdata.api.rest
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.masterdata.domain.model.DomVerein
import at.mocode.masterdata.domain.model.Verein
import at.mocode.masterdata.domain.repository.VereinRepository
import io.ktor.http.*
import io.ktor.server.request.*
@@ -24,7 +24,6 @@ class VereinController(private val vereinRepository: VereinRepository) {
val vereinId: String,
val vereinsNummer: String,
val name: String,
val kurzname: String? = null,
val bundesland: String? = null,
val ort: String? = null,
val plz: String? = null,
@@ -32,9 +31,9 @@ class VereinController(private val vereinRepository: VereinRepository) {
val email: String? = null,
val telefon: String? = null,
val website: String? = null,
val oepsRegionNummer: String? = null,
val istVeranstalter: Boolean,
val istAktiv: Boolean,
val imageUrl: String? = null,
val bemerkungen: String? = null,
@Serializable(with = InstantSerializer::class)
val updatedAt: Instant
@@ -44,7 +43,6 @@ class VereinController(private val vereinRepository: VereinRepository) {
data class VereinCreateRequest(
val vereinsNummer: String,
val name: String,
val kurzname: String? = null,
val bundesland: String? = null,
val ort: String? = null,
val plz: String? = null,
@@ -52,16 +50,15 @@ class VereinController(private val vereinRepository: VereinRepository) {
val email: String? = null,
val telefon: String? = null,
val website: String? = null,
val oepsRegionNummer: String? = null,
val istVeranstalter: Boolean = false,
val istAktiv: Boolean = true,
val imageUrl: String? = null,
val bemerkungen: String? = null
)
@Serializable
data class VereinUpdateRequest(
val name: String? = null,
val kurzname: String? = null,
val bundesland: String? = null,
val ort: String? = null,
val plz: String? = null,
@@ -69,9 +66,9 @@ class VereinController(private val vereinRepository: VereinRepository) {
val email: String? = null,
val telefon: String? = null,
val website: String? = null,
val oepsRegionNummer: String? = null,
val istVeranstalter: Boolean? = null,
val istAktiv: Boolean? = null,
val imageUrl: String? = null,
val bemerkungen: String? = null
)
@@ -95,7 +92,7 @@ class VereinController(private val vereinRepository: VereinRepository) {
}
/**
* GET /verein/search?q=... — Sucht Vereine nach Name oder Kurzname.
* GET /verein/search?q=... — Sucht Vereine nach Name.
*/
get("/search") {
val query = call.request.queryParameters["q"] ?: ""
@@ -126,10 +123,9 @@ class VereinController(private val vereinRepository: VereinRepository) {
*/
post {
val req = call.receive<VereinCreateRequest>()
val domVerein = DomVerein(
val domVerein = Verein(
vereinsNummer = req.vereinsNummer,
name = req.name,
kurzname = req.kurzname,
vereinName = req.name,
bundesland = req.bundesland,
ort = req.ort,
plz = req.plz,
@@ -137,9 +133,9 @@ class VereinController(private val vereinRepository: VereinRepository) {
email = req.email,
telefon = req.telefon,
website = req.website,
oepsRegionNummer = req.oepsRegionNummer,
istVeranstalter = req.istVeranstalter,
istAktiv = req.istAktiv,
imageUrl = req.imageUrl,
bemerkungen = req.bemerkungen
)
val saved = vereinRepository.save(domVerein)
@@ -154,8 +150,7 @@ class VereinController(private val vereinRepository: VereinRepository) {
val existing = vereinRepository.findById(id) ?: return@put call.respond(HttpStatusCode.NotFound)
val req = call.receive<VereinUpdateRequest>()
val updated = existing.copy(
name = req.name ?: existing.name,
kurzname = req.kurzname ?: existing.kurzname,
vereinName = req.name ?: existing.vereinName,
bundesland = req.bundesland ?: existing.bundesland,
ort = req.ort ?: existing.ort,
plz = req.plz ?: existing.plz,
@@ -163,9 +158,9 @@ class VereinController(private val vereinRepository: VereinRepository) {
email = req.email ?: existing.email,
telefon = req.telefon ?: existing.telefon,
website = req.website ?: existing.website,
oepsRegionNummer = req.oepsRegionNummer ?: existing.oepsRegionNummer,
istVeranstalter = req.istVeranstalter ?: existing.istVeranstalter,
istAktiv = req.istAktiv ?: existing.istAktiv,
imageUrl = req.imageUrl ?: existing.imageUrl,
bemerkungen = req.bemerkungen ?: existing.bemerkungen
)
val saved = vereinRepository.save(updated)
@@ -185,11 +180,10 @@ class VereinController(private val vereinRepository: VereinRepository) {
private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() }
private fun DomVerein.toDto() = VereinDto(
private fun Verein.toDto() = VereinDto(
vereinId = vereinId.toString(),
vereinsNummer = vereinsNummer,
name = name,
kurzname = kurzname,
name = vereinName,
bundesland = bundesland,
ort = ort,
plz = plz,
@@ -197,9 +191,9 @@ class VereinController(private val vereinRepository: VereinRepository) {
email = email,
telefon = telefon,
website = website,
oepsRegionNummer = oepsRegionNummer,
istVeranstalter = istVeranstalter,
istAktiv = istAktiv,
imageUrl = imageUrl,
bemerkungen = bemerkungen,
updatedAt = updatedAt
)
@@ -18,7 +18,7 @@ import kotlin.uuid.Uuid
* aus dem ZNS geprüft.
*
* @property funktionaerId Eindeutige interne ID (UUID).
* @property satzID Typ des Satzes (X = Richter, Y = Parcoursbauer). Aus ZNS (RICHT01.DAT / PARCO01.DAT).
* @property satzId Typ des Satzes (X = Richter, Y = Parcoursbauer). Aus ZNS (RICHT01.DAT / PARCO01.DAT).
* @property satzNummer Satznummer (6-stellig). Aus ZNS (RICHT01.DAT / PARCO01.DAT).
* @property name Vollständiger Name (Nachname, Vorname). Aus ZNS (RICHT01.DAT / PARCO01.DAT).
* @property qualifikation Qualifikationen (getrennt durch `,`). Aus ZNS (RICHT01.DAT / PARCO01.DAT).
@@ -29,24 +29,42 @@ import kotlin.uuid.Uuid
* @property updatedAt Letzter Änderungszeitpunkt.
*/
@Serializable
data class DomFunktionaer(
data class Funktionaer(
@Serializable(with = UuidSerializer::class)
val funktionaerId: Uuid = Uuid.random(),
val satzID: String,
// Reference to base person
@Serializable(with = UuidSerializer::class)
val personId: Uuid? = null,
// === ZNS.zip RICHT01.DAT === ANFANG ===
// Alphanumerisch (1) WERT "X" = RICHTER, "Y" = PARCOURSBAUER
var satzId: String,
// Numerisch (6) FORMAT: 000000
val satzNummer: Int,
// Alphanumerisch (75)
var name: String? = null, // Nachname, Vorname
// Alphanumerisch (30+)
var qualifikationen: List<String> = emptyList(), // Liste der Qualifikations-Kürzel
// var vorname: String,
// var nachname: String,
// var geburtsdatum: LocalDate? = null,
// val richterNummer: String? = null,
// var rollen: Set<FunktionaerRolleE> = emptySet(),
// var richterQualifikation: RichterQualifikationE? = null,
// var qualifiziertFuerSparten: Set<SparteE> = emptySet(),
// var email: String? = null,
// var telefon: String? = null,
// var vereinsNummer: String? = null,
// === ZNS.zip RICHT01.DAT === ENDE ===
// Kontakt
var imageUrl: String? = null,
var email: String? = null,
var telefon: String? = null,
var website: String? = null,
// Adresse
var strasse: String? = null,
var hausnummer: String? = null,
var ort: String? = null,
var plz: String? = null,
var bundesland: String? = null,
// Status & Verwaltung
var istAktiv: Boolean = true,
@@ -73,12 +91,12 @@ data class DomFunktionaer(
/**
* Prüft, ob der Funktionär als Richter qualifiziert ist.
*/
fun istRichter(): Boolean = satzID.uppercase() == "X"
fun istRichter(): Boolean = satzId.uppercase() == "X"
/**
* Prüft, ob der Funktionär als Parcoursbauer qualifiziert ist.
*/
fun istParcoursbauer(): Boolean = satzID.uppercase() == "Y"
fun istParcoursbauer(): Boolean = satzId.uppercase() == "Y"
/**
* Validiert die Pflichtfelder für den Turniereinsatz.
@@ -97,5 +115,5 @@ data class DomFunktionaer(
/**
* Erstellt eine Kopie mit aktualisiertem Zeitstempel.
*/
fun withUpdatedTimestamp(): DomFunktionaer = this.copy(updatedAt = Clock.System.now())
fun withUpdatedTimestamp(): Funktionaer = this.copy(updatedAt = Clock.System.now())
}
@@ -2,7 +2,7 @@
package at.mocode.masterdata.domain.model
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.SparteE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.UuidSerializer
@@ -15,20 +15,20 @@ import kotlin.uuid.Uuid
*/
@Serializable
data class LicenseMatrixEntry(
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val licenseId: Uuid = Uuid.random(),
val sparte: SparteE,
val lizenzKlasse: LizenzKlasseE,
val maxTurnierklasseCode: String, // E, A, L, LM, M, S
val sparte: SparteE,
val lizenzKlasse: ReiterLizenzKlasseE,
val maxTurnierklasseCode: String, // E, A, L, LM, M, S
@Serializable(with = InstantSerializer::class)
@Serializable(with = InstantSerializer::class)
val validFrom: Instant,
@Serializable(with = InstantSerializer::class)
@Serializable(with = InstantSerializer::class)
val validTo: Instant? = null,
val istAktiv: Boolean = true,
@Serializable(with = InstantSerializer::class)
val istAktiv: Boolean = true,
@Serializable(with = InstantSerializer::class)
val createdAt: Instant,
@Serializable(with = InstantSerializer::class)
@Serializable(with = InstantSerializer::class)
val updatedAt: Instant
)
@@ -4,6 +4,7 @@ package at.mocode.masterdata.domain.model
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.UuidSerializer
import kotlinx.datetime.todayIn
import kotlinx.serialization.Serializable
@@ -39,42 +40,67 @@ import kotlin.uuid.Uuid
* @property updatedAt Timestamp when this record was last updated.
*/
@Serializable
data class DomPferd(
data class Pferd(
@Serializable(with = UuidSerializer::class)
val pferdId: Uuid = Uuid.random(),
// PFERDE01.DAT Information
// Reference to base person
@Serializable(with = UuidSerializer::class)
val personId: Uuid? = null,
// === ZNS.zip PFERDE01.DAT === ANFANG ===
// Alphanumerisch (4)
var kopfnummer: String? = null,
// Alphanumerisch (30)
var pferdeName: String,
// Alphanumerisch (9) FORMAT: 000000000
var lebensnummer: String? = null,
// Alphanumerisch (1)
var geschlecht: PferdeGeschlechtE,
// Numerisch (4) FORMAT: 0000
var geburtsjahr: Int? = null,
// Alphanumerisch (15)
var farbe: String? = null,
// Alphanumerisch (15)
var abstammung: String? = null,
// Numerisch (4) FORMAT: 0000
var vereinNummer: Int? = null,
// Numerisch (4) FORMAT: 0000
var lastPayYear: Int? = null,
// Alphanumerisch (75) Standard: BLANK
var verantwortlichePersonId: String? = null,
// Alphanumerisch (30) Standard: BLANK
var vater: String? = null,
// Alphanumerisch (10) Standard: BLANK
var feiPass: String? = null,
// Alphanumerisch (10) FORMAT: 0000000000
var satznummer: String? = null,
// var geburtsdatum: LocalDate? = null,
// var rasse: String? = null,
// @Serializable(with = UuidSerializer::class)
// var besitzerId: Uuid? = null,
// var zuechterName: String? = null,
// var zuchtbuchNummer: String? = null,
// var chipNummer: String? = null,
// var passNummer: String? = null,
// var oepsNummer: String? = null,
// var mutterName: String? = null,
// var mutterVaterName: String? = null,
// var stockmass: Int? = null, // Height in cm
// === ZNS.zip PFERDE01.DAT === ENDE ===
var istAktiv: Boolean = true,
// Status & Verwaltung
val istAktiv: Boolean = true,
var bemerkungen: String? = null,
var datenQuelle: DatenQuelleE = DatenQuelleE.MANUELL,
var createdAt: Instant = Clock.System.now(),
val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
// Audit
@Serializable(with = InstantSerializer::class)
val createdAt: Instant = Clock.System.now(),
@Serializable(with = InstantSerializer::class)
var updatedAt: Instant = Clock.System.now()
) {
/**
@@ -141,7 +167,7 @@ data class DomPferd(
/**
* Creates a copy of this horse with an updated timestamp.
*/
fun withUpdatedTimestamp(): DomPferd {
fun withUpdatedTimestamp(): Pferd {
return this.copy(updatedAt = Clock.System.now())
}
}
@@ -3,8 +3,7 @@
package at.mocode.masterdata.domain.model
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.SparteE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.LocalDateSerializer
import at.mocode.core.domain.serialization.UuidSerializer
@@ -51,42 +50,101 @@ import kotlin.uuid.Uuid
* @property updatedAt Timestamp when this record was last updated.
*/
@Serializable
data class DomReiter(
data class Reiter(
@Serializable(with = UuidSerializer::class)
val reiterId: Uuid = Uuid.random(),
// Reference to base person
@Serializable(with = UuidSerializer::class)
val personId: Uuid,
val personId: Uuid? = null,
// ZNS Identification
// === ZNS.zip LIZENZ01.DAT === ANFANG ===
// Alphanumerisch (6) FORMAT: 000000
var satznummer: String?,
// Alphanumerisch (50)
var nachname: String,
// Alphanumerisch (25)
var vorname: String,
// Numerisch (2) FORMAT: 99
var bundeslandNummer: Int? = null,
// Alphanumerisch (50)
var vereinsName: String? = null,
// Alphanumerisch (3)
var nation: String? = null,
// Alphanumerisch (4) Keine Lizenz: BLANK
var reiterLizenz: String? = null,
// Alphanumerisch (1) Keine Startkarte: BLANK
var startkarte: String? = null,
// Alphanumerisch (2) Keine Fahrlizenz: BLANK
var fahrLizenz: String? = null,
// Alphanumerisch (2) WERTE: Standard: BLANK, JG=JUGENDLICHER, JR=JUNIOR, 25=U25
var altersklasseJgJrU25: String? = null,
// Alphanumerisch (1) WERTE: Standard: BLANK Y=JUNGER-REITER
var altersklasseY: String? = null,
// Numerisch (8) FORMAT: 00000000
var mitgliedsNummer: Int? = null,
// Alphanumerisch (21) Standard: BLANK
var telefonNummer: String? = null,
// Alphanumerisch (1) Standard: BLANK
var kader: String? = null,
/** Numerisch (4). Letztes Jahr, in dem die Lizenz bezahlt wurde. */
var lastPayYear: Int? = null,
/** Bestimmte Lizenzklasse (z.B. R1, R2, ...). */
var lizenzKlasse: ReiterLizenzKlasseE = ReiterLizenzKlasseE.LIZENZFREI,
// Alphanumerisch (1) WERTE: M=MÄNNLICH, W=WEIBLICH
var geschlecht: String? = null,
// Datum (8) FORMAT: YYYYMMDD
@Serializable(with = LocalDateSerializer::class)
var geburtsdatum: LocalDate? = null,
// Alphanumerisch (10) Standard: BLANK
var feiId: String? = null,
// Alphanumerisch (1) Werte: BLANK = nicht auf Sperrliste, S=auf Sperrliste, dort nachsehen!
var sperrListe: String? = null,
// Alphanumerisch (10) Standard: BLANK
var lizenzInfo: String? = null,
var lizenzKlasse: LizenzKlasseE = LizenzKlasseE.LIZENZFREI,
// === ZNS.zip LIZENZ01.DAT === ENDE ===
// Kontakt
var imageUrl: String? = null,
var email: String? = null,
var telefon: String? = null,
var website: String? = null,
// Adresse
var strasse: String? = null,
var hausnummer: String? = null,
var plz: String? = null,
var ort: String? = null,
var bundesland: String? = null,
// Status & Verwaltung
val istAktiv: Boolean = true,
var bemerkungen: String? = null,
val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
// Audit
@Serializable(with = InstantSerializer::class)
val createdAt: Instant = Clock.System.now(),
@Serializable(with = InstantSerializer::class)
@@ -112,19 +170,19 @@ data class DomReiter(
fun hasLizenz(): Boolean = !reiterLizenz.isNullOrBlank()
/**
* Checks if the rider has a license for a specific sparte.
* Checks if the rider holds a license for a specific discipline.
* Simple logic for now: Any non-blank license field counts.
*/
fun hasLizenzForSparte(sparte: SparteE): Boolean {
// If we have a license class, check if it's applicable for the sparte
if (lizenzKlasse == LizenzKlasseE.LIZENZFREI) return false
fun hasLizenzForSparte(sparte: at.mocode.core.domain.model.SparteE): Boolean {
return when (sparte) {
SparteE.DRESSUR -> true // Everyone with a license can do dressage (simplified)
SparteE.SPRINGEN -> !listOf(LizenzKlasseE.RD1, LizenzKlasseE.RD2, LizenzKlasseE.RD3).contains(lizenzKlasse)
else -> true
at.mocode.core.domain.model.SparteE.DRESSUR -> !reiterLizenz.isNullOrBlank()
at.mocode.core.domain.model.SparteE.SPRINGEN -> !reiterLizenz.isNullOrBlank()
at.mocode.core.domain.model.SparteE.FAHREN -> !fahrLizenz.isNullOrBlank()
else -> hasLizenz()
}
}
/**
* Validates the rider for competition entry.
* Returns a list of warning messages (never hard errors TBA has final say).
@@ -146,5 +204,5 @@ data class DomReiter(
/**
* Creates a copy of this rider with an updated timestamp.
*/
fun withUpdatedTimestamp(): DomReiter = this.copy(updatedAt = Clock.System.now())
fun withUpdatedTimestamp(): Reiter = this.copy(updatedAt = Clock.System.now())
}
@@ -21,8 +21,7 @@ import kotlin.uuid.Uuid
*
* @property vereinId Eindeutige interne ID (UUID).
* @property vereinsNummer ÖPS-Vereinsnummer aus ZNS (VEREIN01.dat), 4-stellig. Primärschlüssel für ZNS-Datenaustausch.
* @property name Offizieller Vereinsname.
* @property kurzname Kurzbezeichnung des Vereins (optional).
* @property vereinName Offizieller Vereinsname.
* @property bundesland Bundesland, in dem der Verein ansässig ist.
* @property ort Ort / Stadt des Vereinssitzes.
* @property plz Postleitzahl.
@@ -30,7 +29,6 @@ import kotlin.uuid.Uuid
* @property email Offizielle E-Mail-Adresse des Vereins.
* @property telefon Telefonnummer des Vereins.
* @property website Website-URL des Vereins.
* @property oepsRegionNummer Regionsnummer beim OEPS (Landesverband).
* @property istVeranstalter Ob der Verein als Veranstalter von Turnieren zugelassen ist.
* @property istAktiv Ob der Verein aktuell aktiv ist.
* @property bemerkungen Interne Notizen.
@@ -39,35 +37,40 @@ import kotlin.uuid.Uuid
* @property updatedAt Letzter Änderungszeitpunkt.
*/
@Serializable
data class DomVerein(
data class Verein(
@Serializable(with = UuidSerializer::class)
val vereinId: Uuid = Uuid.random(),
// Identifikation
// Reference to base person
@Serializable(with = UuidSerializer::class)
val personId: Uuid? = null,
// === ZNS.zip VEREIN01.DAT === ANFANG ===
/** Numerisch (4) FORMAT: 0000. Primärschlüssel für ZNS-Datenaustausch. */
val vereinsNummer: String,
// Stammdaten
var name: String,
var kurzname: String? = null,
/** Alphanumerisch (50). Offizieller Vereinsname. */
var vereinName: String,
// Adresse
var bundesland: String? = null,
var ort: String? = null,
var plz: String? = null,
var strasse: String? = null,
// === ZNS.zip VEREIN01.DAT === ENDE ===
// Kontakt
var imageUrl: String? = null,
var email: String? = null,
var telefon: String? = null,
var website: String? = null,
// OEPS-Verwaltung
var oepsRegionNummer: String? = null,
var istVeranstalter: Boolean = false,
// Adresse
var strasse: String? = null,
var hausnummer: String? = null,
var plz: String? = null,
var ort: String? = null,
var bundesland: String? = null,
// Status & Verwaltung
var istAktiv: Boolean = true,
var logoUrl: String? = null,
var istVeranstalter: Boolean = false,
var bemerkungen: String? = null,
var datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS,
@@ -80,7 +83,7 @@ data class DomVerein(
/**
* Gibt den Anzeigenamen zurück Kurzname bevorzugt, sonst vollständiger Name.
*/
fun getDisplayName(): String = kurzname ?: name
fun getDisplayName(): String = vereinName
/**
* Gibt den vollständigen Anzeigenamen mit Vereinsnummer zurück.
@@ -91,13 +94,13 @@ data class DomVerein(
* Prüft, ob vollständige Adressdaten vorhanden sind.
*/
fun hasCompleteAddress(): Boolean =
!ort.isNullOrBlank() && !plz.isNullOrBlank() && !strasse.isNullOrBlank()
!strasse.isNullOrBlank() && !plz.isNullOrBlank() && !ort.isNullOrBlank()
/**
* Validiert den Verein für den Einsatz als Veranstalter.
* Gibt Warnungen zurück (kein harter Fehler Override-Event möglich).
*/
fun validateFuerVeranstaltung(): List<String> {
fun validateVeranstaltung(): List<String> {
val warnings = mutableListOf<String>()
if (!istAktiv) {
@@ -118,5 +121,5 @@ data class DomVerein(
/**
* Erstellt eine Kopie mit aktualisiertem Zeitstempel.
*/
fun withUpdatedTimestamp(): DomVerein = this.copy(updatedAt = Clock.System.now())
fun withUpdatedTimestamp(): Verein = this.copy(updatedAt = Clock.System.now())
}
@@ -2,11 +2,11 @@
package at.mocode.masterdata.domain.repository
import at.mocode.masterdata.domain.model.DomFunktionaer
import at.mocode.masterdata.domain.model.Funktionaer
import kotlin.uuid.Uuid
/**
* Repository-Interface für DomFunktionaer (Funktionär) Domain-Operationen.
* Repository-Interface für Funktionaer (Funktionär) Domain-Operationen.
*
* Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit
* von konkreten Implementierungsdetails (Datenbank, etc.).
@@ -16,22 +16,22 @@ interface FunktionaerRepository {
/**
* Sucht einen Funktionär anhand seiner eindeutigen ID.
*/
suspend fun findById(id: Uuid): DomFunktionaer?
suspend fun findById(id: Uuid): Funktionaer?
/**
* Sucht einen Funktionär anhand seiner Satz-ID und Satznummer.
*/
suspend fun findBySatz(satzID: String, satzNummer: Int): DomFunktionaer?
suspend fun findBySatz(satzID: String, satzNummer: Int): Funktionaer?
/**
* Gibt alle Funktionäre zurück (paginiert).
*/
suspend fun findAll(limit: Int = 100, offset: Int = 0): List<DomFunktionaer>
suspend fun findAll(limit: Int = 100, offset: Int = 0): List<Funktionaer>
/**
* Speichert einen Funktionär (Insert oder Update).
*/
suspend fun save(funktionaer: DomFunktionaer): DomFunktionaer
suspend fun save(funktionaer: Funktionaer): Funktionaer
/**
* Löscht einen Funktionär anhand seiner ID.
@@ -3,11 +3,11 @@
package at.mocode.masterdata.domain.repository
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.model.Pferd
import kotlin.uuid.Uuid
/**
* Repository interface for DomPferd (Horse) domain operations.
* Repository interface for Pferd (Horse) domain operations.
*
* This interface defines the contract for horse data access operations
* without depending on specific implementation details (database, etc.).
@@ -22,7 +22,7 @@ interface HorseRepository {
* @param id The unique identifier of the horse
* @return The horse if found, null otherwise
*/
suspend fun findById(id: Uuid): DomPferd?
suspend fun findById(id: Uuid): Pferd?
/**
* Finds a horse by its life number (Lebensnummer).
@@ -30,7 +30,7 @@ interface HorseRepository {
* @param lebensnummer The life number to search for
* @return The horse if found, null otherwise
*/
suspend fun findByLebensnummer(lebensnummer: String): DomPferd?
suspend fun findByLebensnummer(lebensnummer: String): Pferd?
/**
* Finds a horse by its chip number.
@@ -38,7 +38,7 @@ interface HorseRepository {
* @param chipNummer The chip number to search for
* @return The horse if found, null otherwise
*/
suspend fun findByChipNummer(chipNummer: String): DomPferd?
suspend fun findByChipNummer(chipNummer: String): Pferd?
/**
* Finds a horse by its passport number.
@@ -46,7 +46,7 @@ interface HorseRepository {
* @param passNummer The passport number to search for
* @return The horse if found, null otherwise
*/
suspend fun findByPassNummer(passNummer: String): DomPferd?
suspend fun findByPassNummer(passNummer: String): Pferd?
/**
* Finds a horse by its OEPS number.
@@ -54,7 +54,7 @@ interface HorseRepository {
* @param oepsNummer The OEPS number to search for
* @return The horse if found, null otherwise
*/
suspend fun findByOepsNummer(oepsNummer: String): DomPferd?
suspend fun findByOepsNummer(oepsNummer: String): Pferd?
/**
* Finds a horse by its FEI number.
@@ -62,7 +62,7 @@ interface HorseRepository {
* @param feiNummer The FEI number to search for
* @return The horse if found, null otherwise
*/
suspend fun findByFeiNummer(feiNummer: String): DomPferd?
suspend fun findByFeiNummer(feiNummer: String): Pferd?
/**
* Finds horses by name (partial match).
@@ -71,7 +71,7 @@ interface HorseRepository {
* @param limit Maximum number of results to return
* @return List of matching horses
*/
suspend fun findByName(searchTerm: String, limit: Int = 50): List<DomPferd>
suspend fun findByName(searchTerm: String, limit: Int = 50): List<Pferd>
/**
* Finds all horses owned by a specific person.
@@ -80,7 +80,7 @@ interface HorseRepository {
* @param activeOnly Whether to return only active horses
* @return List of horses owned by the person
*/
suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean = true): List<DomPferd>
suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean = true): List<Pferd>
/**
* Finds all horses for which a person is responsible.
@@ -89,7 +89,7 @@ interface HorseRepository {
* @param activeOnly Whether to return only active horses
* @return List of horses for which the person is responsible
*/
suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean = true): List<DomPferd>
suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean = true): List<Pferd>
/**
* Finds horses by gender.
@@ -103,7 +103,7 @@ interface HorseRepository {
geschlecht: PferdeGeschlechtE,
activeOnly: Boolean = true,
limit: Int = 100
): List<DomPferd>
): List<Pferd>
/**
* Finds horses by breed.
@@ -113,7 +113,7 @@ interface HorseRepository {
* @param limit Maximum number of results to return
* @return List of horses of the specified breed
*/
suspend fun findByRasse(rasse: String, activeOnly: Boolean = true, limit: Int = 100): List<DomPferd>
suspend fun findByRasse(rasse: String, activeOnly: Boolean = true, limit: Int = 100): List<Pferd>
/**
* Finds horses by birth year.
@@ -122,7 +122,7 @@ interface HorseRepository {
* @param activeOnly Whether to return only active horses
* @return List of horses born in the specified year
*/
suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean = true): List<DomPferd>
suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean = true): List<Pferd>
/**
* Finds horses by birth year range.
@@ -132,7 +132,7 @@ interface HorseRepository {
* @param activeOnly Whether to return only active horses
* @return List of horses born within the specified year range
*/
suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean = true): List<DomPferd>
suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean = true): List<Pferd>
/**
* Finds all active horses.
@@ -140,7 +140,7 @@ interface HorseRepository {
* @param limit Maximum number of results to return
* @return List of active horses
*/
suspend fun findAllActive(limit: Int = 1000): List<DomPferd>
suspend fun findAllActive(limit: Int = 1000): List<Pferd>
/**
* Finds horses with OEPS registration.
@@ -148,7 +148,7 @@ interface HorseRepository {
* @param activeOnly Whether to return only active horses
* @return List of OEPS registered horses
*/
suspend fun findOepsRegistered(activeOnly: Boolean = true): List<DomPferd>
suspend fun findOepsRegistered(activeOnly: Boolean = true): List<Pferd>
/**
* Finds horses with FEI registration.
@@ -156,7 +156,7 @@ interface HorseRepository {
* @param activeOnly Whether to return only active horses
* @return List of FEI registered horses
*/
suspend fun findFeiRegistered(activeOnly: Boolean = true): List<DomPferd>
suspend fun findFeiRegistered(activeOnly: Boolean = true): List<Pferd>
/**
* Saves a horse (create or update).
@@ -164,7 +164,7 @@ interface HorseRepository {
* @param horse The horse to save
* @return The saved horse with updated timestamps
*/
suspend fun save(horse: DomPferd): DomPferd
suspend fun save(horse: Pferd): Pferd
/**
* Deletes a horse by ID.
@@ -252,7 +252,7 @@ interface HorseRepository {
* @param kopfnummer The head number to search for
* @return The list of horses found
*/
suspend fun findByKopfnummer(kopfnummer: String): List<DomPferd>
suspend fun findByKopfnummer(kopfnummer: String): List<Pferd>
/**
* Finds a horse by its ZNS satznummer.
@@ -260,17 +260,17 @@ interface HorseRepository {
* @param satznummer The ZNS satznummer to search for
* @return The horse if found, null otherwise
*/
suspend fun findBySatznummer(satznummer: String): DomPferd?
suspend fun findBySatznummer(satznummer: String): Pferd?
/**
* Speichert ein Pferd basierend auf der ZNS satznummer (Upsert).
* Wenn ein Pferd mit der satznummer existiert, wird es aktualisiert, ansonsten neu angelegt.
*/
suspend fun upsertBySatznummer(horse: DomPferd): DomPferd
suspend fun upsertBySatznummer(horse: Pferd): Pferd
/**
* Speichert ein Pferd basierend auf der Lebensnummer (Upsert).
* Wenn ein Pferd mit der Lebensnummer existiert, wird es aktualisiert, ansonsten neu angelegt.
*/
suspend fun upsertByLebensnummer(horse: DomPferd): DomPferd
suspend fun upsertByLebensnummer(horse: Pferd): Pferd
}
@@ -2,11 +2,11 @@
package at.mocode.masterdata.domain.repository
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import kotlin.uuid.Uuid
/**
* Repository-Interface für DomReiter (Reiter) Domain-Operationen.
* Repository-Interface für Reiter (Reiter) Domain-Operationen.
*
* Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit
* von konkreten Implementierungsdetails (Datenbank etc.).
@@ -16,22 +16,22 @@ interface ReiterRepository {
/**
* Sucht einen Reiter anhand seiner eindeutigen ID.
*/
suspend fun findById(id: Uuid): DomReiter?
suspend fun findById(id: Uuid): Reiter?
/**
* Sucht einen Reiter anhand seiner Satznummer (OEPS-Mitgliedsnummer).
*/
suspend fun findBySatznummer(satznummer: String?): DomReiter?
suspend fun findBySatznummer(satznummer: String?): Reiter?
/**
* Gibt alle Reiter zurück (paginiert).
*/
suspend fun findAll(limit: Int = 100, offset: Int = 0): List<DomReiter>
suspend fun findAll(limit: Int = 100, offset: Int = 0): List<Reiter>
/**
* Speichert einen Reiter (Insert oder Update).
*/
suspend fun save(reiter: DomReiter): DomReiter
suspend fun save(reiter: Reiter): Reiter
/**
* Löscht einen Reiter anhand seiner ID.
@@ -54,5 +54,5 @@ interface ReiterRepository {
* Speichert einen Reiter basierend auf der Satznummer (Upsert).
* Wenn ein Reiter mit der Satznummer existiert, wird er aktualisiert, ansonsten neu angelegt.
*/
suspend fun upsertBySatznummer(reiter: DomReiter): DomReiter
suspend fun upsertBySatznummer(reiter: Reiter): Reiter
}
@@ -2,11 +2,11 @@
package at.mocode.masterdata.domain.repository
import at.mocode.masterdata.domain.model.DomVerein
import at.mocode.masterdata.domain.model.Verein
import kotlin.uuid.Uuid
/**
* Repository-Interface für DomVerein (Verein) Domain-Operationen.
* Repository-Interface für Verein (Verein) Domain-Operationen.
*
* Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit
* von konkreten Implementierungsdetails (Datenbank etc.).
@@ -16,42 +16,42 @@ interface VereinRepository {
/**
* Sucht einen Verein anhand seiner eindeutigen ID.
*/
suspend fun findById(id: Uuid): DomVerein?
suspend fun findById(id: Uuid): Verein?
/**
* Sucht einen Verein anhand seiner OEPS-Vereinsnummer.
*/
suspend fun findByVereinsNummer(vereinsNummer: String): DomVerein?
suspend fun findByVereinsNummer(vereinsNummer: String): Verein?
/**
* Sucht Vereine anhand des Namens (Teilübereinstimmung).
*/
suspend fun findByName(searchTerm: String, limit: Int = 50): List<DomVerein>
suspend fun findByName(searchTerm: String, limit: Int = 50): List<Verein>
/**
* Sucht alle Vereine eines Bundeslandes.
*/
suspend fun findByBundesland(bundesland: String, activeOnly: Boolean = true): List<DomVerein>
suspend fun findByBundesland(bundesland: String, activeOnly: Boolean = true): List<Verein>
/**
* Sucht alle Vereine, die als Veranstalter markiert sind.
*/
suspend fun findVeranstalter(activeOnly: Boolean = true): List<DomVerein>
suspend fun findVeranstalter(activeOnly: Boolean = true): List<Verein>
/**
* Gibt alle aktiven Vereine zurück (paginiert).
*/
suspend fun findAllActive(limit: Int = 100, offset: Int = 0): List<DomVerein>
suspend fun findAllActive(limit: Int = 100, offset: Int = 0): List<Verein>
/**
* Gibt alle Vereine zurück (paginiert).
*/
suspend fun findAll(limit: Int = 100, offset: Int = 0): List<DomVerein>
suspend fun findAll(limit: Int = 100, offset: Int = 0): List<Verein>
/**
* Speichert einen Verein (Insert oder Update).
*/
suspend fun save(verein: DomVerein): DomVerein
suspend fun save(verein: Verein): Verein
/**
* Löscht einen Verein anhand seiner ID.
@@ -74,5 +74,5 @@ interface VereinRepository {
* Speichert einen Verein basierend auf der Vereinsnummer (Upsert).
* Wenn ein Verein mit der Nummer existiert, wird er aktualisiert, ansonsten neu angelegt.
*/
suspend fun upsertByVereinsNummer(verein: DomVerein): DomVerein
suspend fun upsertByVereinsNummer(verein: Verein): Verein
}
@@ -1,7 +1,7 @@
package at.mocode.masterdata.domain.service
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Pferd
import at.mocode.masterdata.domain.model.Reiter
/**
* Service zur Prüfung von Abteilungs-Regeln gemäß ÖTO § 39.
@@ -23,8 +23,8 @@ interface AbteilungsRegelService {
* @return Die Abteilungsnummer (1, 2, 3), in die der Teilnehmer fällt.
*/
fun ermittleAbteilungStrukturell(
reiter: DomReiter,
pferd: DomPferd,
reiter: Reiter,
pferd: Pferd,
turnierklasseCode: String,
sparte: at.mocode.core.domain.model.SparteE,
istCNeu: Boolean = false,
@@ -1,9 +1,9 @@
package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Pferd
import at.mocode.masterdata.domain.model.Reiter
/**
* Standard-Implementierung des [AbteilungsRegelService] gemäß ÖTO § 39.
@@ -11,8 +11,8 @@ import at.mocode.masterdata.domain.model.DomReiter
class AbteilungsRegelServiceImpl : AbteilungsRegelService {
override fun ermittleAbteilungStrukturell(
reiter: DomReiter,
pferd: DomPferd,
reiter: Reiter,
pferd: Pferd,
turnierklasseCode: String,
sparte: SparteE,
istCNeu: Boolean,
@@ -24,13 +24,13 @@ class AbteilungsRegelServiceImpl : AbteilungsRegelService {
if (istCNeu && sparte == SparteE.SPRINGEN) {
if (hoehe != null && hoehe <= 95) {
return when (reiter.lizenzKlasse) {
LizenzKlasseE.LIZENZFREI -> 1
LizenzKlasseE.R1, LizenzKlasseE.RD1 -> 2
ReiterLizenzKlasseE.LIZENZFREI -> 1
ReiterLizenzKlasseE.R1, ReiterLizenzKlasseE.RD1 -> 2
else -> 3 // R2+
}
} else if (hoehe != null && hoehe >= 100) {
return when (reiter.lizenzKlasse) {
LizenzKlasseE.R1, LizenzKlasseE.RD1 -> 1
ReiterLizenzKlasseE.R1, ReiterLizenzKlasseE.RD1 -> 1
else -> 2 // R2+
}
}
@@ -39,7 +39,7 @@ class AbteilungsRegelServiceImpl : AbteilungsRegelService {
// Fall 2: Klassen A & L (Standardregelung § 39 Abs. 1)
if (turnierklasseCode == "A" || turnierklasseCode == "L") {
return when (reiter.lizenzKlasse) {
LizenzKlasseE.R1, LizenzKlasseE.RD1 -> 1 // Abt. 1: R1
ReiterLizenzKlasseE.R1, ReiterLizenzKlasseE.RD1 -> 1 // Abt. 1: R1
else -> 2 // Abt. 2+: R2 und höher
}
}
@@ -2,7 +2,7 @@ package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.AltersklasseDefinition
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import kotlinx.datetime.LocalDate
/**
@@ -30,9 +30,9 @@ interface AltersklasseRechner {
* @return Eine Liste der zutreffenden Altersklassen-Definitionen.
*/
fun ermittleAltersklassen(
reiter: DomReiter,
referenzJahr: Int,
sparte: SparteE? = null,
verfügbareDefinitionen: List<AltersklasseDefinition>
reiter: Reiter,
referenzJahr: Int,
sparte: SparteE? = null,
verfügbareDefinitionen: List<AltersklasseDefinition>
): List<AltersklasseDefinition>
}
@@ -2,7 +2,7 @@ package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.AltersklasseDefinition
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import kotlinx.datetime.LocalDate
/**
@@ -17,10 +17,10 @@ class AltersklasseRechnerImpl : AltersklasseRechner {
}
override fun ermittleAltersklassen(
reiter: DomReiter,
referenzJahr: Int,
sparte: SparteE?,
verfügbareDefinitionen: List<AltersklasseDefinition>
reiter: Reiter,
referenzJahr: Int,
sparte: SparteE?,
verfügbareDefinitionen: List<AltersklasseDefinition>
): List<AltersklasseDefinition> {
val geburtsdatum = reiter.geburtsdatum ?: return emptyList()
val alter = berechneOetoAlter(geburtsdatum, referenzJahr)
@@ -1,7 +1,7 @@
package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.model.LicenseMatrixEntry
import at.mocode.masterdata.domain.model.TurnierklasseDefinition
@@ -21,7 +21,7 @@ interface LicenseMatrixService {
* @return true, wenn der Reiter startberechtigt ist, sonst false.
*/
fun isEligible(
reiter: DomReiter,
reiter: Reiter,
turnierklasse: TurnierklasseDefinition,
sparte: SparteE,
matrix: List<LicenseMatrixEntry>,
@@ -37,7 +37,7 @@ interface LicenseMatrixService {
* @return Der Code der maximal erlaubten Turnierklasse oder null, wenn keine Regel gefunden wurde.
*/
fun getMaxTurnierklasse(
reiter: DomReiter,
reiter: Reiter,
sparte: SparteE,
matrix: List<LicenseMatrixEntry>
): String?
@@ -1,7 +1,7 @@
package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.model.LicenseMatrixEntry
import at.mocode.masterdata.domain.model.TurnierklasseDefinition
@@ -13,7 +13,7 @@ class LicenseMatrixServiceImpl : LicenseMatrixService {
private val classHierarchy = listOf("E", "A", "L", "LM", "M", "S")
override fun isEligible(
reiter: DomReiter,
reiter: Reiter,
turnierklasse: TurnierklasseDefinition,
sparte: SparteE,
matrix: List<LicenseMatrixEntry>,
@@ -35,7 +35,7 @@ class LicenseMatrixServiceImpl : LicenseMatrixService {
}
override fun getMaxTurnierklasse(
reiter: DomReiter,
reiter: Reiter,
sparte: SparteE,
matrix: List<LicenseMatrixEntry>
): String? {
@@ -2,11 +2,11 @@
package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Pferd
import at.mocode.masterdata.domain.model.Reiter
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -17,24 +17,24 @@ class AbteilungsRegelServiceTest {
private val service = AbteilungsRegelServiceImpl()
private val standardPferd = DomPferd(pferdeName = "Testpferd", geschlecht = PferdeGeschlechtE.WALLACH)
private val standardPferd = Pferd(pferdeName = "Testpferd", geschlecht = PferdeGeschlechtE.WALLACH)
private val dummyPersonId = Uuid.random()
@Test
fun `ermittleAbteilungStrukturell teilt Klassen A und L nach R1`() {
val r1Reiter = DomReiter(
val r1Reiter = Reiter(
personId = dummyPersonId,
satznummer = "1",
nachname = "R1",
vorname = "R1",
lizenzKlasse = LizenzKlasseE.R1
lizenzKlasse = ReiterLizenzKlasseE.R1
)
val r2Reiter = DomReiter(
val r2Reiter = Reiter(
personId = dummyPersonId,
satznummer = "2",
nachname = "R2",
vorname = "R2",
lizenzKlasse = LizenzKlasseE.R2
lizenzKlasse = ReiterLizenzKlasseE.R2
)
assertEquals(1, service.ermittleAbteilungStrukturell(r1Reiter, standardPferd, "A", SparteE.SPRINGEN))
@@ -46,26 +46,26 @@ class AbteilungsRegelServiceTest {
@Test
fun `ermittleAbteilungStrukturell berücksichtigt C-NEU Regeln`() {
val lfReiter = DomReiter(
val lfReiter = Reiter(
personId = dummyPersonId,
satznummer = "0",
nachname = "LF",
vorname = "LF",
lizenzKlasse = LizenzKlasseE.LIZENZFREI
lizenzKlasse = ReiterLizenzKlasseE.LIZENZFREI
)
val r1Reiter = DomReiter(
val r1Reiter = Reiter(
personId = dummyPersonId,
satznummer = "1",
nachname = "R1",
vorname = "R1",
lizenzKlasse = LizenzKlasseE.R1
lizenzKlasse = ReiterLizenzKlasseE.R1
)
val r2Reiter = DomReiter(
val r2Reiter = Reiter(
personId = dummyPersonId,
satznummer = "2",
nachname = "R2",
vorname = "R2",
lizenzKlasse = LizenzKlasseE.R2
lizenzKlasse = ReiterLizenzKlasseE.R2
)
// Bis 95cm
@@ -4,7 +4,7 @@ package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.AltersklasseDefinition
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import kotlinx.datetime.LocalDate
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -30,7 +30,7 @@ class AltersklasseRechnerTest {
@Test
fun `ermittleAltersklassen findet passende Definitionen`() {
val reiter = DomReiter(
val reiter = Reiter(
personId = Uuid.random(),
satznummer = "123456",
nachname = "Mustermann",
@@ -73,7 +73,7 @@ class AltersklasseRechnerTest {
@Test
fun `ermittleAltersklassen beruecksichtigt SpartenFilter`() {
val reiter = DomReiter(
val reiter = Reiter(
personId = Uuid.random(),
satznummer = "123456",
nachname = "Mustermann",
@@ -2,9 +2,9 @@
package at.mocode.masterdata.domain.service
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.model.LicenseMatrixEntry
import at.mocode.masterdata.domain.model.TurnierklasseDefinition
import kotlin.test.Test
@@ -21,7 +21,7 @@ class LicenseMatrixServiceTest {
private val matrix = listOf(
LicenseMatrixEntry(
sparte = SparteE.SPRINGEN,
lizenzKlasse = LizenzKlasseE.R1,
lizenzKlasse = ReiterLizenzKlasseE.R1,
maxTurnierklasseCode = "L",
validFrom = nun,
createdAt = nun,
@@ -29,7 +29,7 @@ class LicenseMatrixServiceTest {
),
LicenseMatrixEntry(
sparte = SparteE.SPRINGEN,
lizenzKlasse = LizenzKlasseE.R2,
lizenzKlasse = ReiterLizenzKlasseE.R2,
maxTurnierklasseCode = "M",
validFrom = nun,
createdAt = nun,
@@ -37,7 +37,7 @@ class LicenseMatrixServiceTest {
),
LicenseMatrixEntry(
sparte = SparteE.DRESSUR,
lizenzKlasse = LizenzKlasseE.RD1,
lizenzKlasse = ReiterLizenzKlasseE.RD1,
maxTurnierklasseCode = "L",
validFrom = nun,
createdAt = nun,
@@ -90,12 +90,12 @@ class LicenseMatrixServiceTest {
@Test
fun `isEligible erlaubt Starts bis zum Limit`() {
val r1Reiter = DomReiter(
val r1Reiter = Reiter(
personId = Uuid.random(),
satznummer = "1",
nachname = "R1",
vorname = "Reiter",
lizenzKlasse = LizenzKlasseE.R1
lizenzKlasse = ReiterLizenzKlasseE.R1
)
val klasseA = turnierklassen.find { it.code == "A" }!!
@@ -109,12 +109,12 @@ class LicenseMatrixServiceTest {
@Test
fun `isEligible verweigert Start ohne passende Spartenlizenz`() {
val rd1Reiter = DomReiter(
val rd1Reiter = Reiter(
personId = Uuid.random(),
satznummer = "2",
nachname = "RD1",
vorname = "Reiter",
lizenzKlasse = LizenzKlasseE.RD1
lizenzKlasse = ReiterLizenzKlasseE.RD1
)
val klasseA = turnierklassen.find { it.code == "A" }!!
@@ -2,7 +2,7 @@
package at.mocode.masterdata.infrastructure.persistence
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.SparteE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.*
@@ -72,7 +72,7 @@ class ExposedRegulationRepository : RegulationRepository {
private fun ResultRow.toLicenseMatrixEntry() = LicenseMatrixEntry(
licenseId = this[LicenseTable.id],
sparte = SparteE.valueOf(this[LicenseTable.sparte]),
lizenzKlasse = LizenzKlasseE.valueOf(this[LicenseTable.lizenzKlasse]),
lizenzKlasse = ReiterLizenzKlasseE.valueOf(this[LicenseTable.lizenzKlasse]),
maxTurnierklasseCode = this[LicenseTable.maxTurnierklasseCode],
validFrom = this[LicenseTable.validFrom].toKtInstant(),
validTo = this[LicenseTable.validTo]?.toOptionalKtInstant(),
@@ -2,6 +2,7 @@
package at.mocode.masterdata.infrastructure.persistence
import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
import org.jetbrains.exposed.v1.datetime.timestamp
@@ -2,6 +2,8 @@
package at.mocode.masterdata.infrastructure.persistence
import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable
import at.mocode.masterdata.infrastructure.persistence.verein.VereinTable
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
import org.jetbrains.exposed.v1.datetime.timestamp
@@ -1,10 +1,9 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.funktionaer
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.DomFunktionaer
import at.mocode.masterdata.domain.model.Funktionaer
import at.mocode.masterdata.domain.repository.FunktionaerRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.and
@@ -19,24 +18,25 @@ import kotlin.uuid.Uuid
/**
* Exposed-basierte Implementierung des Funktionaer-Repositorys.
*/
class ExposedFunktionaerRepository : FunktionaerRepository {
class FunktionaerExposedRepository : FunktionaerRepository {
private fun rowToDomFunktionaer(row: ResultRow, qualifikationen: List<String> = emptyList()): DomFunktionaer {
return DomFunktionaer(
funktionaerId = row[FunktionaerTable.id],
satzID = row[FunktionaerTable.satzID] ?: "X",
satzNummer = row[FunktionaerTable.satzNummer] ?: 0,
name = row[FunktionaerTable.name],
qualifikationen = qualifikationen,
istAktiv = row[FunktionaerTable.istAktiv],
bemerkungen = row[FunktionaerTable.bemerkungen],
datenQuelle = DatenQuelleE.valueOf(row[FunktionaerTable.datenQuelle]),
createdAt = row[FunktionaerTable.createdAt],
updatedAt = row[FunktionaerTable.updatedAt]
private fun rowToDomFunktionaer(row: ResultRow, qualifikationen: List<String> = emptyList()): Funktionaer {
return Funktionaer(
funktionaerId = row[FunktionaerTable.id],
personId = row[FunktionaerTable.personId],
satzId = row[FunktionaerTable.satzId] ?: "X",
satzNummer = row[FunktionaerTable.satzNummer] ?: 0,
name = row[FunktionaerTable.name],
qualifikationen = qualifikationen,
istAktiv = row[FunktionaerTable.istAktiv],
bemerkungen = row[FunktionaerTable.bemerkungen],
datenQuelle = DatenQuelleE.valueOf(row[FunktionaerTable.datenQuelle]),
createdAt = row[FunktionaerTable.createdAt],
updatedAt = row[FunktionaerTable.updatedAt]
)
}
override suspend fun findById(id: Uuid): DomFunktionaer? = DatabaseFactory.dbQuery {
override suspend fun findById(id: Uuid): Funktionaer? = DatabaseFactory.dbQuery {
val qualifikationen = FunktionaerQualifikationTable
.selectAll().where { FunktionaerQualifikationTable.funktionaerId eq id }
.map { it[FunktionaerQualifikationTable.qualifikation] }
@@ -46,9 +46,9 @@ class ExposedFunktionaerRepository : FunktionaerRepository {
.singleOrNull()
}
override suspend fun findBySatz(satzID: String, satzNummer: Int): DomFunktionaer? = DatabaseFactory.dbQuery {
override suspend fun findBySatz(satzID: String, satzNummer: Int): Funktionaer? = DatabaseFactory.dbQuery {
val row = FunktionaerTable.selectAll()
.where { (FunktionaerTable.satzID eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) }
.where { (FunktionaerTable.satzId eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) }
.singleOrNull() ?: return@dbQuery null
val qualifikationen = FunktionaerQualifikationTable
@@ -58,7 +58,7 @@ class ExposedFunktionaerRepository : FunktionaerRepository {
rowToDomFunktionaer(row, qualifikationen)
}
override suspend fun findAll(limit: Int, offset: Int): List<DomFunktionaer> = DatabaseFactory.dbQuery {
override suspend fun findAll(limit: Int, offset: Int): List<Funktionaer> = DatabaseFactory.dbQuery {
val funktionaere = FunktionaerTable.selectAll()
.limit(limit).offset(offset.toLong())
.toList()
@@ -73,11 +73,12 @@ class ExposedFunktionaerRepository : FunktionaerRepository {
}
}
override suspend fun save(funktionaer: DomFunktionaer): DomFunktionaer = DatabaseFactory.dbQuery {
override suspend fun save(funktionaer: Funktionaer): Funktionaer = DatabaseFactory.dbQuery {
val exists = FunktionaerTable.selectAll().where { FunktionaerTable.id eq funktionaer.funktionaerId }.any()
if (exists) {
FunktionaerTable.update({ FunktionaerTable.id eq funktionaer.funktionaerId }) {
it[satzID] = funktionaer.satzID
it[personId] = funktionaer.personId
it[satzId] = funktionaer.satzId
it[satzNummer] = funktionaer.satzNummer
it[name] = funktionaer.name
it[istAktiv] = funktionaer.istAktiv
@@ -88,7 +89,8 @@ class ExposedFunktionaerRepository : FunktionaerRepository {
} else {
FunktionaerTable.insert {
it[id] = funktionaer.funktionaerId
it[satzID] = funktionaer.satzID
it[personId] = funktionaer.personId
it[satzId] = funktionaer.satzId
it[satzNummer] = funktionaer.satzNummer
it[name] = funktionaer.name
it[istAktiv] = funktionaer.istAktiv
@@ -121,7 +123,7 @@ class ExposedFunktionaerRepository : FunktionaerRepository {
override suspend fun existsBySatz(satzID: String, satzNummer: Int): Boolean = DatabaseFactory.dbQuery {
FunktionaerTable.selectAll()
.where { (FunktionaerTable.satzID eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) }
.where { (FunktionaerTable.satzId eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) }
.any()
}
}
@@ -1,10 +1,11 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.funktionaer
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
import org.jetbrains.exposed.v1.datetime.timestamp
import kotlin.uuid.ExperimentalUuidApi
/**
@@ -12,19 +13,42 @@ import org.jetbrains.exposed.v1.datetime.timestamp
*/
object FunktionaerTable : Table("funktionaer") {
val id = uuid("funktionaer_id")
val satzID = varchar("satz_id", 1).nullable()
val personId = uuid("person_id").nullable()
// === ZNS.zip RICHT01.DAT === ANFANG ===
val satzId = varchar("satz_id", 1)
val satzNummer = integer("satz_nummer").nullable()
val name = varchar("name", 200).nullable()
// === ZNS.zip RICHT01.DAT === ENDE ===
// Kontakt
val imageUrl = varchar("image_url", 255).nullable()
val email = varchar("email", 200).nullable()
val telefon = varchar("telefon", 50).nullable()
val website = varchar("website", 255).nullable()
// Adresse
val strasse = varchar("strasse", 200).nullable()
val hausnummer = varchar("hausnummer", 10).nullable()
val plz = varchar("plz", 10).nullable()
val ort = varchar("ort", 100).nullable()
val bundesland = varchar("bundesland", 100).nullable()
// Status & Verwaltung
val istAktiv = bool("ist_aktiv").default(true)
val bemerkungen = text("bemerkungen").nullable()
val datenQuelle = varchar("daten_quelle", 50)
// Audit
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
override val primaryKey = PrimaryKey(id)
init {
index("idx_funktionaer_satz", isUnique = true, satzID, satzNummer)
index("idx_funktionaer_satz", isUnique = true, satzId, satzNummer)
}
}
@@ -1,11 +1,11 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.pferd
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.model.Pferd
import at.mocode.masterdata.domain.repository.HorseRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
@@ -14,12 +14,13 @@ import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.update
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
class HorseRepositoryImpl : HorseRepository {
class HorseExposedRepository : HorseRepository {
private fun rowToDomPferd(row: ResultRow): DomPferd {
return DomPferd(
private fun rowToPferd(row: ResultRow): Pferd {
return Pferd(
pferdId = row[HorseTable.id],
kopfnummer = row[HorseTable.kopfnummer],
pferdeName = row[HorseTable.pferdeName],
@@ -42,43 +43,43 @@ class HorseRepositoryImpl : HorseRepository {
)
}
override suspend fun findById(id: Uuid): DomPferd? = DatabaseFactory.dbQuery {
override suspend fun findById(id: Uuid): Pferd? = DatabaseFactory.dbQuery {
HorseTable.selectAll().where { HorseTable.id eq id }
.map(::rowToDomPferd)
.map(::rowToPferd)
.singleOrNull()
}
override suspend fun findByLebensnummer(lebensnummer: String): DomPferd? = DatabaseFactory.dbQuery {
override suspend fun findByLebensnummer(lebensnummer: String): Pferd? = DatabaseFactory.dbQuery {
HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }
.map(::rowToDomPferd)
.map(::rowToPferd)
.singleOrNull()
}
override suspend fun findBySatznummer(satznummer: String): DomPferd? = DatabaseFactory.dbQuery {
override suspend fun findBySatznummer(satznummer: String): Pferd? = DatabaseFactory.dbQuery {
HorseTable.selectAll().where { HorseTable.satznummer eq satznummer }
.map(::rowToDomPferd)
.map(::rowToPferd)
.singleOrNull()
}
override suspend fun findByKopfnummer(kopfnummer: String): List<DomPferd> = DatabaseFactory.dbQuery {
override suspend fun findByKopfnummer(kopfnummer: String): List<Pferd> = DatabaseFactory.dbQuery {
HorseTable.selectAll().where { HorseTable.kopfnummer eq kopfnummer }
.map(::rowToDomPferd)
.map(::rowToPferd)
}
override suspend fun findByName(searchTerm: String, limit: Int): List<DomPferd> = DatabaseFactory.dbQuery {
override suspend fun findByName(searchTerm: String, limit: Int): List<Pferd> = DatabaseFactory.dbQuery {
val pattern = "%$searchTerm%"
HorseTable.selectAll().where { HorseTable.pferdeName like pattern }
.limit(limit)
.map(::rowToDomPferd)
.map(::rowToPferd)
}
override suspend fun findAllActive(limit: Int): List<DomPferd> = DatabaseFactory.dbQuery {
override suspend fun findAllActive(limit: Int): List<Pferd> = DatabaseFactory.dbQuery {
HorseTable.selectAll().where { HorseTable.istAktiv eq true }
.limit(limit)
.map(::rowToDomPferd)
.map(::rowToPferd)
}
override suspend fun save(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery {
override suspend fun save(horse: Pferd): Pferd = DatabaseFactory.dbQuery {
val exists = HorseTable.selectAll().where { HorseTable.id eq horse.pferdId }.any()
if (exists) {
HorseTable.update({ HorseTable.id eq horse.pferdId }) {
@@ -139,11 +140,11 @@ class HorseRepositoryImpl : HorseRepository {
HorseTable.selectAll().where { HorseTable.istAktiv eq true }.count()
}
override suspend fun upsertByLebensnummer(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery {
override suspend fun upsertByLebensnummer(horse: Pferd): Pferd = DatabaseFactory.dbQuery {
val lebensnummer = horse.lebensnummer ?: return@dbQuery save(horse)
val existing = HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }
.map(::rowToDomPferd)
.map(::rowToPferd)
.singleOrNull()
if (existing != null) {
@@ -173,11 +174,11 @@ class HorseRepositoryImpl : HorseRepository {
}
}
override suspend fun upsertBySatznummer(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery {
override suspend fun upsertBySatznummer(horse: Pferd): Pferd = DatabaseFactory.dbQuery {
val satznummer = horse.satznummer ?: return@dbQuery save(horse)
val existing = HorseTable.selectAll().where { HorseTable.satznummer eq satznummer }
.map(::rowToDomPferd)
.map(::rowToPferd)
.singleOrNull()
if (existing != null) {
@@ -207,28 +208,28 @@ class HorseRepositoryImpl : HorseRepository {
}
}
// Not implemented or needed based on current requirements/DomPferd state
override suspend fun findByChipNummer(chipNummer: String): DomPferd? = null
override suspend fun findByPassNummer(passNummer: String): DomPferd? = null
override suspend fun findByOepsNummer(oepsNummer: String): DomPferd? = null
override suspend fun findByFeiNummer(feiNummer: String): DomPferd? = null
override suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean): List<DomPferd> = emptyList()
override suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean): List<DomPferd> =
// Not implemented or needed based on current requirements/Pferd state
override suspend fun findByChipNummer(chipNummer: String): Pferd? = null
override suspend fun findByPassNummer(passNummer: String): Pferd? = null
override suspend fun findByOepsNummer(oepsNummer: String): Pferd? = null
override suspend fun findByFeiNummer(feiNummer: String): Pferd? = null
override suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean): List<Pferd> = emptyList()
override suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean): List<Pferd> =
emptyList()
override suspend fun findByGeschlecht(
geschlecht: PferdeGeschlechtE,
activeOnly: Boolean,
limit: Int
): List<DomPferd> = emptyList()
): List<Pferd> = emptyList()
override suspend fun findByRasse(rasse: String, activeOnly: Boolean, limit: Int): List<DomPferd> = emptyList()
override suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean): List<DomPferd> = emptyList()
override suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean): List<DomPferd> =
override suspend fun findByRasse(rasse: String, activeOnly: Boolean, limit: Int): List<Pferd> = emptyList()
override suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean): List<Pferd> = emptyList()
override suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean): List<Pferd> =
emptyList()
override suspend fun findOepsRegistered(activeOnly: Boolean): List<DomPferd> = emptyList()
override suspend fun findFeiRegistered(activeOnly: Boolean): List<DomPferd> = emptyList()
override suspend fun findOepsRegistered(activeOnly: Boolean): List<Pferd> = emptyList()
override suspend fun findFeiRegistered(activeOnly: Boolean): List<Pferd> = emptyList()
override suspend fun existsByChipNummer(chipNummer: String): Boolean = false
override suspend fun existsByPassNummer(passNummer: String): Boolean = false
override suspend fun existsByOepsNummer(oepsNummer: String): Boolean = false
@@ -1,16 +1,21 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.pferd
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
import org.jetbrains.exposed.v1.datetime.timestamp
import kotlin.uuid.ExperimentalUuidApi
/**
* Exposed-Tabellendefinition für die Pferd-Entität basierend auf PFERDE01.DAT.
*/
object HorseTable : Table("horse") {
val id = uuid("horse_id")
val personId = uuid("person_id").nullable()
// === ZNS.zip PFERDE01.DAT === ANFANG ===
val kopfnummer = varchar("kopfnummer", 4).nullable().index()
val pferdeName = varchar("pferde_name", 200).index()
val lebensnummer = varchar("lebensnummer", 50).nullable().index()
@@ -25,9 +30,14 @@ object HorseTable : Table("horse") {
val feiPass = varchar("fei_pass", 50).nullable()
val satznummer = varchar("satznummer", 10).nullable()
// === ZNS.zip PFERDE01.DAT === ENDE ===
// Status & Verwaltung
val istAktiv = bool("ist_aktiv").default(true)
val bemerkungen = text("bemerkungen").nullable()
val datenQuelle = varchar("daten_quelle", 50)
// Audit
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
@@ -1,24 +1,25 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.reiter
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.repository.ReiterRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.*
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
/**
* Exposed-basierte Implementierung des Reiter-Repositorys.
*/
class ExposedReiterRepository : ReiterRepository {
class ReiterExposedRepository : ReiterRepository {
private fun rowToDomReiter(row: ResultRow): DomReiter {
return DomReiter(
private fun rowToDomReiter(row: ResultRow): Reiter {
return Reiter(
reiterId = row[ReiterTable.id],
personId = row[ReiterTable.personId],
satznummer = row[ReiterTable.satznummer],
@@ -41,7 +42,7 @@ class ExposedReiterRepository : ReiterRepository {
feiId = row[ReiterTable.feiId],
sperrListe = row[ReiterTable.sperrListe],
lizenzInfo = row[ReiterTable.lizenzInfo],
lizenzKlasse = LizenzKlasseE.valueOf(row[ReiterTable.lizenzKlasse]),
lizenzKlasse = ReiterLizenzKlasseE.valueOf(row[ReiterTable.lizenzKlasse]),
istAktiv = row[ReiterTable.istAktiv],
bemerkungen = row[ReiterTable.bemerkungen],
datenQuelle = DatenQuelleE.valueOf(row[ReiterTable.datenQuelle]),
@@ -50,25 +51,25 @@ class ExposedReiterRepository : ReiterRepository {
)
}
override suspend fun findById(id: Uuid): DomReiter? = DatabaseFactory.dbQuery {
override suspend fun findById(id: Uuid): Reiter? = DatabaseFactory.dbQuery {
ReiterTable.selectAll().where { ReiterTable.id eq id }
.map { rowToDomReiter(it) }
.singleOrNull()
}
override suspend fun findBySatznummer(satznummer: String?): DomReiter? = DatabaseFactory.dbQuery {
override suspend fun findBySatznummer(satznummer: String?): Reiter? = DatabaseFactory.dbQuery {
ReiterTable.selectAll().where { ReiterTable.satznummer eq satznummer }
.map { row -> rowToDomReiter(row) }
.singleOrNull()
}
override suspend fun findAll(limit: Int, offset: Int): List<DomReiter> = DatabaseFactory.dbQuery {
override suspend fun findAll(limit: Int, offset: Int): List<Reiter> = DatabaseFactory.dbQuery {
ReiterTable.selectAll()
.limit(limit).offset(offset.toLong())
.map { row -> rowToDomReiter(row) }
}
override suspend fun save(reiter: DomReiter): DomReiter = DatabaseFactory.dbQuery {
override suspend fun save(reiter: Reiter): Reiter = DatabaseFactory.dbQuery {
val exists = ReiterTable.selectAll().where { ReiterTable.id eq reiter.reiterId }.any()
if (exists) {
ReiterTable.update({ ReiterTable.id eq reiter.reiterId }) {
@@ -147,7 +148,7 @@ class ExposedReiterRepository : ReiterRepository {
ReiterTable.selectAll().where { ReiterTable.satznummer eq satznummer }.any()
}
override suspend fun upsertBySatznummer(reiter: DomReiter): DomReiter = DatabaseFactory.dbQuery {
override suspend fun upsertBySatznummer(reiter: Reiter): Reiter = DatabaseFactory.dbQuery {
val existing = findBySatznummer(reiter.satznummer)
if (existing != null) {
@@ -1,19 +1,22 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.reiter
import at.mocode.core.domain.model.LizenzKlasseE
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
import org.jetbrains.exposed.v1.datetime.date
import org.jetbrains.exposed.v1.datetime.timestamp
import kotlin.uuid.ExperimentalUuidApi
/**
* Exposed-Tabellendefinition für die Reiter-Entität.
*/
object ReiterTable : Table("reiter") {
val id = uuid("reiter_id")
val personId = uuid("person_id")
val personId = uuid("person_id").nullable()
// === ZNS.zip LITENZ01.DAT === ANFANG ===
val satznummer = varchar("satznummer", 10).nullable()
val nachname = varchar("nachname", 100)
val vorname = varchar("vorname", 100)
@@ -34,11 +37,28 @@ object ReiterTable : Table("reiter") {
val feiId = varchar("fei_id", 20).nullable()
val sperrListe = varchar("sperr_liste", 50).nullable()
val lizenzInfo = varchar("lizenz_info", 100).nullable()
val lizenzKlasse = varchar("lizenz_klasse", 50).default(LizenzKlasseE.LIZENZFREI.name)
// === ZNS.zip LITENZ01.DAT === ENDE ===
// Kontakt
val imageUrl = varchar("image_url", 255).nullable()
val email = varchar("email", 200).nullable()
val telefon = varchar("telefon", 50).nullable()
val website = varchar("website", 255).nullable()
// Adresse
val strasse = varchar("strasse", 200).nullable()
val hausnummer = varchar("hausnummer", 10).nullable()
val plz = varchar("plz", 10).nullable()
val ort = varchar("ort", 100).nullable()
val bundesland = varchar("bundesland", 100).nullable()
// Status & Verwaltung
val istAktiv = bool("ist_aktiv").default(true)
val bemerkungen = text("bemerkungen").nullable()
val datenQuelle = varchar("daten_quelle", 50)
// Audit
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
@@ -1,102 +1,113 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.verein
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.DomVerein
import at.mocode.masterdata.domain.model.Verein
import at.mocode.masterdata.domain.repository.VereinRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.like
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.jdbc.*
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
/**
* Exposed-basierte Implementierung des Verein-Repositorys.
*/
class ExposedVereinRepository : VereinRepository {
class VereinExposedRepository : VereinRepository {
private fun rowToDomVerein(row: ResultRow): DomVerein {
return DomVerein(
private fun rowToVereinDomain(row: ResultRow): Verein {
return Verein(
vereinId = row[VereinTable.id],
personId = row[VereinTable.personId],
// === ZNS.zip VEREIN01.DAT === ANFANG ===
vereinsNummer = row[VereinTable.vereinsNummer],
name = row[VereinTable.name],
kurzname = row[VereinTable.kurzname],
bundesland = row[VereinTable.bundesland],
ort = row[VereinTable.ort],
plz = row[VereinTable.plz],
strasse = row[VereinTable.strasse],
vereinName = row[VereinTable.vereinName],
// === ZNS.zip VEREIN01.DAT === ENDE ===
// Kontakt
imageUrl = row[VereinTable.imageUrl],
email = row[VereinTable.email],
telefon = row[VereinTable.telefon],
website = row[VereinTable.website],
oepsRegionNummer = row[VereinTable.oepsRegionNummer],
istVeranstalter = row[VereinTable.istVeranstalter],
// Adresse
strasse = row[VereinTable.strasse],
hausnummer = row[VereinTable.hausnummer],
plz = row[VereinTable.plz],
ort = row[VereinTable.ort],
bundesland = row[VereinTable.bundesland],
// Status & Verwaltung
istAktiv = row[VereinTable.istAktiv],
logoUrl = row[VereinTable.logoUrl],
istVeranstalter = row[VereinTable.istVeranstalter],
bemerkungen = row[VereinTable.bemerkungen],
datenQuelle = DatenQuelleE.valueOf(row[VereinTable.datenQuelle]),
// Audit
createdAt = row[VereinTable.createdAt],
updatedAt = row[VereinTable.updatedAt]
)
}
override suspend fun findById(id: Uuid): DomVerein? = DatabaseFactory.dbQuery {
override suspend fun findById(id: Uuid): Verein? = DatabaseFactory.dbQuery {
VereinTable.selectAll().where { VereinTable.id eq id }
.map(::rowToDomVerein)
.map(::rowToVereinDomain)
.singleOrNull()
}
override suspend fun findByVereinsNummer(vereinsNummer: String): DomVerein? = DatabaseFactory.dbQuery {
override suspend fun findByVereinsNummer(vereinsNummer: String): Verein? = DatabaseFactory.dbQuery {
VereinTable.selectAll().where { VereinTable.vereinsNummer eq vereinsNummer }
.map(::rowToDomVerein)
.map(::rowToVereinDomain)
.singleOrNull()
}
override suspend fun findByName(searchTerm: String, limit: Int): List<DomVerein> = DatabaseFactory.dbQuery {
override suspend fun findByName(searchTerm: String, limit: Int): List<Verein> = DatabaseFactory.dbQuery {
val pattern = "%$searchTerm%"
VereinTable.selectAll().where { (VereinTable.name like pattern) or (VereinTable.kurzname like pattern) }
VereinTable.selectAll().where { VereinTable.vereinName like pattern }
.limit(limit)
.map(::rowToDomVerein)
.map(::rowToVereinDomain)
}
override suspend fun findByBundesland(bundesland: String, activeOnly: Boolean): List<DomVerein> =
override suspend fun findByBundesland(bundesland: String, activeOnly: Boolean): List<Verein> =
DatabaseFactory.dbQuery {
val query = VereinTable.selectAll().where { VereinTable.bundesland eq bundesland }
if (activeOnly) {
query.andWhere { VereinTable.istAktiv eq true }
}
query.map(::rowToDomVerein)
query.map(::rowToVereinDomain)
}
override suspend fun findVeranstalter(activeOnly: Boolean): List<DomVerein> = DatabaseFactory.dbQuery {
override suspend fun findVeranstalter(activeOnly: Boolean): List<Verein> = DatabaseFactory.dbQuery {
val query = VereinTable.selectAll().where { VereinTable.istVeranstalter eq true }
if (activeOnly) {
query.andWhere { VereinTable.istAktiv eq true }
}
query.map(::rowToDomVerein)
query.map(::rowToVereinDomain)
}
override suspend fun findAllActive(limit: Int, offset: Int): List<DomVerein> = DatabaseFactory.dbQuery {
override suspend fun findAllActive(limit: Int, offset: Int): List<Verein> = DatabaseFactory.dbQuery {
VereinTable.selectAll().where { VereinTable.istAktiv eq true }
.limit(limit).offset(offset.toLong())
.map(::rowToDomVerein)
.map(::rowToVereinDomain)
}
override suspend fun findAll(limit: Int, offset: Int): List<DomVerein> = DatabaseFactory.dbQuery {
override suspend fun findAll(limit: Int, offset: Int): List<Verein> = DatabaseFactory.dbQuery {
VereinTable.selectAll()
.limit(limit).offset(offset.toLong())
.map(::rowToDomVerein)
.map(::rowToVereinDomain)
}
override suspend fun save(verein: DomVerein): DomVerein = DatabaseFactory.dbQuery {
override suspend fun save(verein: Verein): Verein = DatabaseFactory.dbQuery {
val exists = VereinTable.selectAll().where { VereinTable.id eq verein.vereinId }.any()
if (exists) {
VereinTable.update({ VereinTable.id eq verein.vereinId }) {
it[vereinsNummer] = verein.vereinsNummer
it[name] = verein.name
it[kurzname] = verein.kurzname
it[vereinName] = verein.vereinName
it[bundesland] = verein.bundesland
it[ort] = verein.ort
it[plz] = verein.plz
@@ -104,10 +115,9 @@ class ExposedVereinRepository : VereinRepository {
it[email] = verein.email
it[telefon] = verein.telefon
it[website] = verein.website
it[oepsRegionNummer] = verein.oepsRegionNummer
it[istVeranstalter] = verein.istVeranstalter
it[istAktiv] = verein.istAktiv
it[logoUrl] = verein.logoUrl
it[imageUrl] = verein.imageUrl
it[bemerkungen] = verein.bemerkungen
it[datenQuelle] = verein.datenQuelle.name
it[updatedAt] = verein.updatedAt
@@ -116,9 +126,9 @@ class ExposedVereinRepository : VereinRepository {
} else {
VereinTable.insert {
it[id] = verein.vereinId
it[personId] = verein.personId
it[vereinsNummer] = verein.vereinsNummer
it[name] = verein.name
it[kurzname] = verein.kurzname
it[vereinName] = verein.vereinName
it[bundesland] = verein.bundesland
it[ort] = verein.ort
it[plz] = verein.plz
@@ -126,10 +136,9 @@ class ExposedVereinRepository : VereinRepository {
it[email] = verein.email
it[telefon] = verein.telefon
it[website] = verein.website
it[oepsRegionNummer] = verein.oepsRegionNummer
it[istVeranstalter] = verein.istVeranstalter
it[istAktiv] = verein.istAktiv
it[logoUrl] = verein.logoUrl
it[imageUrl] = verein.imageUrl
it[bemerkungen] = verein.bemerkungen
it[datenQuelle] = verein.datenQuelle.name
it[createdAt] = verein.createdAt
@@ -151,17 +160,16 @@ class ExposedVereinRepository : VereinRepository {
VereinTable.selectAll().where { VereinTable.vereinsNummer eq vereinsNummer }.any()
}
override suspend fun upsertByVereinsNummer(verein: DomVerein): DomVerein = DatabaseFactory.dbQuery {
override suspend fun upsertByVereinsNummer(verein: Verein): Verein = DatabaseFactory.dbQuery {
val existing = VereinTable.selectAll().where { VereinTable.vereinsNummer eq verein.vereinsNummer }
.map(::rowToDomVerein)
.map(::rowToVereinDomain)
.singleOrNull()
if (existing != null) {
val toUpdate = verein.copy(vereinId = existing.vereinId)
VereinTable.update({ VereinTable.id eq existing.vereinId }) {
it[vereinsNummer] = toUpdate.vereinsNummer
it[name] = toUpdate.name
it[kurzname] = toUpdate.kurzname
it[vereinName] = toUpdate.vereinName
it[bundesland] = toUpdate.bundesland
it[ort] = toUpdate.ort
it[plz] = toUpdate.plz
@@ -169,10 +177,9 @@ class ExposedVereinRepository : VereinRepository {
it[email] = toUpdate.email
it[telefon] = toUpdate.telefon
it[website] = toUpdate.website
it[oepsRegionNummer] = toUpdate.oepsRegionNummer
it[istVeranstalter] = toUpdate.istVeranstalter
it[istAktiv] = toUpdate.istAktiv
it[logoUrl] = toUpdate.logoUrl
it[imageUrl] = toUpdate.imageUrl
it[bemerkungen] = toUpdate.bemerkungen
it[datenQuelle] = toUpdate.datenQuelle.name
it[updatedAt] = toUpdate.updatedAt
@@ -1,32 +1,47 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
@file:OptIn(ExperimentalUuidApi::class)
package at.mocode.masterdata.infrastructure.persistence
package at.mocode.masterdata.infrastructure.persistence.verein
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
import org.jetbrains.exposed.v1.datetime.timestamp
import kotlin.uuid.ExperimentalUuidApi
/**
* Exposed-Tabellendefinition für die Verein-Entität.
*/
object VereinTable : Table("verein") {
val id = uuid("verein_id")
val personId = uuid("person_id").nullable()
// === ZNS.zip VEREIN01.DAT === ANFANG ===
val vereinsNummer = varchar("vereins_nummer", 10).uniqueIndex()
val name = varchar("name", 200)
val kurzname = varchar("kurzname", 100).nullable()
val bundesland = varchar("bundesland", 100).nullable()
val ort = varchar("ort", 100).nullable()
val plz = varchar("plz", 10).nullable()
val strasse = varchar("strasse", 200).nullable()
val vereinName = varchar("verein_name", 200)
// === ZNS.zip VEREIN01.DAT === ENDE ===
// Kontakt
val imageUrl = varchar("image_url", 255).nullable()
val email = varchar("email", 200).nullable()
val telefon = varchar("telefon", 50).nullable()
val website = varchar("website", 255).nullable()
val oepsRegionNummer = varchar("oeps_region_nummer", 10).nullable()
val istVeranstalter = bool("ist_veranstalter").default(false)
// Adresse
val strasse = varchar("strasse", 200).nullable()
val hausnummer = varchar("hausnummer", 10).nullable()
val plz = varchar("plz", 10).nullable()
val ort = varchar("ort", 100).nullable()
val bundesland = varchar("bundesland", 100).nullable()
// Status & Verwaltung
val istAktiv = bool("ist_aktiv").default(true)
val logoUrl = varchar("logo_url", 255).nullable()
val istVeranstalter = bool("ist_veranstalter").default(false)
val bemerkungen = text("bemerkungen").nullable()
val datenQuelle = varchar("daten_quelle", 50)
// Audit
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
@@ -2,7 +2,7 @@
package at.mocode.masterdata.infrastructure.persistence
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.SparteE
import at.mocode.masterdata.domain.model.LicenseMatrixEntry
import at.mocode.masterdata.domain.model.TurnierklasseDefinition
@@ -77,7 +77,7 @@ class RegulationSeedVerificationTest {
val oetoMatrix = listOf(
LicenseMatrixEntry(
sparte = SparteE.SPRINGEN,
lizenzKlasse = LizenzKlasseE.R1,
lizenzKlasse = ReiterLizenzKlasseE.R1,
maxTurnierklasseCode = "L",
validFrom = now,
createdAt = now,
@@ -85,7 +85,7 @@ class RegulationSeedVerificationTest {
),
LicenseMatrixEntry(
sparte = SparteE.SPRINGEN,
lizenzKlasse = LizenzKlasseE.R2,
lizenzKlasse = ReiterLizenzKlasseE.R2,
maxTurnierklasseCode = "M",
validFrom = now,
createdAt = now,
@@ -93,12 +93,12 @@ class RegulationSeedVerificationTest {
)
)
val r1Reiter = at.mocode.masterdata.domain.model.DomReiter(
val r1Reiter = at.mocode.masterdata.domain.model.Reiter(
personId = Uuid.random(),
satznummer = "123456",
nachname = "Müller",
vorname = "Hans",
lizenzKlasse = LizenzKlasseE.R1
lizenzKlasse = ReiterLizenzKlasseE.R1
)
val klasseL = TurnierklasseDefinition(
@@ -4,6 +4,10 @@ import at.mocode.masterdata.api.rest.*
import at.mocode.masterdata.application.usecase.*
import at.mocode.masterdata.domain.repository.*
import at.mocode.masterdata.infrastructure.persistence.*
import at.mocode.masterdata.infrastructure.persistence.funktionaer.FunktionaerExposedRepository
import at.mocode.masterdata.infrastructure.persistence.pferd.HorseExposedRepository
import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterExposedRepository
import at.mocode.masterdata.infrastructure.persistence.verein.VereinExposedRepository
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
@@ -40,22 +44,22 @@ class MasterdataConfiguration {
@Bean
fun reiterRepository(): ReiterRepository {
return ExposedReiterRepository()
return ReiterExposedRepository()
}
@Bean
fun vereinRepository(): VereinRepository {
return ExposedVereinRepository()
return VereinExposedRepository()
}
@Bean
fun horseRepository(): HorseRepository {
return HorseRepositoryImpl()
return HorseExposedRepository()
}
@Bean
fun funktionaerRepository(): FunktionaerRepository {
return ExposedFunktionaerRepository()
return FunktionaerExposedRepository()
}
@Bean
@@ -2,6 +2,10 @@ package at.mocode.masterdata.service.config
import at.mocode.masterdata.infrastructure.persistence.*
import at.mocode.masterdata.infrastructure.persistence.funktionaer.FunktionaerTable
import at.mocode.masterdata.infrastructure.persistence.pferd.HorseTable
import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable
import at.mocode.masterdata.infrastructure.persistence.verein.VereinTable
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
@@ -1,6 +1,6 @@
-- 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
-- Lizenz-Keys entsprechen ReiterLizenzKlasseE-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)
@@ -1,6 +1,6 @@
-- 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
-- Lizenz-Keys entsprechen ReiterLizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3
-- Status: DRAFT wird auf STABLE angehoben nach Fachfreigabe durch ÖTO-Fachreferat
-- ─────────────────────────────────────────────────────────────────────────────