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
@@ -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" }!!