Standardize and refactor master data infrastructure: rename tables for plural consistency, remove unused entity tables, improve ZNS import mappings with enriched license properties, introduce Altersklasse domain model, activate Consul service discovery, and update application configuration accordingly.

This commit is contained in:
2026-04-06 19:48:15 +02:00
parent bc13a58a14
commit abaaeddaaf
53 changed files with 1575 additions and 333 deletions
@@ -0,0 +1,22 @@
@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 eine Bewerbs-Klasse (z.B. E, A, L, M, S).
*/
@Serializable
data class BewerbsKlasse(
@Serializable(with = UuidSerializer::class)
val bewerbsklasseId: Uuid = Uuid.random(),
val sparte: String,
val code: String,
val bezeichnung: String,
val maxHoehe: Int? = null,
val aufgabenNiveau: String? = null,
val istAktiv: Boolean = true
)
@@ -0,0 +1,35 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.masterdata.domain.model
import at.mocode.core.domain.model.SparteE
import at.mocode.core.domain.serialization.InstantSerializer
import at.mocode.core.domain.serialization.UuidSerializer
import kotlinx.serialization.Serializable
import kotlin.time.Instant
import kotlin.uuid.Uuid
/**
* Domänenmodell für eine Bewerbsklasse gemäß ÖTO.
*/
@Serializable
data class BewerbsklasseDefinition(
@Serializable(with = UuidSerializer::class)
val bewerbsklasseId: Uuid = Uuid.random(),
val sparte: SparteE,
val code: String, // E, A, L, LM, M, S
val bezeichnung: String,
val maxHoehe: Int? = null, // in cm (Springen)
val aufgabenNiveau: String? = null, // (Dressur)
@Serializable(with = InstantSerializer::class)
val validFrom: Instant,
@Serializable(with = InstantSerializer::class)
val validTo: Instant? = null,
val istAktiv: Boolean = true,
@Serializable(with = InstantSerializer::class)
val createdAt: Instant,
@Serializable(with = InstantSerializer::class)
val updatedAt: Instant
)
@@ -7,12 +7,12 @@ import at.mocode.core.domain.serialization.UuidSerializer
import kotlin.uuid.Uuid
/**
* Domain-Modell für Bundesland.
* Domain-Modell für Bundesland (Mehrzahl 'Bundeslaender').
*/
@Serializable
data class Bundesland(
@Serializable(with = UuidSerializer::class)
val id: Uuid,
val bundeslandId: Uuid,
val bundeslandNr: Int,
val bezeichnung: String,
val wappenUrl: String? = null
@@ -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 eine Fahr-Lizenz (z.B. F1, F2).
*/
@Serializable
data class FahrLizenz(
@Serializable(with = UuidSerializer::class)
val lizenzId: Uuid = Uuid.random(),
val code: String,
val bezeichnung: String,
val istAktiv: Boolean = true
)
@@ -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 at.mocode.core.domain.serialization.LocalDateSerializer
import kotlinx.datetime.LocalDate
import kotlin.uuid.Uuid
@Serializable
data class ReitLizenz(
@Serializable(with = UuidSerializer::class)
val lizenzId: Uuid = Uuid.random(),
val code: String,
val bezeichnung: String,
val sparte: String? = null,
val istAktiv: Boolean = true
)
@@ -14,6 +14,16 @@ import kotlin.time.Clock
import kotlin.time.Instant
import kotlin.uuid.Uuid
@Serializable
data class ReiterLizenz(
@Serializable(with = UuidSerializer::class)
val lizenzId: Uuid = Uuid.random(),
val lizenzTyp: String, // STARTKARTE, REITERLIZENZ, FAHRLIZENZ
val kuerzel: String,
@Serializable(with = LocalDateSerializer::class)
val gueltigBis: LocalDate? = null
)
/**
* Domain model representing a rider (Reiter) in the actor-context.
*
@@ -88,6 +98,15 @@ data class Reiter(
@Serializable(with = UuidSerializer::class)
var nationId: Uuid? = null,
@Serializable(with = UuidSerializer::class)
var reitLizenzId: Uuid? = null,
@Serializable(with = UuidSerializer::class)
var fahrLizenzId: Uuid? = null,
@Serializable(with = UuidSerializer::class)
var startkarteId: Uuid? = null,
// Alphanumerisch (4) Keine Lizenz: BLANK
var reiterLizenz: String? = null,
@@ -98,10 +117,10 @@ data class Reiter(
var fahrLizenz: String? = null,
// Alphanumerisch (2) WERTE: Standard: BLANK, JG=JUGENDLICHER, JR=JUNIOR, 25=U25
var altersklasseJgJrU25: String? = null,
var altersklasseJgJrU25: at.mocode.core.domain.model.ReiterAltersKlasseE? = null,
// Alphanumerisch (1) WERTE: Standard: BLANK Y=JUNGER-REITER
var altersklasseY: String? = null,
var altersklasseY: at.mocode.core.domain.model.ReiterAltersKlasseE? = null,
// Numerisch (8) FORMAT: 00000000
var mitgliedsNummer: Int? = null,
@@ -145,7 +164,12 @@ data class Reiter(
@Serializable(with = InstantSerializer::class)
val createdAt: Instant = Clock.System.now(),
@Serializable(with = InstantSerializer::class)
var updatedAt: Instant = Clock.System.now()
var updatedAt: Instant = Clock.System.now(),
/**
* List of specialized licenses for this rider.
*/
var lizenzen: List<ReiterLizenz> = emptyList()
) {
/**
* Returns the display name of the rider.
@@ -183,7 +207,7 @@ data class Reiter(
/**
* Validates the 8-digit membership number.
* Format: [B][VVV][MMMM]
* B: Bundesland (1 digit)
* B: Bundesland (1 digit, 1-9)
* VVV: Verein (3 digits)
* MMMM: Member (4 digits)
*/
@@ -192,12 +216,20 @@ data class Reiter(
if (nrStr.length != 8) return false
val b = nrStr.substring(0, 1).toInt()
if (b < 1 || b > 9) return false // Valid Bundesland prefix is 1-9
// 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.
// ZNS bundeslandNummer is 01-09, while membership first digit is 1-9
if (bundeslandNummer != null && b != bundeslandNummer) {
return false
}
// Verein part (2nd-4th digit)
// ZNS Verein ID usually contains state + 3 digits.
// Here we just check if it's not all zeros as a basic sanity check
val vvv = nrStr.substring(1, 4).toInt()
if (vvv == 0) return false
return true
}
@@ -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 eine Startkarte.
*/
@Serializable
data class Startkarte(
@Serializable(with = UuidSerializer::class)
val startkarteId: Uuid = Uuid.random(),
val code: String,
val bezeichnung: String,
val istAktiv: Boolean = true
)
@@ -0,0 +1,20 @@
@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 eine Turnier-Kategorie (z.B. CSN-C, CDN-A, CSN-C Neu).
*/
@Serializable
data class TurnierKategorie(
@Serializable(with = UuidSerializer::class)
val kategorieId: Uuid = Uuid.random(),
val code: String,
val bezeichnung: String,
val sparte: String? = null,
val istAktiv: Boolean = true
)
@@ -0,0 +1,22 @@
@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 eine Turnier-Klasse.
*/
@Serializable
data class TurnierKlasse(
@Serializable(with = UuidSerializer::class)
val turnierklasseId: Uuid = Uuid.random(),
val sparte: String,
val code: String,
val bezeichnung: String,
val maxHoehe: Int? = null,
val aufgabenNiveau: String? = null,
val istAktiv: Boolean = true
)
@@ -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 eine Turnier-Sparte.
*/
@Serializable
data class TurnierSparte(
@Serializable(with = UuidSerializer::class)
val sparteId: Uuid = Uuid.random(),
val code: String,
val bezeichnung: String,
val istAktiv: Boolean = true
)
@@ -0,0 +1,12 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.masterdata.domain.repository
import at.mocode.masterdata.domain.model.AltersklasseDefinition
/**
* Repository für Altersklassen-Stammdaten.
*/
interface AltersklassenRepository {
suspend fun findByCode(code: String): AltersklasseDefinition?
}
@@ -0,0 +1,16 @@
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
package at.mocode.masterdata.domain.repository
import at.mocode.masterdata.domain.model.FahrLizenz
import at.mocode.masterdata.domain.model.ReitLizenz
import at.mocode.masterdata.domain.model.Startkarte
/**
* Repository für alle Lizenz-Stammdaten (Reit, Fahr, Startkarten).
*/
interface MasterdataLicenseRepository {
suspend fun findReitLizenzByCode(code: String): at.mocode.masterdata.domain.model.ReitLizenz?
suspend fun findFahrLizenzByCode(code: String): at.mocode.masterdata.domain.model.FahrLizenz?
suspend fun findStartkarteByCode(code: String): at.mocode.masterdata.domain.model.Startkarte?
}
@@ -9,6 +9,7 @@ import at.mocode.masterdata.domain.model.*
*/
interface RegulationRepository {
suspend fun findAllTurnierklassen(): List<TurnierklasseDefinition>
suspend fun findAllBewerbsklassen(): List<BewerbsklasseDefinition>
suspend fun findAllLicenseMatrixEntries(): List<LicenseMatrixEntry>
suspend fun findAllRichtverfahren(): List<RichtverfahrenDefinition>
suspend fun findAllGebuehren(): List<GebuehrDefinition>