Refactor license matrix and tokenizer logic: rename LicenseTable to LizenzTable, replace LicenseMatrixService with LizenzMatrixService, enhance tokenizer with normalized and fallback token handling, improve ZNS import for license extraction, and update related documentation.
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Waiting to run
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Waiting to run
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Waiting to run
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Waiting to run
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Waiting to run
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Waiting to run
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Waiting to run
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Waiting to run
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Waiting to run
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Waiting to run
This commit is contained in:
+15
-3
@@ -192,13 +192,25 @@ data class Reiter(
|
||||
|
||||
/**
|
||||
* Checks if the rider holds a license for a specific discipline.
|
||||
* Simple logic for now: Any non-blank license field counts.
|
||||
* Strikte Logik: Eine Dressur-Lizenz (RD1..RD3) gilt nur für DRESSUR,
|
||||
* eine Reit-/Spring-Lizenz (R1..R4) nur für SPRINGEN. FAHREN nutzt die separate Fahr-Lizenz.
|
||||
*/
|
||||
fun hasLizenzForSparte(sparte: at.mocode.core.domain.model.SparteE): Boolean {
|
||||
val lk = lizenzKlasse
|
||||
return when (sparte) {
|
||||
at.mocode.core.domain.model.SparteE.DRESSUR -> !reiterLizenz.isNullOrBlank()
|
||||
at.mocode.core.domain.model.SparteE.SPRINGEN -> !reiterLizenz.isNullOrBlank()
|
||||
at.mocode.core.domain.model.SparteE.DRESSUR ->
|
||||
lk == ReiterLizenzKlasseE.RD1 ||
|
||||
lk == ReiterLizenzKlasseE.RD2 ||
|
||||
lk == ReiterLizenzKlasseE.RD3
|
||||
|
||||
at.mocode.core.domain.model.SparteE.SPRINGEN ->
|
||||
lk == ReiterLizenzKlasseE.R1 ||
|
||||
lk == ReiterLizenzKlasseE.R2 ||
|
||||
lk == ReiterLizenzKlasseE.R3 ||
|
||||
lk == ReiterLizenzKlasseE.R4
|
||||
|
||||
at.mocode.core.domain.model.SparteE.FAHREN -> !fahrLizenz.isNullOrBlank()
|
||||
|
||||
else -> hasLizenz()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@Serializable
|
||||
data class ReitLizenz(
|
||||
data class Reiterlizenz(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val lizenzId: Uuid = Uuid.random(),
|
||||
val code: String,
|
||||
+3
-3
@@ -3,14 +3,14 @@
|
||||
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.Reiterlizenz
|
||||
import at.mocode.masterdata.domain.model.Startkarte
|
||||
|
||||
/**
|
||||
* Repository für alle Lizenz-Stammdaten (Reit, Fahr, Startkarten).
|
||||
*/
|
||||
interface MasterdataLicenseRepository {
|
||||
suspend fun findReitLizenzByCode(code: String): ReitLizenz?
|
||||
interface LizenzRepository {
|
||||
suspend fun findReitLizenzByCode(code: String): Reiterlizenz?
|
||||
suspend fun findFahrLizenzByCode(code: String): FahrLizenz?
|
||||
suspend fun findStartkarteByCode(code: String): Startkarte?
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package at.mocode.masterdata.domain.service
|
||||
|
||||
import at.mocode.core.domain.model.ReiterLizenzKlasseE
|
||||
import at.mocode.core.domain.model.SparteE
|
||||
import at.mocode.masterdata.domain.model.Reiter
|
||||
import at.mocode.masterdata.domain.model.LicenseMatrixEntry
|
||||
import at.mocode.masterdata.domain.model.TurnierklasseDefinition
|
||||
|
||||
/**
|
||||
* Standard-Implementierung des [LicenseMatrixService] gemäß ÖTO.
|
||||
*/
|
||||
class LicenseMatrixServiceImpl : LicenseMatrixService {
|
||||
|
||||
private val classHierarchy = listOf("E", "A", "L", "LM", "M", "S")
|
||||
|
||||
override fun isEligible(
|
||||
reiter: Reiter,
|
||||
turnierklasse: TurnierklasseDefinition,
|
||||
sparte: SparteE,
|
||||
matrix: List<LicenseMatrixEntry>,
|
||||
alleKlassen: List<TurnierklasseDefinition>
|
||||
): Boolean {
|
||||
// 1. Basis-Check: Hat der Reiter überhaupt eine Lizenz für diese Sparte?
|
||||
if (!reiter.hasLizenzForSparte(sparte)) return false
|
||||
|
||||
// 2. Max Turnierklasse aus Matrix ermitteln
|
||||
val maxClassCode = getMaxTurnierklasse(reiter, sparte, matrix) ?: return false
|
||||
|
||||
// 3. Hierarchie-Check (maxClassCode vs. turnierklasse.code)
|
||||
val maxIndex = classHierarchy.indexOf(maxClassCode)
|
||||
val targetIndex = classHierarchy.indexOf(turnierklasse.code)
|
||||
|
||||
if (maxIndex == -1 || targetIndex == -1) return false
|
||||
|
||||
return targetIndex <= maxIndex
|
||||
}
|
||||
|
||||
override fun getMaxTurnierklasse(
|
||||
reiter: Reiter,
|
||||
sparte: SparteE,
|
||||
matrix: List<LicenseMatrixEntry>
|
||||
): String? {
|
||||
// Suche passenden Eintrag in der Matrix für (Sparte, Lizenzklasse)
|
||||
val entry = matrix.find { it.sparte == sparte && it.lizenzKlasse == reiter.lizenzKlasse }
|
||||
?: matrix.find { it.sparte == SparteE.DRESSUR && sparte == SparteE.DRESSUR && it.lizenzKlasse == reiter.lizenzKlasse } // Fallback/Spezial
|
||||
?: if (reiter.lizenzKlasse == ReiterLizenzKlasseE.R1 ||
|
||||
reiter.lizenzKlasse == ReiterLizenzKlasseE.R2 ||
|
||||
reiter.lizenzKlasse == ReiterLizenzKlasseE.R3 ||
|
||||
reiter.lizenzKlasse == ReiterLizenzKlasseE.R4) {
|
||||
// Fallback für Dressur, wenn man eine Springlizenz hat (R1 gilt oft auch als RD1 etc. in manchen Kontexten,
|
||||
// aber hier schauen wir primär ob die Matrix einen generischen Eintrag hat)
|
||||
matrix.find { it.sparte == sparte && it.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI }
|
||||
} else null
|
||||
|
||||
return entry?.maxTurnierklasseCode
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ import at.mocode.masterdata.domain.model.TurnierklasseDefinition
|
||||
/**
|
||||
* Service zur Prüfung der Teilnahmeberechtigung basierend auf der Lizenz-Matrix.
|
||||
*/
|
||||
interface LicenseMatrixService {
|
||||
interface LizenzMatrixService {
|
||||
|
||||
/**
|
||||
* Prüft, ob ein Reiter mit seiner aktuellen Lizenz in einer bestimmten Turnierklasse startberechtigt ist.
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package at.mocode.masterdata.domain.service
|
||||
|
||||
import at.mocode.core.domain.model.ReiterLizenzKlasseE
|
||||
import at.mocode.core.domain.model.SparteE
|
||||
import at.mocode.masterdata.domain.model.Reiter
|
||||
import at.mocode.masterdata.domain.model.LicenseMatrixEntry
|
||||
import at.mocode.masterdata.domain.model.TurnierklasseDefinition
|
||||
|
||||
/**
|
||||
* Standard-Implementierung des [LizenzMatrixService] gemäß ÖTO.
|
||||
*/
|
||||
class LizenzMatrixServiceImpl : LizenzMatrixService {
|
||||
|
||||
private val classHierarchy = listOf("E", "A", "L", "LM", "M", "S")
|
||||
|
||||
override fun isEligible(
|
||||
reiter: Reiter,
|
||||
turnierklasse: TurnierklasseDefinition,
|
||||
sparte: SparteE,
|
||||
matrix: List<LicenseMatrixEntry>,
|
||||
alleKlassen: List<TurnierklasseDefinition>
|
||||
): Boolean {
|
||||
// 1. Basis-Check: Hat der Reiter überhaupt eine Lizenz für diese Sparte?
|
||||
if (!reiter.hasLizenzForSparte(sparte)) return false
|
||||
|
||||
// 2. Max Turnierklasse aus Matrix ermitteln
|
||||
val maxClassCode = getMaxTurnierklasse(reiter, sparte, matrix) ?: return false
|
||||
|
||||
// 3. Hierarchie-Check (maxClassCode vs. turnierklasse.code)
|
||||
val maxIndex = classHierarchy.indexOf(maxClassCode)
|
||||
val targetIndex = classHierarchy.indexOf(turnierklasse.code)
|
||||
|
||||
if (maxIndex == -1 || targetIndex == -1) return false
|
||||
|
||||
return targetIndex <= maxIndex
|
||||
}
|
||||
|
||||
override fun getMaxTurnierklasse(
|
||||
reiter: Reiter,
|
||||
sparte: SparteE,
|
||||
matrix: List<LicenseMatrixEntry>
|
||||
): String? {
|
||||
// 1) Direkter Treffer in Matrix für (Sparte, Lizenzklasse)
|
||||
val direct = matrix.find { it.sparte == sparte && it.lizenzKlasse == reiter.lizenzKlasse }
|
||||
if (direct != null) return direct.maxTurnierklasseCode
|
||||
|
||||
// 2) Cross-Discipline Mapping (R<->RD) gemäß ÖTO-Äquivalenzen
|
||||
// Hinweis: RD4 ist im aktuellen Enum nicht modelliert. R4 wird für Zwecke der
|
||||
// Dressur-Kappung wie RD3 behandelt (max. S), bis RD4 eingeführt wird.
|
||||
val mappedLizenz = when (sparte) {
|
||||
SparteE.DRESSUR -> when (reiter.lizenzKlasse) {
|
||||
ReiterLizenzKlasseE.R1 -> ReiterLizenzKlasseE.RD1
|
||||
ReiterLizenzKlasseE.R2 -> ReiterLizenzKlasseE.RD2
|
||||
ReiterLizenzKlasseE.R3, ReiterLizenzKlasseE.R4 -> ReiterLizenzKlasseE.RD3 // RD4 derzeit nicht modelliert
|
||||
else -> reiter.lizenzKlasse
|
||||
}
|
||||
SparteE.SPRINGEN -> when (reiter.lizenzKlasse) {
|
||||
ReiterLizenzKlasseE.RD1 -> ReiterLizenzKlasseE.R1
|
||||
ReiterLizenzKlasseE.RD2 -> ReiterLizenzKlasseE.R2
|
||||
ReiterLizenzKlasseE.RD3 -> ReiterLizenzKlasseE.R3
|
||||
else -> reiter.lizenzKlasse
|
||||
}
|
||||
else -> reiter.lizenzKlasse
|
||||
}
|
||||
val cross = matrix.find { it.sparte == sparte && it.lizenzKlasse == mappedLizenz }
|
||||
if (cross != null) return cross.maxTurnierklasseCode
|
||||
|
||||
// 3) Letzter Fallback: LIZENZFREI in gewünschter Sparte
|
||||
return matrix.find { it.sparte == sparte && it.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI }
|
||||
?.maxTurnierklasseCode
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -13,9 +13,9 @@ import kotlin.test.assertTrue
|
||||
import kotlin.time.Clock
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
class LicenseMatrixServiceTest {
|
||||
class LiznezMatrixServiceTest {
|
||||
|
||||
private val service = LicenseMatrixServiceImpl()
|
||||
private val service = LizenzMatrixServiceImpl()
|
||||
private val nun = Clock.System.now()
|
||||
|
||||
private val matrix = listOf(
|
||||
Reference in New Issue
Block a user