feat(masterdata): add controllers, services, and repositories for Reiter, Horse, and Verein domains

- Introduced entities `ReiterController`, `HorseController`, and `VereinController`, with associated REST routes.
- Implemented upsert functionality for `Reiter`, `Horse`, and `Verein` repositories.
- Added services for `Altersklasse` calculations and integrated them into the domain layer.
- Updated database schema to include `ReiterTable`, `HorseTable`, `VereinTable`, and `FunktionaerTable`.
- Refactored `masterdataApiModule` to register new domain controllers.
- Adjusted Ktor server and Spring configurations to support new domains.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
Stefan Mogeritsch 2026-03-30 13:16:55 +02:00
parent c576bbd6af
commit 0c870ba2e3
15 changed files with 533 additions and 33 deletions

View File

@ -1,24 +1,24 @@
package at.mocode.masterdata.api
import at.mocode.masterdata.api.plugins.IdempotencyPlugin
import at.mocode.masterdata.api.rest.AltersklasseController
import at.mocode.masterdata.api.rest.BundeslandController
import at.mocode.masterdata.api.rest.CountryController
import at.mocode.masterdata.api.rest.PlatzController
import io.ktor.server.application.Application
import io.ktor.server.routing.routing
import at.mocode.masterdata.api.rest.*
import io.ktor.server.application.*
import io.ktor.server.routing.*
/**
* Ktor-Modul für den Masterdata-Bounded-Context.
*
* - Installiert das IdempotencyPlugin (Header Idempotency-Key) global.
* - Registriert alle Masterdata-Routen (Country, Bundesland, Altersklasse, Platz).
* - Registriert alle Masterdata-Routen (Country, Bundesland, Altersklasse, Platz, Reiter, Horse, Verein).
*/
fun Application.masterdataApiModule(
countryController: CountryController,
bundeslandController: BundeslandController,
altersklasseController: AltersklasseController,
platzController: PlatzController
platzController: PlatzController,
reiterController: ReiterController,
horseController: HorseController,
vereinController: VereinController
) {
// Installiere das Idempotency-Plugin global für alle Routen
IdempotencyPlugin.install(this)
@ -29,5 +29,8 @@ fun Application.masterdataApiModule(
with(bundeslandController) { registerRoutes() }
with(altersklasseController) { registerRoutes() }
with(platzController) { registerRoutes() }
with(reiterController) { registerRoutes() }
with(horseController) { registerRoutes() }
with(vereinController) { registerRoutes() }
}
}

View File

@ -0,0 +1,95 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.masterdata.api.rest
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.LocalDateSerializer
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.masterdata.domain.repository.HorseRepository
import io.ktor.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
import kotlin.time.Instant
import kotlin.uuid.Uuid
/**
* Controller für Pferde-bezogene REST-Endpunkte.
*/
class HorseController(private val horseRepository: HorseRepository) {
@Serializable
data class HorseDto(
val pferdId: String,
val pferdeName: String,
val geschlecht: String,
@Serializable(with = LocalDateSerializer::class)
val geburtsdatum: LocalDate? = null,
val rasse: String? = null,
val lebensnummer: String? = null,
val oepsNummer: String? = null,
val feiNummer: String? = null,
val istAktiv: Boolean,
@Serializable(with = InstantSerializer::class)
val updatedAt: Instant
)
fun Route.registerRoutes() {
route("/horse") {
/**
* Sucht Pferde nach Name.
*/
get("/search") {
val query = call.request.queryParameters["q"] ?: ""
val results = horseRepository.findByName(query)
call.respond(results.map { it.toDto() })
}
/**
* Ruft ein spezifisches Pferd ab.
*/
get("/{id}") {
val idStr = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
val id = try {
Uuid.parse(idStr)
} catch (e: Exception) {
return@get call.respond(HttpStatusCode.BadRequest)
}
val pferd = horseRepository.findById(id)
if (pferd != null) {
call.respond(pferd.toDto())
} else {
call.respond(HttpStatusCode.NotFound)
}
}
/**
* Sucht ein Pferd nach seiner Lebensnummer.
*/
get("/lebensnummer/{nr}") {
val nr = call.parameters["nr"] ?: return@get call.respond(HttpStatusCode.BadRequest)
val pferd = horseRepository.findByLebensnummer(nr)
if (pferd != null) {
call.respond(pferd.toDto())
} else {
call.respond(HttpStatusCode.NotFound)
}
}
}
}
private fun DomPferd.toDto() = HorseDto(
pferdId = pferdId.toString(),
pferdeName = pferdeName,
geschlecht = geschlecht.name,
geburtsdatum = geburtsdatum,
rasse = rasse,
lebensnummer = lebensnummer,
oepsNummer = oepsNummer,
feiNummer = feiNummer,
istAktiv = istAktiv,
updatedAt = updatedAt
)
}

View File

@ -0,0 +1,97 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.masterdata.api.rest
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.repository.ReiterRepository
import io.ktor.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
import kotlin.time.Instant
import kotlin.uuid.Uuid
/**
* Controller für Reiter-bezogene REST-Endpunkte.
*/
class ReiterController(private val reiterRepository: ReiterRepository) {
@Serializable
data class ReiterDto(
val reiterId: String,
val satznummer: String,
val nachname: String,
val vorname: String,
@Serializable(with = LocalDateSerializer::class)
val geburtsdatum: LocalDate? = null,
val lizenzNummer: String? = null,
val lizenzKlasse: String,
val startkartAktiv: Boolean,
val nation: String? = null,
val vereinsName: String? = null,
@Serializable(with = InstantSerializer::class)
val updatedAt: Instant
)
fun Route.registerRoutes() {
route("/reiter") {
/**
* Sucht Reiter nach Name oder Satznummer.
*/
get("/search") {
val query = call.request.queryParameters["q"] ?: ""
val results = reiterRepository.findByName(query)
call.respond(results.map { it.toDto() })
}
/**
* Ruft einen spezifischen Reiter ab.
*/
get("/{id}") {
val idStr = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
val id = try {
Uuid.parse(idStr)
} catch (e: Exception) {
return@get call.respond(HttpStatusCode.BadRequest)
}
val reiter = reiterRepository.findById(id)
if (reiter != null) {
call.respond(reiter.toDto())
} else {
call.respond(HttpStatusCode.NotFound)
}
}
/**
* Sucht einen Reiter nach seiner Satznummer.
*/
get("/satznummer/{nr}") {
val nr = call.parameters["nr"] ?: return@get call.respond(HttpStatusCode.BadRequest)
val reiter = reiterRepository.findBySatznummer(nr)
if (reiter != null) {
call.respond(reiter.toDto())
} else {
call.respond(HttpStatusCode.NotFound)
}
}
}
}
private fun DomReiter.toDto() = ReiterDto(
reiterId = reiterId.toString(),
satznummer = satznummer,
nachname = nachname,
vorname = vorname,
geburtsdatum = geburtsdatum,
lizenzNummer = lizenzNummer,
lizenzKlasse = lizenzKlasse.name,
startkartAktiv = startkartAktiv,
nation = nation,
vereinsName = vereinsName,
updatedAt = updatedAt
)
}

View File

@ -0,0 +1,89 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
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.repository.VereinRepository
import io.ktor.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import kotlin.uuid.Uuid
/**
* Controller für Vereins-bezogene REST-Endpunkte.
*/
class VereinController(private val vereinRepository: VereinRepository) {
@Serializable
data class VereinDto(
val vereinId: String,
val vereinsNummer: String,
val name: String,
val kurzname: String? = null,
val bundesland: String,
val ort: String? = null,
val istVeranstalter: Boolean,
val istAktiv: Boolean,
@Serializable(with = InstantSerializer::class)
val updatedAt: kotlin.time.Instant
)
fun Route.registerRoutes() {
route("/verein") {
/**
* Sucht Vereine nach Name oder Kurzname.
*/
get("/search") {
val query = call.request.queryParameters["q"] ?: ""
val results = vereinRepository.findByName(query)
call.respond(results.map { it.toDto() })
}
/**
* Ruft einen spezifischen Verein ab.
*/
get("/{id}") {
val idStr = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
val id = try {
Uuid.parse(idStr)
} catch (e: Exception) {
return@get call.respond(HttpStatusCode.BadRequest)
}
val verein = vereinRepository.findById(id)
if (verein != null) {
call.respond(verein.toDto())
} else {
call.respond(HttpStatusCode.NotFound)
}
}
/**
* Sucht einen Verein nach seiner Vereinsnummer.
*/
get("/nummer/{nr}") {
val nr = call.parameters["nr"] ?: return@get call.respond(HttpStatusCode.BadRequest)
val verein = vereinRepository.findByVereinsNummer(nr)
if (verein != null) {
call.respond(verein.toDto())
} else {
call.respond(HttpStatusCode.NotFound)
}
}
}
}
private fun DomVerein.toDto() = VereinDto(
vereinId = vereinId.toString(),
vereinsNummer = vereinsNummer,
name = name,
kurzname = kurzname,
bundesland = bundesland ?: "",
ort = ort,
istVeranstalter = istVeranstalter,
istAktiv = istAktiv,
updatedAt = updatedAt
)
}

View File

@ -2,8 +2,8 @@
package at.mocode.masterdata.domain.repository
import at.mocode.masterdata.domain.model.DomPferd
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.masterdata.domain.model.DomPferd
import kotlin.uuid.Uuid
/**
@ -245,4 +245,10 @@ interface HorseRepository {
* @return The count of FEI registered horses
*/
suspend fun countFeiRegistered(activeOnly: Boolean = true): Long
/**
* 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
}

View File

@ -86,4 +86,10 @@ interface ReiterRepository {
* Prüft ob ein Reiter mit der gegebenen Satznummer bereits existiert.
*/
suspend fun existsBySatznummer(satznummer: String): Boolean
/**
* 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
}

View File

@ -69,4 +69,10 @@ interface VereinRepository {
* Prüft ob ein Verein mit der gegebenen Vereinsnummer bereits existiert.
*/
suspend fun existsByVereinsNummer(vereinsNummer: String): Boolean
/**
* 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
}

View File

@ -0,0 +1,38 @@
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 kotlinx.datetime.LocalDate
/**
* Service zur Berechnung und Ermittlung von Altersklassen gemäß ÖTO.
*/
interface AltersklasseRechner {
/**
* Ermittelt das Alter einer Person für ein bestimmtes Jahr gemäß der ÖTO-Stichtagsregel
* (Alter am 31.12. des laufenden Kalenderjahres).
*
* @param geburtsdatum Das Geburtsdatum der Person.
* @param referenzJahr Das Kalenderjahr, für das das Alter berechnet werden soll.
* @return Das Alter in Jahren.
*/
fun berechneOetoAlter(geburtsdatum: LocalDate, referenzJahr: Int): Int
/**
* Ermittelt alle zutreffenden Altersklassen für einen Reiter in einem bestimmten Jahr und einer Sparte.
*
* @param reiter Der Reiter, für den die Altersklasse ermittelt werden soll.
* @param referenzJahr Das Kalenderjahr des Turniers.
* @param sparte Die Sparte des Bewerbs (optional).
* @param verfügbareDefinitionen Die Liste der im System definierten Altersklassen.
* @return Eine Liste der zutreffenden Altersklassen-Definitionen.
*/
fun ermittleAltersklassen(
reiter: DomReiter,
referenzJahr: Int,
sparte: SparteE? = null,
verfügbareDefinitionen: List<AltersklasseDefinition>
): List<AltersklasseDefinition>
}

View File

@ -0,0 +1,43 @@
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 kotlinx.datetime.LocalDate
/**
* Standard-Implementierung des [AltersklasseRechner] gemäß ÖTO.
*/
class AltersklasseRechnerImpl : AltersklasseRechner {
override fun berechneOetoAlter(geburtsdatum: LocalDate, referenzJahr: Int): Int {
// Gemäß ÖTO: Stichtag für alle Altersklassen ist der 31. Dezember des laufenden Kalenderjahres.
// Das bedeutet einfach: ReferenzJahr - GeburtsJahr.
return referenzJahr - geburtsdatum.year
}
override fun ermittleAltersklassen(
reiter: DomReiter,
referenzJahr: Int,
sparte: SparteE?,
verfügbareDefinitionen: List<AltersklasseDefinition>
): List<AltersklasseDefinition> {
val geburtsdatum = reiter.geburtsdatum ?: return emptyList()
val alter = berechneOetoAlter(geburtsdatum, referenzJahr)
return verfügbareDefinitionen.filter { def ->
if (!def.istAktiv) return@filter false
// Sparte prüfen (falls in der Definition eine Sparte vorgegeben ist)
if (def.sparteFilter != null && sparte != null && def.sparteFilter != sparte) {
return@filter false
}
// Alter prüfen
val minMatch = def.minAlter == null || alter >= def.minAlter!!
val maxMatch = def.maxAlter == null || alter <= def.maxAlter!!
minMatch && maxMatch
}
}
}

View File

@ -174,4 +174,35 @@ class ExposedReiterRepository : ReiterRepository {
override suspend fun existsBySatznummer(satznummer: String): Boolean = DatabaseFactory.dbQuery {
ReiterTable.selectAll().where { ReiterTable.satznummer eq satznummer }.any()
}
override suspend fun upsertBySatznummer(reiter: DomReiter): DomReiter = DatabaseFactory.dbQuery {
val existing = ReiterTable.selectAll().where { ReiterTable.satznummer eq reiter.satznummer }
.map(::rowToDomReiter)
.singleOrNull()
if (existing != null) {
val toUpdate = reiter.copy(reiterId = existing.reiterId)
ReiterTable.update({ ReiterTable.id eq existing.reiterId }) {
it[personId] = toUpdate.personId
it[nachname] = toUpdate.nachname
it[vorname] = toUpdate.vorname
it[geburtsdatum] = toUpdate.geburtsdatum
it[lizenzNummer] = toUpdate.lizenzNummer
it[lizenzKlasse] = toUpdate.lizenzKlasse.name
it[startkartAktiv] = toUpdate.startkartAktiv
it[startkartSaison] = toUpdate.startkartSaison
it[feiId] = toUpdate.feiId
it[nation] = toUpdate.nation
it[vereinsNummer] = toUpdate.vereinsNummer
it[vereinsName] = toUpdate.vereinsName
it[istGastreiter] = toUpdate.istGastreiter
it[istAktiv] = toUpdate.istAktiv
it[datenQuelle] = toUpdate.datenQuelle.name
it[updatedAt] = toUpdate.updatedAt
}
toUpdate
} else {
save(reiter)
}
}
}

View File

@ -7,14 +7,10 @@ import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.DomVerein
import at.mocode.masterdata.domain.repository.VereinRepository
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.or
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 org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.like
import org.jetbrains.exposed.v1.jdbc.andWhere
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.jdbc.*
import kotlin.uuid.Uuid
/**
@ -151,4 +147,35 @@ class ExposedVereinRepository : VereinRepository {
override suspend fun existsByVereinsNummer(vereinsNummer: String): Boolean = DatabaseFactory.dbQuery {
VereinTable.selectAll().where { VereinTable.vereinsNummer eq vereinsNummer }.any()
}
override suspend fun upsertByVereinsNummer(verein: DomVerein): DomVerein = DatabaseFactory.dbQuery {
val existing = VereinTable.selectAll().where { VereinTable.vereinsNummer eq verein.vereinsNummer }
.map(::rowToDomVerein)
.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[bundesland] = toUpdate.bundesland
it[ort] = toUpdate.ort
it[plz] = toUpdate.plz
it[strasse] = toUpdate.strasse
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[bemerkungen] = toUpdate.bemerkungen
it[datenQuelle] = toUpdate.datenQuelle.name
it[updatedAt] = toUpdate.updatedAt
}
toUpdate
} else {
save(verein)
}
}
}

View File

@ -283,4 +283,43 @@ class HorseRepositoryImpl : HorseRepository {
}
query.count()
}
override suspend fun upsertByLebensnummer(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery {
val lebensnummer = horse.lebensnummer ?: return@dbQuery save(horse)
val existing = HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer }
.map(::rowToDomPferd)
.singleOrNull()
if (existing != null) {
val toUpdate = horse.copy(pferdId = existing.pferdId)
HorseTable.update({ HorseTable.id eq existing.pferdId }) {
it[pferdeName] = toUpdate.pferdeName
it[geschlecht] = toUpdate.geschlecht.name
it[geburtsdatum] = toUpdate.geburtsdatum
it[rasse] = toUpdate.rasse
it[farbe] = toUpdate.farbe
it[besitzerId] = toUpdate.besitzerId
it[verantwortlichePersonId] = toUpdate.verantwortlichePersonId
it[zuechterName] = toUpdate.zuechterName
it[zuchtbuchNummer] = toUpdate.zuchtbuchNummer
it[lebensnummer] = toUpdate.lebensnummer
it[chipNummer] = toUpdate.chipNummer
it[passNummer] = toUpdate.passNummer
it[oepsNummer] = toUpdate.oepsNummer
it[feiNummer] = toUpdate.feiNummer
it[vaterName] = toUpdate.vaterName
it[mutterName] = toUpdate.mutterName
it[mutterVaterName] = toUpdate.mutterVaterName
it[stockmass] = toUpdate.stockmass
it[istAktiv] = toUpdate.istAktiv
it[bemerkungen] = toUpdate.bemerkungen
it[datenQuelle] = toUpdate.datenQuelle.name
it[updatedAt] = toUpdate.updatedAt
}
toUpdate
} else {
save(horse)
}
}
}

View File

@ -1,16 +1,10 @@
package at.mocode.masterdata.service.config
import at.mocode.masterdata.api.masterdataApiModule
import at.mocode.masterdata.api.rest.AltersklasseController
import at.mocode.masterdata.api.rest.BundeslandController
import at.mocode.masterdata.api.rest.CountryController
import at.mocode.masterdata.api.rest.PlatzController
import io.ktor.server.engine.embeddedServer
import io.ktor.server.engine.EmbeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import at.mocode.masterdata.api.rest.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.DisposableBean
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@ -33,7 +27,10 @@ class KtorServerConfiguration {
countryController: CountryController,
bundeslandController: BundeslandController,
altersklasseController: AltersklasseController,
platzController: PlatzController
platzController: PlatzController,
reiterController: ReiterController,
horseController: HorseController,
vereinController: VereinController
): EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine.Configuration> {
log.info("Starting Masterdata Ktor server on port {}", port)
val engine = embeddedServer(Netty, port = port) {
@ -41,7 +38,10 @@ class KtorServerConfiguration {
countryController = countryController,
bundeslandController = bundeslandController,
altersklasseController = altersklasseController,
platzController = platzController
platzController = platzController,
reiterController = reiterController,
horseController = horseController,
vereinController = vereinController
)
}
engine.start(wait = false)

View File

@ -1,9 +1,9 @@
package at.mocode.masterdata.service.config
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.api.rest.*
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
@ -134,6 +134,21 @@ class MasterdataConfiguration {
): PlatzController {
return PlatzController(getPlatzUseCase, createPlatzUseCase)
}
@Bean
fun reiterController(reiterRepository: ReiterRepository): ReiterController {
return ReiterController(reiterRepository)
}
@Bean
fun horseController(horseRepository: HorseRepository): HorseController {
return HorseController(horseRepository)
}
@Bean
fun vereinController(vereinRepository: VereinRepository): VereinController {
return VereinController(vereinRepository)
}
}
/**

View File

@ -1,10 +1,7 @@
package at.mocode.masterdata.service.config
import at.mocode.masterdata.infrastructure.persistence.AltersklasseTable
import at.mocode.masterdata.infrastructure.persistence.BundeslandTable
import at.mocode.masterdata.infrastructure.persistence.LandTable
import at.mocode.masterdata.infrastructure.persistence.PlatzTable
import at.mocode.masterdata.infrastructure.persistence.*
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
@ -36,7 +33,11 @@ class MasterdataDatabaseConfiguration {
LandTable,
BundeslandTable,
AltersklasseTable,
PlatzTable
PlatzTable,
ReiterTable,
HorseTable,
VereinTable,
FunktionaerTable
)
log.info("Masterdata database schema initialized successfully")
}
@ -72,7 +73,11 @@ class MasterdataTestDatabaseConfiguration {
LandTable,
BundeslandTable,
AltersklasseTable,
PlatzTable
PlatzTable,
ReiterTable,
HorseTable,
VereinTable,
FunktionaerTable
)
log.info("Test masterdata database schema initialized successfully")
}