Refactor master data infrastructure to streamline Reiter and Bundesland relationships, add V012 migration, harmonize domain models, implement repository methods for enhanced ZNS import logic, and update associated tests.
This commit is contained in:
+21
-2
@@ -6,6 +6,8 @@ import at.mocode.masterdata.domain.repository.VereinRepository
|
||||
import at.mocode.masterdata.domain.repository.HorseRepository
|
||||
import at.mocode.masterdata.domain.repository.FunktionaerRepository
|
||||
import at.mocode.masterdata.domain.repository.ReiterRepository
|
||||
import at.mocode.masterdata.domain.repository.LandRepository
|
||||
import at.mocode.masterdata.domain.repository.BundeslandRepository
|
||||
import at.mocode.zns.parser.ZnsFunktionaerParser
|
||||
import at.mocode.zns.parser.ZnsPferdParser
|
||||
import at.mocode.zns.parser.ZnsReiterParser
|
||||
@@ -40,7 +42,9 @@ class ZnsImportService(
|
||||
private val vereinRepository: VereinRepository,
|
||||
private val reiterRepository: ReiterRepository,
|
||||
private val horseRepository: HorseRepository,
|
||||
private val funktionaerRepository: FunktionaerRepository
|
||||
private val funktionaerRepository: FunktionaerRepository,
|
||||
private val landRepository: LandRepository,
|
||||
private val bundeslandRepository: BundeslandRepository
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -166,7 +170,19 @@ class ZnsImportService(
|
||||
var aktualisiert = 0
|
||||
zeilen.forEachIndexed { index, zeile ->
|
||||
runCatching {
|
||||
val reiter = ZnsReiterParser.parse(zeile) ?: return@forEachIndexed
|
||||
val parsed = ZnsReiterParser.parse(zeile) ?: return@forEachIndexed
|
||||
|
||||
// Relationen auflösen
|
||||
val verein = parsed.vereinsName?.let { vereinRepository.findByExactName(it) }
|
||||
val bundesland = parsed.bundeslandNummer?.let { bundeslandRepository.findByNr(it) }
|
||||
val nation = parsed.nation?.let { landRepository.findByIsoAlpha3Code(it) }
|
||||
|
||||
val reiter = parsed.copy(
|
||||
vereinId = verein?.vereinId,
|
||||
bundeslandId = bundesland?.bundeslandId,
|
||||
nationId = nation?.landId
|
||||
)
|
||||
|
||||
val vorhanden = reiterRepository.findBySatznummer(reiter.satznummer)
|
||||
if (vorhanden == null) {
|
||||
reiterRepository.save(reiter)
|
||||
@@ -179,6 +195,9 @@ class ZnsImportService(
|
||||
bundeslandNummer = reiter.bundeslandNummer,
|
||||
vereinsName = reiter.vereinsName,
|
||||
nation = reiter.nation,
|
||||
vereinId = reiter.vereinId,
|
||||
bundeslandId = reiter.bundeslandId,
|
||||
nationId = reiter.nationId,
|
||||
reiterLizenz = reiter.reiterLizenz,
|
||||
startkarte = reiter.startkarte,
|
||||
fahrLizenz = reiter.fahrLizenz,
|
||||
|
||||
+20
-1
@@ -8,6 +8,8 @@ import at.mocode.masterdata.domain.repository.FunktionaerRepository
|
||||
import at.mocode.masterdata.domain.repository.HorseRepository
|
||||
import at.mocode.masterdata.domain.repository.ReiterRepository
|
||||
import at.mocode.masterdata.domain.repository.VereinRepository
|
||||
import at.mocode.masterdata.domain.repository.LandRepository
|
||||
import at.mocode.masterdata.domain.repository.BundeslandRepository
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
@@ -28,6 +30,8 @@ class ZnsImportServiceTest {
|
||||
private val reiterRepository = mockk<ReiterRepository>()
|
||||
private val horseRepository = mockk<HorseRepository>()
|
||||
private val funktionaerRepository = mockk<FunktionaerRepository>()
|
||||
private val landRepository = mockk<LandRepository>()
|
||||
private val bundeslandRepository = mockk<BundeslandRepository>()
|
||||
|
||||
private lateinit var service: ZnsImportService
|
||||
|
||||
@@ -35,7 +39,19 @@ class ZnsImportServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
service = ZnsImportService(vereinRepository, reiterRepository, horseRepository, funktionaerRepository)
|
||||
service = ZnsImportService(
|
||||
vereinRepository,
|
||||
reiterRepository,
|
||||
horseRepository,
|
||||
funktionaerRepository,
|
||||
landRepository,
|
||||
bundeslandRepository
|
||||
)
|
||||
|
||||
// Standard-Stubs für optionale Lookups, damit Tests ohne spezifische Erwartungen nicht fehlschlagen
|
||||
coEvery { landRepository.findByIsoAlpha3Code(any()) } returns null
|
||||
coEvery { bundeslandRepository.findByNr(any()) } returns null
|
||||
coEvery { vereinRepository.findByExactName(any()) } returns null
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -244,6 +260,9 @@ class ZnsImportServiceTest {
|
||||
coEvery { horseRepository.save(any()) } answers { firstArg<Pferd>() }
|
||||
coEvery { funktionaerRepository.findBySatz(any(), any()) } returns null
|
||||
coEvery { funktionaerRepository.save(any()) } answers { firstArg<Funktionaer>() }
|
||||
coEvery { vereinRepository.findByExactName(any()) } returns null
|
||||
coEvery { bundeslandRepository.findByNr(any()) } returns null
|
||||
coEvery { landRepository.findByIsoAlpha3Code(any()) } returns null
|
||||
|
||||
// Importiere nacheinander (Simulation eines vollständigen Workflows)
|
||||
val res1 = service.importiereZip(zipVerein)
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.masterdata.domain.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Domain-Modell für Bundesland.
|
||||
*/
|
||||
@Serializable
|
||||
data class Bundesland(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val id: Uuid,
|
||||
val bundeslandNr: Int,
|
||||
val bezeichnung: String,
|
||||
val wappenUrl: String? = null
|
||||
)
|
||||
+1
@@ -35,6 +35,7 @@ data class BundeslandDefinition(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var landId: Uuid, // FK zu LandDefinition.landId
|
||||
|
||||
var bundeslandNr: Int? = null,
|
||||
var oepsCode: String?, // z. B. "01", "02", ... für Österreich; eindeutig pro landId = Österreich
|
||||
var iso3166_2_Code: String?, // z. B. "AT-1", "DE-BY"; Eindeutig global oder pro Land?
|
||||
var name: String, // z. B. "Niederösterreich", "Bayern"
|
||||
|
||||
+35
-13
@@ -78,6 +78,16 @@ data class Reiter(
|
||||
// Alphanumerisch (3)
|
||||
var nation: String? = null,
|
||||
|
||||
// Relationen zu Masterdaten
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var vereinId: Uuid? = null,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var bundeslandId: Uuid? = null,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var nationId: Uuid? = null,
|
||||
|
||||
// Alphanumerisch (4) Keine Lizenz: BLANK
|
||||
var reiterLizenz: String? = null,
|
||||
|
||||
@@ -126,19 +136,6 @@ data class Reiter(
|
||||
|
||||
// === 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,
|
||||
@@ -183,6 +180,27 @@ data class Reiter(
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates the 8-digit membership number.
|
||||
* Format: [B][VVV][MMMM]
|
||||
* B: Bundesland (1 digit)
|
||||
* VVV: Verein (3 digits)
|
||||
* MMMM: Member (4 digits)
|
||||
*/
|
||||
fun validateMitgliedsNummer(): Boolean {
|
||||
val nrStr = mitgliedsNummer?.toString()?.padStart(8, '0') ?: return false
|
||||
if (nrStr.length != 8) return false
|
||||
|
||||
val b = nrStr.substring(0, 1).toInt()
|
||||
// Validation against bundeslandNummer if available
|
||||
if (bundeslandNummer != null && b != (bundeslandNummer!! % 10)) {
|
||||
// ZNS bundeslandNummer is 01-09, while membership first digit is 1-9
|
||||
// This might need refinement depending on how "00" (Unbekannt) is handled in membership numbers.
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the rider for competition entry.
|
||||
* Returns a list of warning messages (never hard errors – TBA has final say).
|
||||
@@ -198,6 +216,10 @@ data class Reiter(
|
||||
warnings.add("Reiter ${getDisplayName()} hat keine aktive Startkarte für das aktuelle Jahr")
|
||||
}
|
||||
|
||||
if (!validateMitgliedsNummer()) {
|
||||
warnings.add("Reiter ${getDisplayName()} hat eine ungültige Mitgliedsnummer")
|
||||
}
|
||||
|
||||
return warnings
|
||||
}
|
||||
|
||||
|
||||
+5
-46
@@ -6,112 +6,71 @@ import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository interface for BundeslandDefinition (Federal State) domain operations.
|
||||
*
|
||||
* This interface defines the contract for federal state data access operations
|
||||
* without depending on specific implementation details (database, etc.).
|
||||
* Following the hexagonal architecture pattern, this interface belongs
|
||||
* to the domain layer and will be implemented in the infrastructure layer.
|
||||
*/
|
||||
interface BundeslandRepository {
|
||||
|
||||
/**
|
||||
* ZNS-Spezifisch: Sucht ein Bundesland anhand seiner Nummer (01-09).
|
||||
*/
|
||||
suspend fun findByNr(nr: Int): at.mocode.masterdata.domain.model.BundeslandDefinition?
|
||||
|
||||
/**
|
||||
* Finds a federal state by its unique ID.
|
||||
*
|
||||
* @param id The unique identifier of the federal state
|
||||
* @return The federal state if found, null otherwise
|
||||
*/
|
||||
suspend fun findById(id: Uuid): BundeslandDefinition?
|
||||
|
||||
/**
|
||||
* Finds a federal state by its OEPS code.
|
||||
*
|
||||
* @param oepsCode The OEPS code (e.g., "01", "02")
|
||||
* @param landId The country ID to search within
|
||||
* @return The federal state if found, null otherwise
|
||||
*/
|
||||
suspend fun findByOepsCode(oepsCode: String, landId: Uuid): BundeslandDefinition?
|
||||
|
||||
/**
|
||||
* Finds a federal state by its ISO 3166-2 code.
|
||||
*
|
||||
* @param iso3166_2_Code The ISO 3166-2 code (e.g., "AT-1", "DE-BY")
|
||||
* @return The federal state if found, null otherwise
|
||||
*/
|
||||
suspend fun findByIso3166_2_Code(iso3166_2_Code: String): BundeslandDefinition?
|
||||
|
||||
/**
|
||||
* Finds all federal states for a specific country.
|
||||
*
|
||||
* @param landId The country ID
|
||||
* @param activeOnly Whether to return only active federal states
|
||||
* @param orderBySortierung Whether to order by sortierReihenfolge field
|
||||
* @return List of federal states for the country
|
||||
*/
|
||||
suspend fun findByCountry(landId: Uuid, activeOnly: Boolean = true, orderBySortierung: Boolean = true): List<BundeslandDefinition>
|
||||
|
||||
/**
|
||||
* Finds federal states by name (partial match).
|
||||
*
|
||||
* @param searchTerm The search term to match against federal state names
|
||||
* @param landId Optional country ID to limit search
|
||||
* @param limit Maximum number of results to return
|
||||
* @return List of matching federal states
|
||||
*/
|
||||
suspend fun findByName(searchTerm: String, landId: Uuid? = null, limit: Int = 50): List<BundeslandDefinition>
|
||||
|
||||
/**
|
||||
* Finds all active federal states.
|
||||
*
|
||||
* @param orderBySortierung Whether to order by sortierReihenfolge field
|
||||
* @return List of active federal states
|
||||
*/
|
||||
suspend fun findAllActive(orderBySortierung: Boolean = true): List<BundeslandDefinition>
|
||||
|
||||
/**
|
||||
* Saves a federal state (create or update).
|
||||
*
|
||||
* @param bundesland The federal state to save
|
||||
* @return The saved federal state with updated timestamps
|
||||
*/
|
||||
suspend fun save(bundesland: BundeslandDefinition): BundeslandDefinition
|
||||
|
||||
/**
|
||||
* Upsert basierend auf dem natürlichen Schlüssel (landId + kuerzel).
|
||||
* Existiert bereits ein Datensatz mit gleicher Kombination, wird er aktualisiert,
|
||||
* ansonsten wird ein neuer Datensatz eingefügt.
|
||||
*/
|
||||
suspend fun upsertByLandIdAndKuerzel(bundesland: BundeslandDefinition): BundeslandDefinition
|
||||
|
||||
/**
|
||||
* Deletes a federal state by ID.
|
||||
*
|
||||
* @param id The unique identifier of the federal state to delete
|
||||
* @return true if the federal state was deleted, false if not found
|
||||
*/
|
||||
suspend fun delete(id: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Checks if a federal state with the given OEPS code exists for a country.
|
||||
*
|
||||
* @param oepsCode The OEPS code to check
|
||||
* @param landId The country ID
|
||||
* @return true if a federal state with this code exists, false otherwise
|
||||
*/
|
||||
suspend fun existsByOepsCode(oepsCode: String, landId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Checks if a federal state with the given ISO 3166-2 code exists.
|
||||
*
|
||||
* @param iso3166_2_Code The ISO 3166-2 code to check
|
||||
* @return true if a federal state with this code exists, false otherwise
|
||||
*/
|
||||
suspend fun existsByIso3166_2_Code(iso3166_2_Code: String): Boolean
|
||||
|
||||
/**
|
||||
* Counts the total number of active federal states for a country.
|
||||
*
|
||||
* @param landId The country ID
|
||||
* @return The total count of active federal states
|
||||
*/
|
||||
suspend fun countActiveByCountry(landId: Uuid): Long
|
||||
}
|
||||
|
||||
+5
@@ -23,6 +23,11 @@ interface VereinRepository {
|
||||
*/
|
||||
suspend fun findByVereinsNummer(vereinsNummer: String): Verein?
|
||||
|
||||
/**
|
||||
* Sucht einen Verein anhand seines exakten Namens.
|
||||
*/
|
||||
suspend fun findByExactName(vereinName: String): Verein?
|
||||
|
||||
/**
|
||||
* Sucht Vereine anhand des Namens (Teilübereinstimmung).
|
||||
*/
|
||||
|
||||
+7
@@ -18,6 +18,7 @@ class BundeslandRepositoryImpl : BundeslandRepository {
|
||||
return BundeslandDefinition(
|
||||
bundeslandId = row[BundeslandTable.id],
|
||||
landId = row[BundeslandTable.landId],
|
||||
bundeslandNr = row[BundeslandTable.bundeslandNr],
|
||||
oepsCode = row[BundeslandTable.oepsCode],
|
||||
iso3166_2_Code = row[BundeslandTable.iso3166_2_Code],
|
||||
name = row[BundeslandTable.name],
|
||||
@@ -30,6 +31,12 @@ class BundeslandRepositoryImpl : BundeslandRepository {
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun findByNr(nr: Int): BundeslandDefinition? = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where { BundeslandTable.bundeslandNr eq nr }
|
||||
.map(::rowToBundeslandDefinition)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Uuid): BundeslandDefinition? = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where { BundeslandTable.id eq id }
|
||||
.map(::rowToBundeslandDefinition)
|
||||
|
||||
+2
-1
@@ -10,8 +10,9 @@ import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
* Exposed-Tabellendefinition für die Bundesland-Entität.
|
||||
*/
|
||||
object BundeslandTable : Table("bundesland") {
|
||||
val id = uuid("bundesland_id")
|
||||
val id = uuid("id")
|
||||
val landId = uuid("land_id")
|
||||
val bundeslandNr = integer("bundesland_nr").nullable()
|
||||
val oepsCode = varchar("oeps_code", 10).nullable()
|
||||
val iso3166_2_Code = varchar("iso_3166_2_code", 10).nullable()
|
||||
val name = varchar("name", 100)
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.masterdata.infrastructure.persistence.reiter
|
||||
|
||||
import at.mocode.masterdata.domain.model.Bundesland
|
||||
import at.mocode.masterdata.domain.model.BundeslandDefinition
|
||||
import at.mocode.masterdata.domain.repository.BundeslandRepository
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.jdbc.*
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Exposed-Implementierung des Bundesland-Repositorys.
|
||||
* Hinweis: Implementiert nur die für den ZNS-Import notwendigen Methoden.
|
||||
*/
|
||||
class BundeslandExposedRepository : BundeslandRepository {
|
||||
private fun rowToDom(row: ResultRow) = Bundesland(
|
||||
id = row[BundeslandTable.id],
|
||||
bundeslandNr = row[BundeslandTable.bundeslandNr],
|
||||
bezeichnung = row[BundeslandTable.bezeichnung],
|
||||
wappenUrl = row[BundeslandTable.wappenUrl]
|
||||
)
|
||||
|
||||
override suspend fun findByNr(nr: Int): Bundesland? = DatabaseFactory.dbQuery {
|
||||
BundeslandTable.selectAll().where { BundeslandTable.bundeslandNr eq nr }
|
||||
.map(::rowToDom)
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
// Dummy-Implementierungen für das Interface, da derzeit nicht vom ZNS-Import benötigt
|
||||
override suspend fun findById(id: Uuid): BundeslandDefinition? = null
|
||||
override suspend fun findByOepsCode(oepsCode: String, landId: Uuid): BundeslandDefinition? = null
|
||||
override suspend fun findByIso3166_2_Code(iso3166_2_Code: String): BundeslandDefinition? = null
|
||||
override suspend fun findByCountry(landId: Uuid, activeOnly: Boolean, orderBySortierung: Boolean): List<BundeslandDefinition> = emptyList()
|
||||
override suspend fun findByName(searchTerm: String, landId: Uuid?, limit: Int): List<BundeslandDefinition> = emptyList()
|
||||
override suspend fun findAllActive(orderBySortierung: Boolean): List<BundeslandDefinition> = emptyList()
|
||||
override suspend fun save(bundesland: BundeslandDefinition): BundeslandDefinition = bundesland
|
||||
override suspend fun upsertByLandIdAndKuerzel(bundesland: BundeslandDefinition): BundeslandDefinition = bundesland
|
||||
override suspend fun delete(id: Uuid): Boolean = false
|
||||
override suspend fun existsByOepsCode(oepsCode: String, landId: Uuid): Boolean = false
|
||||
override suspend fun existsByIso3166_2_Code(iso3166_2_Code: String): Boolean = false
|
||||
override suspend fun countActiveByCountry(landId: Uuid): Long = 0L
|
||||
}
|
||||
+9
@@ -30,6 +30,9 @@ class ReiterExposedRepository : ReiterRepository {
|
||||
bundeslandNummer = row[ReiterTable.bundeslandNummer],
|
||||
vereinsName = row[ReiterTable.vereinsName],
|
||||
nation = row[ReiterTable.nation],
|
||||
vereinId = row[ReiterTable.vereinId],
|
||||
bundeslandId = row[ReiterTable.bundeslandId],
|
||||
nationId = row[ReiterTable.nationId],
|
||||
reiterLizenz = row[ReiterTable.reiterLizenz],
|
||||
startkarte = row[ReiterTable.startkarte],
|
||||
fahrLizenz = row[ReiterTable.fahrLizenz],
|
||||
@@ -91,6 +94,9 @@ class ReiterExposedRepository : ReiterRepository {
|
||||
it[bundeslandNummer] = reiter.bundeslandNummer
|
||||
it[vereinsName] = reiter.vereinsName
|
||||
it[nation] = reiter.nation
|
||||
it[vereinId] = reiter.vereinId
|
||||
it[bundeslandId] = reiter.bundeslandId
|
||||
it[nationId] = reiter.nationId
|
||||
it[reiterLizenz] = reiter.reiterLizenz
|
||||
it[startkarte] = reiter.startkarte
|
||||
it[fahrLizenz] = reiter.fahrLizenz
|
||||
@@ -121,6 +127,9 @@ class ReiterExposedRepository : ReiterRepository {
|
||||
it[bundeslandNummer] = reiter.bundeslandNummer
|
||||
it[vereinsName] = reiter.vereinsName
|
||||
it[nation] = reiter.nation
|
||||
it[vereinId] = reiter.vereinId
|
||||
it[bundeslandId] = reiter.bundeslandId
|
||||
it[nationId] = reiter.nationId
|
||||
it[reiterLizenz] = reiter.reiterLizenz
|
||||
it[startkarte] = reiter.startkarte
|
||||
it[fahrLizenz] = reiter.fahrLizenz
|
||||
|
||||
+35
-13
@@ -23,6 +23,11 @@ object ReiterTable : Table("reiter") {
|
||||
val bundeslandNummer = integer("bundesland_nummer").nullable()
|
||||
val vereinsName = varchar("vereins_name", 200).nullable()
|
||||
val nation = varchar("nation", 10).nullable()
|
||||
|
||||
val vereinId = uuid("verein_id").nullable()
|
||||
val bundeslandId = uuid("bundesland_id").nullable()
|
||||
val nationId = uuid("nation_id").nullable()
|
||||
|
||||
val reiterLizenz = varchar("reiter_lizenz", 20).nullable()
|
||||
val startkarte = varchar("startkarte", 20).nullable()
|
||||
val fahrLizenz = varchar("fahr_lizenz", 20).nullable()
|
||||
@@ -41,19 +46,6 @@ object ReiterTable : Table("reiter") {
|
||||
|
||||
// === ZNS.zip LITENZ01.DAT === ENDE ===
|
||||
|
||||
// Kontakt
|
||||
val imageUrl = varchar("image_url", 255).nullable()
|
||||
val email = varchar("email", 200).nullable()
|
||||
val telefon = varchar("telefon", 50).nullable()
|
||||
val website = varchar("website", 255).nullable()
|
||||
|
||||
// Adresse
|
||||
val strasse = varchar("strasse", 200).nullable()
|
||||
val hausnummer = varchar("hausnummer", 10).nullable()
|
||||
val plz = varchar("plz", 10).nullable()
|
||||
val ort = varchar("ort", 100).nullable()
|
||||
val bundesland = varchar("bundesland", 100).nullable()
|
||||
|
||||
// Status & Verwaltung
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val bemerkungen = text("bemerkungen").nullable()
|
||||
@@ -68,5 +60,35 @@ object ReiterTable : Table("reiter") {
|
||||
init {
|
||||
index("idx_reiter_satznummer", isUnique = true, satznummer)
|
||||
index("idx_reiter_name", isUnique = false, nachname, vorname)
|
||||
index("idx_reiter_mitglied", isUnique = false, mitgliedsNummer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposed-Tabellendefinition für die Bundesland-Mastertabelle.
|
||||
*/
|
||||
object BundeslandTable : Table("bundesland") {
|
||||
val id = uuid("bundesland_id")
|
||||
val bundeslandNr = integer("bundesland_nr").uniqueIndex()
|
||||
val bezeichnung = varchar("bezeichnung", 100)
|
||||
val wappenUrl = varchar("wappen_url", 255).nullable()
|
||||
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
|
||||
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposed-Tabellendefinition für die Reiter-Lizenzen.
|
||||
*/
|
||||
object ReiterLizenzTable : Table("reiter_lizenz") {
|
||||
val id = uuid("lizenz_id")
|
||||
val reiterId = uuid("reiter_id")
|
||||
val lizenzTyp = varchar("lizenz_typ", 50) // STARTKARTE, REITERLIZENZ, FAHRLIZENZ
|
||||
val kuerzel = varchar("kuerzel", 20)
|
||||
val gueltigBis = date("gueltig_bis").nullable()
|
||||
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
|
||||
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
+6
@@ -66,6 +66,12 @@ class VereinExposedRepository : VereinRepository {
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByExactName(vereinName: String): Verein? = DatabaseFactory.dbQuery {
|
||||
VereinTable.selectAll().where { VereinTable.vereinName eq vereinName }
|
||||
.map(::rowToVereinDomain)
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<Verein> = DatabaseFactory.dbQuery {
|
||||
val pattern = "%$searchTerm%"
|
||||
VereinTable.selectAll().where { VereinTable.vereinName like pattern }
|
||||
|
||||
+4
@@ -7,6 +7,8 @@ import at.mocode.masterdata.infrastructure.persistence.funktionaer.FunktionaerTa
|
||||
import at.mocode.masterdata.infrastructure.persistence.funktionaer.QualifikationMasterTable
|
||||
import at.mocode.masterdata.infrastructure.persistence.pferd.HorseTable
|
||||
import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable
|
||||
import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterLizenzTable
|
||||
import at.mocode.masterdata.infrastructure.persistence.reiter.BundeslandTable
|
||||
import at.mocode.masterdata.infrastructure.persistence.verein.VereinTable
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.annotation.PreDestroy
|
||||
@@ -53,6 +55,7 @@ class MasterdataDatabaseConfiguration(
|
||||
FunktionaerTable,
|
||||
QualifikationMasterTable,
|
||||
FunktionaerQualifikationTable,
|
||||
ReiterLizenzTable,
|
||||
TurnierklasseTable,
|
||||
LicenseTable,
|
||||
RichtverfahrenTable,
|
||||
@@ -101,6 +104,7 @@ class MasterdataTestDatabaseConfiguration {
|
||||
FunktionaerTable,
|
||||
QualifikationMasterTable,
|
||||
FunktionaerQualifikationTable,
|
||||
ReiterLizenzTable,
|
||||
TurnierklasseTable,
|
||||
LicenseTable,
|
||||
RichtverfahrenTable,
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
-- V012__Reiter_Masterdata_Refactoring.sql
|
||||
-- 1. Bestehende Master-Tabellen harmonisieren
|
||||
|
||||
-- Bundesland Tabelle erweitern (falls nötig)
|
||||
ALTER TABLE bundesland ADD COLUMN IF NOT EXISTS bundesland_nr INT;
|
||||
|
||||
-- Reiter-Lizenzen (1:n Beziehung zu Reiter)
|
||||
CREATE TABLE IF NOT EXISTS reiter_lizenz (
|
||||
lizenz_id UUID PRIMARY KEY,
|
||||
reiter_id UUID NOT NULL,
|
||||
lizenz_typ VARCHAR(50) NOT NULL, -- STARTKARTE, REITERLIZENZ, FAHRLIZENZ
|
||||
kuerzel VARCHAR(20) NOT NULL,
|
||||
gueltig_bis DATE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 2. Reiter-Tabelle anpassen
|
||||
|
||||
-- Redundante Spalten entfernen (werden nun über Person/Reiter-Identität/Verein bezogen)
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS image_url;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS email;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS telefon;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS website;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS strasse;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS hausnummer;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS plz;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS ort;
|
||||
ALTER TABLE reiter DROP COLUMN IF EXISTS bundesland;
|
||||
|
||||
-- Neue Fremdschlüssel-Spalten hinzufügen
|
||||
ALTER TABLE reiter ADD COLUMN IF NOT EXISTS verein_id UUID;
|
||||
ALTER TABLE reiter ADD COLUMN IF NOT EXISTS bundesland_id UUID;
|
||||
ALTER TABLE reiter ADD COLUMN IF NOT EXISTS nation_id UUID;
|
||||
|
||||
-- Fremdschlüssel-Constraints
|
||||
ALTER TABLE reiter ADD CONSTRAINT fk_reiter_verein FOREIGN KEY (verein_id) REFERENCES verein(verein_id);
|
||||
ALTER TABLE reiter ADD CONSTRAINT fk_reiter_bundesland FOREIGN KEY (bundesland_id) REFERENCES bundesland(id);
|
||||
ALTER TABLE reiter ADD CONSTRAINT fk_reiter_nation FOREIGN KEY (nation_id) REFERENCES land(id);
|
||||
ALTER TABLE reiter ADD CONSTRAINT fk_reiter_lizenz_reiter FOREIGN KEY (reiter_id) REFERENCES reiter(reiter_id) ON DELETE CASCADE;
|
||||
|
||||
-- 3. Daten gemäß OEPS-Spezifikation korrigieren (für Österreich)
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
austria_id UUID;
|
||||
BEGIN
|
||||
SELECT id INTO austria_id FROM land WHERE iso_alpha2_code = 'AT';
|
||||
|
||||
IF austria_id IS NOT NULL THEN
|
||||
-- Bestehende Einträge löschen oder aktualisieren
|
||||
-- Wir setzen die bundesland_nr gemäß User-Vorgabe:
|
||||
-- 01=Wien, 02=NÖ, 03=Burgenland, 04=Steiermark, 05=Kärnten, 06=Oberösterreich, 07=Salzburg, 08=Tirol, 09=Vorarlberg, 00=Unbekannt
|
||||
|
||||
UPDATE bundesland SET bundesland_nr = 1, name = 'Wien', kuerzel = 'W' WHERE land_id = austria_id AND (oeps_code = '09' OR name = 'Wien');
|
||||
UPDATE bundesland SET bundesland_nr = 2, name = 'Niederösterreich', kuerzel = 'NÖ' WHERE land_id = austria_id AND (oeps_code = '03' OR name = 'Niederösterreich');
|
||||
UPDATE bundesland SET bundesland_nr = 3, name = 'Burgenland', kuerzel = 'BGLD' WHERE land_id = austria_id AND (oeps_code = '01' OR name = 'Burgenland');
|
||||
UPDATE bundesland SET bundesland_nr = 4, name = 'Steiermark', kuerzel = 'STMK' WHERE land_id = austria_id AND (oeps_code = '06' OR name = 'Steiermark');
|
||||
UPDATE bundesland SET bundesland_nr = 5, name = 'Kärnten', kuerzel = 'KTN' WHERE land_id = austria_id AND (oeps_code = '02' OR name = 'Kärnten');
|
||||
UPDATE bundesland SET bundesland_nr = 6, name = 'Oberösterreich', kuerzel = 'OÖ' WHERE land_id = austria_id AND (oeps_code = '04' OR name = 'Oberösterreich');
|
||||
UPDATE bundesland SET bundesland_nr = 7, name = 'Salzburg', kuerzel = 'SBG' WHERE land_id = austria_id AND (oeps_code = '05' OR name = 'Salzburg');
|
||||
UPDATE bundesland SET bundesland_nr = 8, name = 'Tirol', kuerzel = 'T' WHERE land_id = austria_id AND (oeps_code = '07' OR name = 'Tirol');
|
||||
UPDATE bundesland SET bundesland_nr = 9, name = 'Vorarlberg', kuerzel = 'VBG' WHERE land_id = austria_id AND (oeps_code = '08' OR name = 'Vorarlberg');
|
||||
|
||||
-- Fehlende "Unbekannt" hinzufügen
|
||||
INSERT INTO bundesland (land_id, bundesland_nr, name, kuerzel, oeps_code)
|
||||
VALUES (austria_id, 0, 'Unbekannt', 'UNK', '00')
|
||||
ON CONFLICT DO NOTHING;
|
||||
END IF;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user