Remove Dom prefix from domain models: delete DomVeranstaltung, rename models like DomNennung to Nennung, update references in repositories, services, and tests for consistency.

This commit is contained in:
2026-04-08 22:59:12 +02:00
parent 085656a85b
commit 2d42578378
13 changed files with 71 additions and 218 deletions
@@ -13,7 +13,7 @@ import kotlin.uuid.Uuid
/**
* Domain-Modell für eine Abteilung im registration-context.
*
* Eine Abteilung ist die kleinste startbare Einheit innerhalb eines [DomBewerb]s.
* Eine Abteilung ist die kleinste startbare Einheit innerhalb eines [Bewerb]s.
* Ein Bewerb kann in mehrere Abteilungen aufgeteilt sein (z.B. Abt. 1: ohne Lizenz,
* Abt. 2: mit Lizenz R1). Die Aufteilung erfolgt gemäß ÖTO § 39 und den
* spartenspezifischen Bestimmungen.
@@ -32,7 +32,7 @@ import kotlin.uuid.Uuid
* @property updatedAt Letzter Änderungszeitpunkt.
*/
@Serializable
data class DomAbteilung(
data class Abteilung(
@Serializable(with = UuidSerializer::class)
val abteilungId: Uuid = Uuid.random(),
@@ -106,5 +106,5 @@ data class DomAbteilung(
/**
* Erstellt eine Kopie mit aktualisiertem Zeitstempel.
*/
fun withUpdatedTimestamp(): DomAbteilung = this.copy(updatedAt = Clock.System.now())
fun withUpdatedTimestamp(): Abteilung = this.copy(updatedAt = Clock.System.now())
}
@@ -13,7 +13,7 @@ import kotlin.uuid.Uuid
/**
* Domain-Modell für eine Startliste im registration-context.
*
* Eine Startliste gehört zu einer [DomAbteilung] und enthält die geordnete Liste
* Eine Startliste gehört zu einer [Abteilung] und enthält die geordnete Liste
* der Starter (Nennungen) mit ihren Startnummern. Sie durchläuft einen definierten
* Workflow: NICHT_ERSTELLT → ENTWURF → VEROEFFENTLICHT → GESPERRT → ARCHIVIERT.
*
@@ -128,7 +128,7 @@ data class DomStartliste(
/**
* Ein einzelner Eintrag in einer Startliste.
*
* Verbindet eine Startnummer mit einer Nennung ([DomNennung]).
* Verbindet eine Startnummer mit einer Nennung ([Nennung]).
*
* @property startnummer Die zugewiesene Startnummer (Kopfnummer gemäß Ubiquitous Language).
* @property nennungId Referenz auf die zugehörige Nennung (UUID).
@@ -40,7 +40,7 @@ import kotlin.uuid.Uuid
* @property updatedAt Timestamp when this entry was last updated.
*/
@Serializable
data class DomNennung(
data class Nennung(
@Serializable(with = UuidSerializer::class)
val nennungId: Uuid = Uuid.random(),
@@ -95,5 +95,5 @@ data class DomNennung(
/**
* Creates a copy of this entry with an updated timestamp.
*/
fun withUpdatedTimestamp(): DomNennung = this.copy(updatedAt = Clock.System.now())
fun withUpdatedTimestamp(): Nennung = this.copy(updatedAt = Clock.System.now())
}
@@ -40,7 +40,7 @@ import kotlin.uuid.Uuid
* @property createdAt Timestamp when this transfer was recorded.
*/
@Serializable
data class DomNennungsTransfer(
data class NennungsTransfer(
@Serializable(with = UuidSerializer::class)
val transferId: Uuid = Uuid.random(),
@@ -2,23 +2,23 @@
package at.mocode.entries.domain.repository
import at.mocode.entries.domain.model.DomAbteilung
import at.mocode.entries.domain.model.DomBewerb
import at.mocode.entries.domain.model.Abteilung
import at.mocode.entries.domain.model.Bewerb
import kotlin.uuid.Uuid
/**
* Repository-Interface für DomBewerb und DomAbteilung Domain-Operationen.
* Repository-Interface für Bewerb und Abteilung Domain-Operationen.
*/
interface CompetitionRepository {
// Bewerbe
suspend fun findBewerbById(id: Uuid): DomBewerb?
suspend fun findBewerbeByTurnierId(turnierId: Uuid): List<DomBewerb>
suspend fun saveBewerb(bewerb: DomBewerb): DomBewerb
suspend fun findBewerbById(id: Uuid): Bewerb?
suspend fun findBewerbeByTurnierId(turnierId: Uuid): List<Bewerb>
suspend fun saveBewerb(bewerb: Bewerb): Bewerb
suspend fun deleteBewerb(id: Uuid): Boolean
// Abteilungen
suspend fun findAbteilungById(id: Uuid): DomAbteilung?
suspend fun findAbteilungenByBewerbId(bewerbId: Uuid): List<DomAbteilung>
suspend fun saveAbteilung(abteilung: DomAbteilung): DomAbteilung
suspend fun findAbteilungById(id: Uuid): Abteilung?
suspend fun findAbteilungenByBewerbId(bewerbId: Uuid): List<Abteilung>
suspend fun saveAbteilung(abteilung: Abteilung): Abteilung
suspend fun deleteAbteilung(id: Uuid): Boolean
}
@@ -3,11 +3,11 @@
package at.mocode.entries.domain.repository
import at.mocode.core.domain.model.NennStatusE
import at.mocode.entries.domain.model.DomNennung
import at.mocode.entries.domain.model.Nennung
import kotlin.uuid.Uuid
/**
* Repository-Interface für DomNennung (Nennung) Domain-Operationen.
* Repository-Interface für Nennung (Nennung) Domain-Operationen.
*
* Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit
* von konkreten Implementierungsdetails (Datenbank, etc.).
@@ -17,52 +17,52 @@ interface NennungRepository {
/**
* Sucht eine Nennung anhand ihrer eindeutigen ID.
*/
suspend fun findById(id: Uuid): DomNennung?
suspend fun findById(id: Uuid): Nennung?
/**
* Sucht alle Nennungen für einen bestimmten Bewerb.
*/
suspend fun findByBewerbId(bewerbId: Uuid): List<DomNennung>
suspend fun findByBewerbId(bewerbId: Uuid): List<Nennung>
/**
* Sucht alle Nennungen für eine bestimmte Abteilung.
*/
suspend fun findByAbteilungId(abteilungId: Uuid): List<DomNennung>
suspend fun findByAbteilungId(abteilungId: Uuid): List<Nennung>
/**
* Sucht alle Nennungen für ein bestimmtes Turnier.
*/
suspend fun findByTurnierId(turnierId: Uuid): List<DomNennung>
suspend fun findByTurnierId(turnierId: Uuid): List<Nennung>
/**
* Sucht alle Nennungen eines bestimmten Reiters.
*/
suspend fun findByReiterId(reiterId: Uuid): List<DomNennung>
suspend fun findByReiterId(reiterId: Uuid): List<Nennung>
/**
* Sucht alle Nennungen für ein bestimmtes Pferd.
*/
suspend fun findByPferdId(pferdId: Uuid): List<DomNennung>
suspend fun findByPferdId(pferdId: Uuid): List<Nennung>
/**
* Sucht alle Nennungen eines Reiters für ein bestimmtes Turnier.
*/
suspend fun findByReiterIdAndTurnierId(reiterId: Uuid, turnierId: Uuid): List<DomNennung>
suspend fun findByReiterIdAndTurnierId(reiterId: Uuid, turnierId: Uuid): List<Nennung>
/**
* Sucht alle Nennungen mit einem bestimmten Status.
*/
suspend fun findByStatus(status: NennStatusE): List<DomNennung>
suspend fun findByStatus(status: NennStatusE): List<Nennung>
/**
* Sucht alle Nachnennungen für einen Bewerb.
*/
suspend fun findNachnennungenByBewerbId(bewerbId: Uuid): List<DomNennung>
suspend fun findNachnennungenByBewerbId(bewerbId: Uuid): List<Nennung>
/**
* Speichert eine Nennung (Insert oder Update).
*/
suspend fun save(nennung: DomNennung): DomNennung
suspend fun save(nennung: Nennung): Nennung
/**
* Löscht eine Nennung anhand ihrer ID.
@@ -2,14 +2,14 @@
package at.mocode.entries.domain.repository
import at.mocode.entries.domain.model.DomNennungsTransfer
import at.mocode.entries.domain.model.NennungsTransfer
import kotlin.uuid.Uuid
/**
* Repository-Interface für DomNennungsTransfer Domain-Operationen.
* Repository-Interface für NennungsTransfer Domain-Operationen.
*/
interface NennungsTransferRepository {
suspend fun findById(id: Uuid): DomNennungsTransfer?
suspend fun findByUrsprungsNennungId(nennungId: Uuid): List<DomNennungsTransfer>
suspend fun save(transfer: DomNennungsTransfer): DomNennungsTransfer
suspend fun findById(id: Uuid): NennungsTransfer?
suspend fun findByUrsprungsNennungId(nennungId: Uuid): List<NennungsTransfer>
suspend fun save(transfer: NennungsTransfer): NennungsTransfer
}
@@ -6,8 +6,8 @@ import at.mocode.core.domain.model.AbteilungsTeilungsTypE
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.entries.domain.model.Abteilung
import at.mocode.entries.domain.model.Bewerb
import at.mocode.masterdata.domain.model.Reiter
/**
@@ -32,13 +32,13 @@ class AbteilungsRegelService {
* @param bewerb Der betroffene Bewerb.
* @param abteilungen Liste der verfügbaren Abteilungen des Bewerbs.
* @param reiter Der Reiter, der genannt werden soll.
* @return Die passende [DomAbteilung] oder null, wenn keine Zuordnung eindeutig möglich ist.
* @return Die passende [Abteilung] oder null, wenn keine Zuordnung eindeutig möglich ist.
*/
fun bestimmeAbteilung(
bewerb: DomBewerb,
abteilungen: List<DomAbteilung>,
bewerb: Bewerb,
abteilungen: List<Abteilung>,
reiter: Reiter
): DomAbteilung? {
): Abteilung? {
if (abteilungen.isEmpty()) return null
if (abteilungen.size == 1) return abteilungen.first()
@@ -115,8 +115,8 @@ class AbteilungsRegelService {
* Beispiel CSN-C-NEU: Ein Bewerb muss zwingend eine Abteilung für lizenzfreie Reiter haben.
*/
fun validateStrukturelleVollstaendigkeit(
bewerb: DomBewerb,
abteilungen: List<DomAbteilung>
bewerb: Bewerb,
abteilungen: List<Abteilung>
): List<String> {
val warnings = mutableListOf<String>()
@@ -3,8 +3,8 @@
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.entries.domain.model.Abteilung
import at.mocode.entries.domain.model.Bewerb
import at.mocode.masterdata.domain.model.Reiter
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -213,7 +213,7 @@ class AbteilungsRegelServiceTest {
pruefungsTyp: PruefungsTypE = PruefungsTypE.SPRINGEN_UEBRIG,
teilungsTyp: AbteilungsTeilungsTypE = AbteilungsTeilungsTypE.KEINE,
hoeheCm: Int? = null
) = DomBewerb(
) = Bewerb(
turnierId = Uuid.random(),
bewerbNummer = 1,
bezeichnung = "Testbewerb",
@@ -229,7 +229,7 @@ class AbteilungsRegelServiceTest {
nummer: Int,
bezeichnung: String? = null,
starterAnzahl: Int = 0
) = DomAbteilung(
) = Abteilung(
bewerbId = bewerbId,
abteilungsNummer = nummer,
bezeichnung = bezeichnung,
@@ -4,7 +4,7 @@ package at.mocode.entries.service.persistence
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.model.Nennung
import at.mocode.entries.domain.repository.NennungRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.and
@@ -24,7 +24,7 @@ import kotlin.uuid.toKotlinUuid
*/
class NennungRepositoryImpl : NennungRepository {
private fun rowToNennung(row: ResultRow): DomNennung = DomNennung(
private fun rowToNennung(row: ResultRow): Nennung = Nennung(
nennungId = row[NennungTable.id].toKotlinUuid(),
abteilungId = row[NennungTable.abteilungId].toKotlinUuid(),
bewerbId = row[NennungTable.bewerbId].toKotlinUuid(),
@@ -41,57 +41,57 @@ class NennungRepositoryImpl : NennungRepository {
updatedAt = row[NennungTable.updatedAt]
)
override suspend fun findById(id: Uuid): DomNennung? = tenantTransaction {
override suspend fun findById(id: Uuid): Nennung? = tenantTransaction {
NennungTable.selectAll().where { NennungTable.id eq id.toJavaUuid() }
.map(::rowToNennung)
.singleOrNull()
}
override suspend fun findByBewerbId(bewerbId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findByBewerbId(bewerbId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.bewerbId eq bewerbId.toJavaUuid() }
.map(::rowToNennung)
}
override suspend fun findByAbteilungId(abteilungId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findByAbteilungId(abteilungId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.abteilungId eq abteilungId.toJavaUuid() }
.map(::rowToNennung)
}
override suspend fun findByTurnierId(turnierId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findByTurnierId(turnierId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.turnierId eq turnierId.toJavaUuid() }
.map(::rowToNennung)
}
override suspend fun findByReiterId(reiterId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findByReiterId(reiterId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.reiterId eq reiterId.toJavaUuid() }
.map(::rowToNennung)
}
override suspend fun findByPferdId(pferdId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findByPferdId(pferdId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.pferdId eq pferdId.toJavaUuid() }
.map(::rowToNennung)
}
override suspend fun findByReiterIdAndTurnierId(reiterId: Uuid, turnierId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findByReiterIdAndTurnierId(reiterId: Uuid, turnierId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where {
(NennungTable.reiterId eq reiterId.toJavaUuid()) and
(NennungTable.turnierId eq turnierId.toJavaUuid())
}.map(::rowToNennung)
}
override suspend fun findByStatus(status: NennStatusE): List<DomNennung> = tenantTransaction {
override suspend fun findByStatus(status: NennStatusE): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where { NennungTable.status eq status.name }
.map(::rowToNennung)
}
override suspend fun findNachnennungenByBewerbId(bewerbId: Uuid): List<DomNennung> = tenantTransaction {
override suspend fun findNachnennungenByBewerbId(bewerbId: Uuid): List<Nennung> = tenantTransaction {
NennungTable.selectAll().where {
(NennungTable.bewerbId eq bewerbId.toJavaUuid()) and
(NennungTable.istNachnennung eq true)
}.map(::rowToNennung)
}
override suspend fun save(nennung: DomNennung): DomNennung = tenantTransaction {
override suspend fun save(nennung: Nennung): Nennung = tenantTransaction {
val now = Clock.System.now()
val existing = NennungTable.selectAll()
.where { NennungTable.id eq nennung.nennungId.toJavaUuid() }
@@ -2,7 +2,7 @@
package at.mocode.entries.service.persistence
import at.mocode.entries.domain.model.DomNennungsTransfer
import at.mocode.entries.domain.model.NennungsTransfer
import at.mocode.entries.domain.repository.NennungsTransferRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
@@ -19,7 +19,7 @@ import kotlin.uuid.toKotlinUuid
*/
class NennungsTransferRepositoryImpl : NennungsTransferRepository {
private fun rowToTransfer(row: ResultRow): DomNennungsTransfer = DomNennungsTransfer(
private fun rowToTransfer(row: ResultRow): NennungsTransfer = NennungsTransfer(
transferId = row[NennungsTransferTable.id].toKotlinUuid(),
ursprungsNennungId = row[NennungsTransferTable.ursprungsNennungId].toKotlinUuid(),
neueNennungId = row[NennungsTransferTable.neueNennungId].toKotlinUuid(),
@@ -34,19 +34,19 @@ class NennungsTransferRepositoryImpl : NennungsTransferRepository {
createdAt = row[NennungsTransferTable.createdAt]
)
override suspend fun findById(id: Uuid): DomNennungsTransfer? = tenantTransaction {
override suspend fun findById(id: Uuid): NennungsTransfer? = tenantTransaction {
NennungsTransferTable.selectAll().where { NennungsTransferTable.id eq id.toJavaUuid() }
.map(::rowToTransfer)
.singleOrNull()
}
override suspend fun findByUrsprungsNennungId(nennungId: Uuid): List<DomNennungsTransfer> = tenantTransaction {
override suspend fun findByUrsprungsNennungId(nennungId: Uuid): List<NennungsTransfer> = tenantTransaction {
NennungsTransferTable.selectAll()
.where { NennungsTransferTable.ursprungsNennungId eq nennungId.toJavaUuid() }
.map(::rowToTransfer)
}
override suspend fun save(transfer: DomNennungsTransfer): DomNennungsTransfer = tenantTransaction {
override suspend fun save(transfer: NennungsTransfer): NennungsTransfer = tenantTransaction {
val now = Clock.System.now()
NennungsTransferTable.insert { stmt ->
stmt[id] = transfer.transferId.toJavaUuid()
@@ -4,8 +4,8 @@ package at.mocode.entries.service.usecase
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
import at.mocode.entries.domain.model.Nennung
import at.mocode.entries.domain.model.NennungsTransfer
import at.mocode.entries.domain.repository.NennungRepository
import at.mocode.entries.domain.repository.NennungsTransferRepository
import org.slf4j.LoggerFactory
@@ -61,7 +61,7 @@ class NennungUseCases(
request.turnierId, request.bewerbId, request.reiterId
)
}
val nennung = DomNennung(
val nennung = Nennung(
abteilungId = request.abteilungId,
bewerbId = request.bewerbId,
turnierId = request.turnierId,
@@ -135,7 +135,7 @@ class NennungUseCases(
nennungRepository.save(geschlosseneNennung)
// 2. Neue Nennung anlegen
val neueNennung = DomNennung(
val neueNennung = Nennung(
abteilungId = ursprung.abteilungId,
bewerbId = ursprung.bewerbId,
turnierId = ursprung.turnierId,
@@ -150,7 +150,7 @@ class NennungUseCases(
val gespeicherteNeueNennung = nennungRepository.save(neueNennung)
// 3. Transfer-Record speichern
val transfer = DomNennungsTransfer(
val transfer = NennungsTransfer(
ursprungsNennungId = ursprung.nennungId,
neueNennungId = gespeicherteNeueNennung.nennungId,
alterReiterId = if (request.neuerReiterId != null) ursprung.reiterId else null,
@@ -175,7 +175,7 @@ class NennungUseCases(
// Mapping Helpers
// ---------------------------------------------------------------------------
private fun DomNennung.toDetailDto() = NennungDetailDto(
private fun Nennung.toDetailDto() = NennungDetailDto(
nennungId = nennungId,
abteilungId = abteilungId,
bewerbId = bewerbId,
@@ -193,7 +193,7 @@ class NennungUseCases(
updatedAt = updatedAt.toString()
)
private fun DomNennung.toSummaryDto() = NennungSummaryDto(
private fun Nennung.toSummaryDto() = NennungSummaryDto(
nennungId = nennungId,
turnierId = turnierId,
bewerbId = bewerbId,
@@ -205,7 +205,7 @@ class NennungUseCases(
createdAt = createdAt.toString()
)
private fun DomNennungsTransfer.toDto() = NennungsTransferDto(
private fun NennungsTransfer.toDto() = NennungsTransferDto(
transferId = transferId,
ursprungsNennungId = ursprungsNennungId,
neueNennungId = neueNennungId,
@@ -1,147 +0,0 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.events.domain.model
import at.mocode.core.domain.model.SparteE
import at.mocode.core.domain.model.VeranstaltungsStatusE
import at.mocode.core.domain.model.VeranstaltungsTypE
import at.mocode.core.domain.serialization.KotlinxInstantSerializer
import at.mocode.core.domain.serialization.UuidSerializer
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
import kotlin.time.Clock
import kotlin.time.Instant
import kotlin.uuid.Uuid
/**
* Aggregate Root für eine pferdesportliche Veranstaltung gemäß ÖTO § 2 Abs. 1.
*
* Eine Veranstaltung ist der organisatorische Rahmen (z.B. "Frühjahrsturnier Wiener Neustadt").
* Sie kann ein oder mehrere Turniere enthalten. Die Unterscheidung Veranstaltung ≠ Turnier
* ist eine zentrale ADR-Entscheidung (ADR-0003).
*
* @property veranstaltungId Eindeutige interne ID (UUID).
* @property name Offizieller Name der Veranstaltung.
* @property veranstaltungsTyp Typ gemäß ÖTO (z.B. NATIONAL, INTERNATIONAL).
* @property sparten Liste der enthaltenen Sparten (Springen, Dressur, etc.).
* @property veranstalterVereinId ID des veranstaltenden Vereins (Referenz auf clubs-context).
* @property verantwortlicheFunktionaerId ID des verantwortlichen Funktionärs (Referenz auf officials-context).
* @property startDatum Erster Veranstaltungstag.
* @property endDatum Letzter Veranstaltungstag.
* @property ort Veranstaltungsort (Adresse / Reitanlage).
* @property nennschluss Nennschluss-Datum gemäß Ausschreibung.
* @property status Aktueller Status im Planungs-Workflow.
* @property ausschreibungsId Referenz auf die zugehörige Ausschreibung (optional bis Genehmigung).
* @property oepsGenehmigungsNummer Offizielle Genehmigungsnummer des Verbands (nach Genehmigung).
* @property bemerkungen Interne Bemerkungen.
* @property createdAt Erstellungszeitpunkt.
* @property updatedAt Letzter Änderungszeitpunkt.
*/
@Serializable
data class DomVeranstaltung(
@Serializable(with = UuidSerializer::class)
val veranstaltungId: Uuid = Uuid.random(),
// Basis-Informationen
var name: String,
var veranstaltungsTyp: VeranstaltungsTypE,
var sparten: List<SparteE> = emptyList(),
// Organisation
@Serializable(with = UuidSerializer::class)
var veranstalterVereinId: Uuid,
@Serializable(with = UuidSerializer::class)
var verantwortlicheFunktionaerId: Uuid? = null,
// Termine
var startDatum: LocalDate,
var endDatum: LocalDate,
var ort: String,
var nennschluss: LocalDate? = null,
// Workflow-Status
var status: VeranstaltungsStatusE = VeranstaltungsStatusE.IN_PLANUNG,
// Verknüpfungen
@Serializable(with = UuidSerializer::class)
var ausschreibungsId: Uuid? = null,
var oepsGenehmigungsNummer: String? = null,
// Administrativ
var bemerkungen: String? = null,
// Audit
@Serializable(with = KotlinxInstantSerializer::class)
val createdAt: Instant = Clock.System.now(),
@Serializable(with = KotlinxInstantSerializer::class)
var updatedAt: Instant = Clock.System.now()
) {
/**
* Gibt die Dauer der Veranstaltung in Tagen zurück.
*/
fun getDauerInTagen(): Int =
(endDatum.toEpochDays() - startDatum.toEpochDays()).toInt() + 1
/**
* Prüft ob die Veranstaltung mehrtägig ist.
*/
fun istMehrtaegig(): Boolean = startDatum != endDatum
/**
* Prüft ob Nennungen aktuell möglich sind (Status GENEHMIGT und Nennschluss nicht abgelaufen).
* Gibt Warnungen zurück keine harten Fehler (Warn-Logik statt Exception, ADR-0007).
*/
fun validateNennungsmoeglichkeit(): List<String> {
val warnings = mutableListOf<String>()
if (status != VeranstaltungsStatusE.GENEHMIGT) {
warnings.add(
"Veranstaltung ist nicht im Status GENEHMIGT (aktuell: $status). " +
"Nennungen sind erst nach Genehmigung möglich."
)
}
if (nennschluss == null) {
warnings.add("Kein Nennschluss definiert. Bitte Ausschreibung vervollständigen.")
}
if (veranstalterVereinId == null) {
warnings.add("Kein Veranstalter-Verein zugewiesen.")
}
return warnings
}
/**
* Validiert die Pflichtfelder für die Einreichung beim Verband.
* Gibt Warnungen zurück keine harten Fehler (Warn-Logik statt Exception, ADR-0007).
*/
fun validateFuerEinreichung(): List<String> {
val warnings = mutableListOf<String>()
if (name.isBlank()) {
warnings.add("Veranstaltungsname ist erforderlich.")
}
if (ort.isBlank()) {
warnings.add("Veranstaltungsort ist erforderlich.")
}
if (endDatum < startDatum) {
warnings.add("Enddatum darf nicht vor dem Startdatum liegen.")
}
if (sparten.isEmpty()) {
warnings.add("Mindestens eine Sparte muss angegeben werden.")
}
if (nennschluss == null) {
warnings.add("Nennschluss ist für die Einreichung erforderlich.")
}
nennschluss?.let { nl ->
if (nl >= startDatum) {
warnings.add("Nennschluss muss vor dem Veranstaltungsbeginn liegen (§ 2 ÖTO).")
}
}
if (ausschreibungsId == null) {
warnings.add("Keine Ausschreibung verknüpft. Einreichung ohne Ausschreibung nicht möglich.")
}
return warnings
}
/**
* Erstellt eine Kopie mit aktualisiertem Zeitstempel.
*/
fun withUpdatedTimestamp(): DomVeranstaltung = this.copy(updatedAt = Clock.System.now())
}