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
@@ -2,7 +2,7 @@
package at.mocode.entries.api
import at.mocode.core.domain.model.NennungsStatusE
import at.mocode.core.domain.model.NennStatusE
import at.mocode.core.domain.model.StartwunschE
import at.mocode.core.domain.serialization.UuidSerializer
import kotlinx.serialization.Serializable
@@ -13,28 +13,28 @@ import kotlin.uuid.Uuid
*/
@Serializable
data class NennungDetailDto(
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val nennungId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val abteilungId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val bewerbId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val turnierId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val reiterId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val pferdId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val zahlerId: Uuid? = null,
val status: NennungsStatusE,
val startwunsch: StartwunschE,
val istNachnennung: Boolean,
val nachnenngebuehrErlassen: Boolean,
val isNachnenngebuehrFaellig: Boolean,
val bemerkungen: String? = null,
val createdAt: String,
val updatedAt: String
val status: NennStatusE,
val startwunsch: StartwunschE,
val istNachnennung: Boolean,
val nachnenngebuehrErlassen: Boolean,
val isNachnenngebuehrFaellig: Boolean,
val bemerkungen: String? = null,
val createdAt: String,
val updatedAt: String
)
/**
@@ -42,21 +42,21 @@ data class NennungDetailDto(
*/
@Serializable
data class NennungSummaryDto(
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val nennungId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val turnierId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val bewerbId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val abteilungId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val reiterId: Uuid,
@Serializable(with = UuidSerializer::class)
@Serializable(with = UuidSerializer::class)
val pferdId: Uuid,
val status: NennungsStatusE,
val istNachnennung: Boolean,
val createdAt: String
val status: NennStatusE,
val istNachnennung: Boolean,
val createdAt: String
)
/**
@@ -86,8 +86,8 @@ data class NennungEinreichenRequest(
*/
@Serializable
data class NennungStatusAendernRequest(
val neuerStatus: NennungsStatusE,
val bemerkungen: String? = null
val neuerStatus: NennStatusE,
val bemerkungen: String? = null
)
/**
@@ -2,7 +2,7 @@
package at.mocode.entries.domain.model
import at.mocode.core.domain.model.NennungsStatusE
import at.mocode.core.domain.model.NennStatusE
import at.mocode.core.domain.model.StartwunschE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.UuidSerializer
@@ -28,8 +28,8 @@ import kotlin.uuid.Uuid
* @property abteilungId Reference to the Abteilung (smallest entry unit).
* @property bewerbId Reference to the parent Bewerb (for display/reporting).
* @property turnierId Reference to the Turnier.
* @property reiterId Reference to the DomReiter (actor-context).
* @property pferdId Reference to the DomPferd (actor-context).
* @property reiterId Reference to the Reiter (actor-context).
* @property pferdId Reference to the Pferd (actor-context).
* @property zahlerId Reference to the payer (may differ from rider, e.g. club pays).
* @property status Current status of this entry.
* @property startwunsch Rider's preferred starting position (vorne/hinten).
@@ -63,7 +63,7 @@ data class DomNennung(
val zahlerId: Uuid? = null,
// Entry Details
val status: NennungsStatusE = NennungsStatusE.EINGEGANGEN,
val status: NennStatusE = NennStatusE.EINGEGANGEN,
val startwunsch: StartwunschE = StartwunschE.KEIN_WUNSCH,
// Late Entry (Nachnennung)
@@ -83,8 +83,8 @@ data class DomNennung(
* Checks if this entry is still active (not withdrawn or cancelled).
*/
fun isAktiv(): Boolean = status !in listOf(
NennungsStatusE.ZURUECKGEZOGEN,
NennungsStatusE.NICHT_ANGETRETEN
NennStatusE.ZURUECKGEZOGEN,
NennStatusE.NICHT_ANGETRETEN
)
/**
@@ -2,7 +2,7 @@
package at.mocode.entries.domain.repository
import at.mocode.core.domain.model.NennungsStatusE
import at.mocode.core.domain.model.NennStatusE
import at.mocode.entries.domain.model.DomNennung
import kotlin.uuid.Uuid
@@ -52,7 +52,7 @@ interface NennungRepository {
/**
* Sucht alle Nennungen mit einem bestimmten Status.
*/
suspend fun findByStatus(status: NennungsStatusE): List<DomNennung>
suspend fun findByStatus(status: NennStatusE): List<DomNennung>
/**
* Sucht alle Nachnennungen für einen Bewerb.
@@ -84,5 +84,5 @@ interface NennungRepository {
/**
* Zählt alle Nennungen für ein Turnier mit einem bestimmten Status.
*/
suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennungsStatusE): Long
suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennStatusE): Long
}
@@ -3,12 +3,12 @@
package at.mocode.entries.domain.service
import at.mocode.core.domain.model.AbteilungsTeilungsTypE
import at.mocode.core.domain.model.LizenzKlasseE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.PruefungsTypE
import at.mocode.core.domain.model.SparteE
import at.mocode.entries.domain.model.DomAbteilung
import at.mocode.entries.domain.model.DomBewerb
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
/**
* Service für die Anwendung der Abteilungs-Regeln gemäß ÖTO § 39.
@@ -37,14 +37,14 @@ class AbteilungsRegelService {
fun bestimmeAbteilung(
bewerb: DomBewerb,
abteilungen: List<DomAbteilung>,
reiter: DomReiter
reiter: Reiter
): DomAbteilung? {
if (abteilungen.isEmpty()) return null
if (abteilungen.size == 1) return abteilungen.first()
return when (bewerb.teilungsTyp) {
AbteilungsTeilungsTypE.NACH_LIZENZ -> {
val istLizenzfrei = reiter.lizenzKlasse == LizenzKlasseE.LIZENZFREI
val istLizenzfrei = reiter.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI
if (istLizenzfrei) {
// Suche Abteilung für Lizenzfreie (oft Abt. 1 oder explizit benannt)
abteilungen.find { it.bezeichnung?.contains("frei", ignoreCase = true) == true }
@@ -61,7 +61,7 @@ class AbteilungsRegelService {
AbteilungsTeilungsTypE.STRUKTURELL -> {
// Bei strukturellen Teilungen (z.B. Caprilli oder CSN-C-NEU)
if (bewerb.pruefungsTyp == PruefungsTypE.CAPRILLI) {
val istLizenzfrei = reiter.lizenzKlasse == LizenzKlasseE.LIZENZFREI
val istLizenzfrei = reiter.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI
if (istLizenzfrei) abteilungen.find { it.abteilungsNummer == 1 }
else abteilungen.find { it.abteilungsNummer == 2 }
} else if (bewerb.sparte == SparteE.SPRINGEN && bewerb.hoeheCm != null) {
@@ -70,7 +70,7 @@ class AbteilungsRegelService {
// ≥ 100 cm: nur R1+ erlaubt → alle in Abt. 1 (R1/R2+)
val hoehe = bewerb.hoeheCm!!
if (hoehe <= 95) {
val istLizenzfrei = reiter.lizenzKlasse == LizenzKlasseE.LIZENZFREI
val istLizenzfrei = reiter.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI
if (istLizenzfrei) {
abteilungen.find { it.bezeichnung?.contains("ohne", ignoreCase = true) == true }
?: abteilungen.minByOrNull { it.abteilungsNummer }
@@ -80,7 +80,7 @@ class AbteilungsRegelService {
}
} else {
// ≥ 100 cm: Pflicht-Teilung R1 / R2+
val istR1 = reiter.lizenzKlasse == LizenzKlasseE.R1
val istR1 = reiter.lizenzKlasse == ReiterLizenzKlasseE.R1
if (istR1) {
abteilungen.find { it.bezeichnung?.contains("R1", ignoreCase = false) == true }
?: abteilungen.minByOrNull { it.abteilungsNummer }
@@ -5,7 +5,7 @@ package at.mocode.entries.domain.service
import at.mocode.core.domain.model.*
import at.mocode.entries.domain.model.DomAbteilung
import at.mocode.entries.domain.model.DomBewerb
import at.mocode.masterdata.domain.model.DomReiter
import at.mocode.masterdata.domain.model.Reiter
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.uuid.Uuid
@@ -30,7 +30,7 @@ class AbteilungsRegelServiceTest {
val abt1 = createAbteilung(bewerb.bewerbId, 1, "lizenzfrei")
val abt2 = createAbteilung(bewerb.bewerbId, 2, "R1 und hoeher")
val reiter = createReiter(lizenzKlasse = LizenzKlasseE.LIZENZFREI)
val reiter = createReiter(lizenzKlasse = ReiterLizenzKlasseE.LIZENZFREI)
val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), reiter)
@@ -43,7 +43,7 @@ class AbteilungsRegelServiceTest {
val abt1 = createAbteilung(bewerb.bewerbId, 1, "lizenzfrei")
val abt2 = createAbteilung(bewerb.bewerbId, 2, "Lizenz")
val reiter = createReiter(lizenzKlasse = LizenzKlasseE.R1)
val reiter = createReiter(lizenzKlasse = ReiterLizenzKlasseE.R1)
val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), reiter)
@@ -68,7 +68,7 @@ class AbteilungsRegelServiceTest {
val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 95)
val abtOhne = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz")
val abtMit = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz")
val reiter = createReiter(LizenzKlasseE.LIZENZFREI)
val reiter = createReiter(ReiterLizenzKlasseE.LIZENZFREI)
val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), reiter)
@@ -80,7 +80,7 @@ class AbteilungsRegelServiceTest {
val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 95)
val abtOhne = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz")
val abtMit = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz")
val reiter = createReiter(LizenzKlasseE.R1)
val reiter = createReiter(ReiterLizenzKlasseE.R1)
val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), reiter)
@@ -93,7 +93,7 @@ class AbteilungsRegelServiceTest {
val abtOhne = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz")
val abtMit = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz")
val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), createReiter(LizenzKlasseE.LIZENZFREI))
val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), createReiter(ReiterLizenzKlasseE.LIZENZFREI))
assertEquals(abtOhne.abteilungId, result?.abteilungId)
}
@@ -105,7 +105,7 @@ class AbteilungsRegelServiceTest {
val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 100)
val abtR1 = createAbteilung(bewerb.bewerbId, 1, "R1")
val abtR2 = createAbteilung(bewerb.bewerbId, 2, "R2+")
val reiter = createReiter(LizenzKlasseE.R1)
val reiter = createReiter(ReiterLizenzKlasseE.R1)
val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), reiter)
@@ -117,7 +117,7 @@ class AbteilungsRegelServiceTest {
val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 100)
val abtR1 = createAbteilung(bewerb.bewerbId, 1, "R1")
val abtR2 = createAbteilung(bewerb.bewerbId, 2, "R2+")
val reiter = createReiter(LizenzKlasseE.R2)
val reiter = createReiter(ReiterLizenzKlasseE.R2)
val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), reiter)
@@ -130,7 +130,7 @@ class AbteilungsRegelServiceTest {
val abtR1 = createAbteilung(bewerb.bewerbId, 1, "R1")
val abtR2 = createAbteilung(bewerb.bewerbId, 2, "R2+")
val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), createReiter(LizenzKlasseE.R1))
val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), createReiter(ReiterLizenzKlasseE.R1))
assertEquals(abtR1.abteilungId, result?.abteilungId)
}
@@ -191,7 +191,7 @@ class AbteilungsRegelServiceTest {
val abt1 = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz")
val abt2 = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz")
val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(LizenzKlasseE.LIZENZFREI))
val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(ReiterLizenzKlasseE.LIZENZFREI))
assertEquals(abt1.abteilungId, result?.abteilungId)
}
@@ -202,7 +202,7 @@ class AbteilungsRegelServiceTest {
val abt1 = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz")
val abt2 = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz")
val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(LizenzKlasseE.R1))
val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(ReiterLizenzKlasseE.R1))
assertEquals(abt2.abteilungId, result?.abteilungId)
}
@@ -236,7 +236,7 @@ class AbteilungsRegelServiceTest {
starterAnzahl = starterAnzahl
)
private fun createReiter(lizenzKlasse: LizenzKlasseE = LizenzKlasseE.LIZENZFREI) = DomReiter(
private fun createReiter(lizenzKlasse: ReiterLizenzKlasseE = ReiterLizenzKlasseE.LIZENZFREI) = Reiter(
personId = Uuid.random(),
satznummer = "123456",
nachname = "Mustermann",
@@ -2,7 +2,7 @@
package at.mocode.entries.service.persistence
import at.mocode.core.domain.model.NennungsStatusE
import at.mocode.core.domain.model.NennStatusE
import at.mocode.core.domain.model.StartwunschE
import at.mocode.entries.domain.model.DomNennung
import at.mocode.entries.domain.repository.NennungRepository
@@ -32,7 +32,7 @@ class NennungRepositoryImpl : NennungRepository {
reiterId = row[NennungTable.reiterId].toKotlinUuid(),
pferdId = row[NennungTable.pferdId].toKotlinUuid(),
zahlerId = row[NennungTable.zahlerId]?.toKotlinUuid(),
status = NennungsStatusE.valueOf(row[NennungTable.status]),
status = NennStatusE.valueOf(row[NennungTable.status]),
startwunsch = StartwunschE.valueOf(row[NennungTable.startwunsch]),
istNachnennung = row[NennungTable.istNachnennung],
nachnenngebuehrErlassen = row[NennungTable.nachnenngebuehrErlassen],
@@ -79,7 +79,7 @@ class NennungRepositoryImpl : NennungRepository {
}.map(::rowToNennung)
}
override suspend fun findByStatus(status: NennungsStatusE): List<DomNennung> = tenantTransaction {
override suspend fun findByStatus(status: NennStatusE): List<DomNennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.status eq status.name }
.map(::rowToNennung)
}
@@ -145,7 +145,7 @@ class NennungRepositoryImpl : NennungRepository {
NennungTable.selectAll().where { NennungTable.abteilungId eq abteilungId.toJavaUuid() }.count()
}
override suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennungsStatusE): Long = tenantTransaction {
override suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennStatusE): Long = tenantTransaction {
NennungTable.selectAll().where {
(NennungTable.turnierId eq turnierId.toJavaUuid()) and
(NennungTable.status eq status.name)
@@ -2,7 +2,7 @@
package at.mocode.entries.service.usecase
import at.mocode.core.domain.model.NennungsStatusE
import at.mocode.core.domain.model.NennStatusE
import at.mocode.entries.api.*
import at.mocode.entries.domain.model.DomNennung
import at.mocode.entries.domain.model.DomNennungsTransfer
@@ -101,7 +101,7 @@ class NennungUseCases(
val nennung = nennungRepository.findById(nennungId)
?: throw NoSuchElementException("Nennung nicht gefunden: $nennungId")
val updated = nennung.copy(status = NennungsStatusE.ZURUECKGEZOGEN).withUpdatedTimestamp()
val updated = nennung.copy(status = NennStatusE.ZURUECKGEZOGEN).withUpdatedTimestamp()
val saved = nennungRepository.save(updated)
log.info("Nennung zurückgezogen: nennungId={}", nennungId)
return saved.toDetailDto()
@@ -131,7 +131,7 @@ class NennungUseCases(
}
// 1. Ursprungs-Nennung schließen
val geschlosseneNennung = ursprung.copy(status = NennungsStatusE.TRANSFERIERT).withUpdatedTimestamp()
val geschlosseneNennung = ursprung.copy(status = NennStatusE.TRANSFERIERT).withUpdatedTimestamp()
nennungRepository.save(geschlosseneNennung)
// 2. Neue Nennung anlegen
@@ -151,7 +151,7 @@ private fun DomNennung.Companion.random(now: kotlin.time.Instant): DomNennung {
reiterId = Uuid.random(),
pferdId = Uuid.random(),
zahlerId = null,
status = at.mocode.core.domain.model.NennungsStatusE.EINGEGANGEN,
status = at.mocode.core.domain.model.NennStatusE.EINGEGANGEN,
startwunsch = at.mocode.core.domain.model.StartwunschE.VORNE,
istNachnennung = false,
nachnenngebuehrErlassen = false,