From f50d4deb16bb933426ab53d97903927ab462897b Mon Sep 17 00:00:00 2001 From: StefanMoCoAt Date: Mon, 6 Apr 2026 00:00:20 +0200 Subject: [PATCH] Refactor domain models (`DomFunktionaer`, `DomReiter`, `DomPferd`) to align with ZNS conventions: simplify naming, update properties, and enhance parser logic. Adjust related controllers, repository methods, and tests. Update `MASTER_ROADMAP` with changes to domain models. --- CHANGELOG.md | 13 +++ .../mocode/zns/importer/ZnsImportService.kt | 38 ++++---- .../zns/importer/ZnsImportServiceTest.kt | 62 ++++++++---- .../at/mocode/entries/api/NennungDtos.kt | 54 +++++------ .../mocode/entries/domain/model/DomNennung.kt | 12 +-- .../domain/repository/NennungRepository.kt | 6 +- .../domain/service/AbteilungsRegelService.kt | 14 +-- .../service/AbteilungsRegelServiceTest.kt | 24 ++--- .../persistence/NennungRepositoryImpl.kt | 8 +- .../service/usecase/NennungUseCases.kt | 6 +- .../tenant/EntriesIsolationIntegrationTest.kt | 2 +- .../api/rest/FunktionaerController.kt | 10 +- .../masterdata/api/rest/HorseController.kt | 8 +- .../masterdata/api/rest/ReiterController.kt | 12 +-- .../masterdata/api/rest/VereinController.kt | 32 +++--- .../{DomFunktionaer.kt => Funktionaer.kt} | 50 +++++++--- .../domain/model/LicenseMatrixEntry.kt | 20 ++-- .../domain/model/{DomPferd.kt => Pferd.kt} | 62 ++++++++---- .../domain/model/{DomReiter.kt => Reiter.kt} | 88 ++++++++++++++--- .../domain/model/{DomVerein.kt => Verein.kt} | 45 +++++---- .../repository/FunktionaerRepository.kt | 12 +-- .../domain/repository/HorseRepository.kt | 46 ++++----- .../domain/repository/ReiterRepository.kt | 14 +-- .../domain/repository/VereinRepository.kt | 22 ++--- .../domain/service/AbteilungsRegelService.kt | 8 +- .../service/AbteilungsRegelServiceImpl.kt | 18 ++-- .../domain/service/AltersklasseRechner.kt | 10 +- .../domain/service/AltersklasseRechnerImpl.kt | 10 +- .../domain/service/LicenseMatrixService.kt | 6 +- .../service/LicenseMatrixServiceImpl.kt | 6 +- .../service/AbteilungsRegelServiceTest.kt | 28 +++--- .../domain/service/AltersklasseRechnerTest.kt | 6 +- .../service/LicenseMatrixServiceTest.kt | 18 ++-- .../ExposedRegulationRepository.kt | 4 +- .../persistence/ReiterSparteTable.kt | 1 + .../persistence/VereinAnsprechpersonTable.kt | 2 + .../FunktionaerExposedRepository.kt} | 50 +++++----- .../{ => funktionaer}/FunktionaerTable.kt | 32 +++++- .../HorseExposedRepository.kt} | 73 +++++++------- .../persistence/{ => pferd}/HorseTable.kt | 14 ++- .../ReiterExposedRepository.kt} | 27 +++--- .../persistence/{ => reiter}/ReiterTable.kt | 30 +++++- .../VereinExposedRepository.kt} | 97 ++++++++++--------- .../persistence/{ => verein}/VereinTable.kt | 37 ++++--- .../RegulationSeedVerificationTest.kt | 10 +- .../service/config/MasterdataConfiguration.kt | 12 ++- .../config/MasterdataDatabaseConfiguration.kt | 4 + .../migration/V008__Seed_OETO_2026_Data.sql | 2 +- ..._Add_HorseAge_And_LicenseHeight_Matrix.sql | 2 +- .../service/config/RepositoryConfiguration.kt | 13 ++- .../config/ZnsImportDatabaseConfiguration.kt | 8 +- .../at/mocode/core/domain/model/Enums.kt | 29 ++++-- .../at/mocode/zns/parser/ZnsLegacyParsers.kt | 64 ++++++------ .../mocode/zns/parser/ZnsLegacyParsersTest.kt | 40 +++++++- docs/01_Architecture/MASTER_ROADMAP.md | 4 +- docs/01_Architecture/Roadmap_ZNS_Importer.md | 7 +- .../01_Glossary/Ubiquitous_Language.md | 11 ++- 57 files changed, 811 insertions(+), 532 deletions(-) rename backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/{DomFunktionaer.kt => Funktionaer.kt} (74%) rename backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/{DomPferd.kt => Pferd.kt} (81%) rename backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/{DomReiter.kt => Reiter.kt} (69%) rename backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/{DomVerein.kt => Verein.kt} (82%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ExposedFunktionaerRepository.kt => funktionaer/FunktionaerExposedRepository.kt} (72%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ => funktionaer}/FunktionaerTable.kt (55%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{HorseRepositoryImpl.kt => pferd/HorseExposedRepository.kt} (81%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ => pferd}/HorseTable.kt (82%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ExposedReiterRepository.kt => reiter/ReiterExposedRepository.kt} (85%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ => reiter}/ReiterTable.kt (71%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ExposedVereinRepository.kt => verein/VereinExposedRepository.kt} (72%) rename backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/{ => verein}/VereinTable.kt (68%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40c4eefa..cf646e76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,19 @@ Versionierung folgt [Semantic Versioning](https://semver.org/lang/de/). --- +## [1.0.1-SNAPSHOT] — 2026-04-05 + +### Geändert +- **Refactoring:** `DomVerein` zu `Verein`, `DomReiter` zu `Reiter`, `DomPferd` zu `Pferd` und `DomFunktionaer` zu `Funktionaer` umbenannt (Domain, Infrastructure, API, Core). +- **Domain:** `personId` ist nun optional (`nullable`) bei `Verein`, `Reiter`, `Pferd` und `Funktionaer`, um ZNS-Initialimporte zu unterstützen. +- **Infrastructure:** `VereinTable`, `ReiterTable`, `HorseTable` und `FunktionaerTable` synchronisiert; `personId` ist nun optional. +- **API:** `VereinController`, `ReiterController`, `HorseController` und `FunktionaerController` (DTOs/Requests) an die neuen Modelle angepasst. +- **Doku:** `Ubiquitous_Language.md` und `MASTER_ROADMAP.md` an das neue Namensschema (`Reiter`, `Pferd`, `Funktionaer`) angepasst. + +### Behoben +- **ZNS-Import:** Kompatibilitätsprobleme in `ZnsLegacyParsers` und `ZnsImportService` nach Domain-Refactorings behoben (UUID-Person-Referenzen und Enum-Synchronisation). +- **Domain:** Felder `kurzname` und `oepsRegionNummer` bei `Verein` entfernt (nicht in VEREIN01.DAT vorhanden). + ## [1.0.0-SNAPSHOT] — 2026-04-03 ### Hinzugefügt diff --git a/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt b/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt index 26477aae..4b34a389 100644 --- a/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt +++ b/backend/infrastructure/zns-importer/src/main/kotlin/at/mocode/zns/importer/ZnsImportService.kt @@ -19,10 +19,10 @@ import java.util.zip.ZipInputStream * Domänenobjekte über die jeweiligen Repositories (Upsert-Logik). * * Die Verarbeitungsreihenfolge ist fix: - * 1. VEREIN01.DAT → DomVerein (via VereinRepository) - * 2. LIZENZ01.DAT → DomReiter (via ReiterRepository) - * 3. PFERDE01.DAT → DomPferd (via HorseRepository) - * 4. RICHT01.DAT → DomFunktionaer (via FunktionaerRepository) + * 1. VEREIN01.DAT → Verein (via VereinRepository) + * 2. LIZENZ01.DAT → Reiter (via ReiterRepository) + * 3. PFERDE01.DAT → Pferd (via HorseRepository) + * 4. RICHT01.DAT → Funktionaer (via FunktionaerRepository) * * Dieser Service hat **keine** Spring-Abhängigkeit und kann daher sowohl * im Backend (REST-Upload) als auch in der Compose Desktop App (Offline-Import) @@ -57,18 +57,19 @@ class ZnsImportService( ZipInputStream(zipInputStream).use { zip -> var entry = zip.nextEntry while (entry != null) { - val entryPath = entry.name.uppercase() - val fileName = entryPath.substringAfterLast("/") + val fileName = entry.name.uppercase().substringAfterLast("/") if (fileName in setOf(FILE_VEREIN, FILE_LIZENZ, FILE_PFERDE, FILE_RICHT)) { - // Read all bytes from the current entry - val bytes = zip.readBytes() - // Convert to string using the correct charset and split into lines - val content = bytes.toString(CP850) + val outputStream = java.io.ByteArrayOutputStream() + val buffer = ByteArray(4096) + var len: Int + while (zip.read(buffer).also { len = it } > 0) { + outputStream.write(buffer, 0, len) + } + val content = outputStream.toString(CP850) val lines = content.split(Regex("\\r?\\n|\\r")).filter { it.isNotBlank() } dateien[fileName] = lines } - zip.closeEntry() entry = zip.nextEntry } } @@ -83,6 +84,8 @@ class ZnsImportService( */ suspend fun importiereZip(zipInputStream: InputStream): ZnsImportResult { val dateien = extrahiereDateien(zipInputStream) + // println("[DEBUG_LOG] Gefundene Dateien: ${dateien.keys}") + // dateien.forEach { (name, lines) -> println("[DEBUG_LOG] Datei $name hat ${lines.size} Zeilen") } val fehler = mutableListOf() val warnungen = mutableListOf() @@ -126,13 +129,11 @@ class ZnsImportService( } else { vereinRepository.save( vorhanden.copy( - name = verein.name, - kurzname = verein.kurzname, + vereinName = verein.vereinName, bundesland = verein.bundesland, ort = verein.ort, plz = verein.plz, strasse = verein.strasse, - oepsRegionNummer = verein.oepsRegionNummer, istAktiv = verein.istAktiv, datenQuelle = verein.datenQuelle ).withUpdatedTimestamp() @@ -207,9 +208,12 @@ class ZnsImportService( val pferd = ZnsLegacyParsers.parsePferd(zeile) ?: return@forEachIndexed if (pferd.pferdeName.isBlank()) return@forEachIndexed - // Match primarily by satznummer, then by lebensnummer, then by kopfnummer+name + // Match primarily by satznummer, then by lebensnummer val vorhanden = pferd.satznummer?.takeIf { it.isNotBlank() }?.let { horseRepository.findBySatznummer(it) } ?: pferd.lebensnummer?.takeIf { it.isNotBlank() }?.let { horseRepository.findByLebensnummer(it) } + ?: (if (pferd.pferdeName.isNotBlank()) horseRepository.findByName(pferd.pferdeName, 100).find { + it.geburtsjahr == pferd.geburtsjahr && it.kopfnummer == pferd.kopfnummer + } else null) if (vorhanden == null) { horseRepository.save(pferd) @@ -253,7 +257,7 @@ class ZnsImportService( zeilen.forEachIndexed { index, zeile -> runCatching { val funktionaer = ZnsLegacyParsers.parseFunktionaer(zeile) ?: return@forEachIndexed - val satzID = funktionaer.satzID + val satzID = funktionaer.satzId val satzNummer = funktionaer.satzNummer val vorhanden = funktionaerRepository.findBySatz(satzID, satzNummer) if (vorhanden == null) { @@ -262,8 +266,6 @@ class ZnsImportService( } else { funktionaerRepository.save( vorhanden.copy( - satzID = satzID, - satzNummer = satzNummer, name = funktionaer.name, qualifikationen = funktionaer.qualifikationen, istAktiv = funktionaer.istAktiv, diff --git a/backend/infrastructure/zns-importer/src/test/kotlin/at/mocode/zns/importer/ZnsImportServiceTest.kt b/backend/infrastructure/zns-importer/src/test/kotlin/at/mocode/zns/importer/ZnsImportServiceTest.kt index da988f36..d1b766e0 100644 --- a/backend/infrastructure/zns-importer/src/test/kotlin/at/mocode/zns/importer/ZnsImportServiceTest.kt +++ b/backend/infrastructure/zns-importer/src/test/kotlin/at/mocode/zns/importer/ZnsImportServiceTest.kt @@ -1,9 +1,9 @@ package at.mocode.zns.importer -import at.mocode.masterdata.domain.model.DomFunktionaer -import at.mocode.masterdata.domain.model.DomPferd -import at.mocode.masterdata.domain.model.DomReiter -import at.mocode.masterdata.domain.model.DomVerein +import at.mocode.masterdata.domain.model.Funktionaer +import at.mocode.masterdata.domain.model.Pferd +import at.mocode.masterdata.domain.model.Reiter +import at.mocode.masterdata.domain.model.Verein import at.mocode.masterdata.domain.repository.FunktionaerRepository import at.mocode.masterdata.domain.repository.HorseRepository import at.mocode.masterdata.domain.repository.ReiterRepository @@ -113,29 +113,29 @@ class ZnsImportServiceTest { val zip = buildZip("VEREIN01.DAT" to vereinZeile()) coEvery { vereinRepository.findByVereinsNummer(any()) } returns null - coEvery { vereinRepository.save(any()) } answers { firstArg() } + coEvery { vereinRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) assertThat(result.vereineImportiert).isEqualTo(1) assertThat(result.vereineAktualisiert).isEqualTo(0) assertThat(result.fehler).isEmpty() - coVerify(exactly = 1) { vereinRepository.save(any()) } + coVerify(exactly = 1) { vereinRepository.save(any()) } } @Test fun `importiereZip - bestehende Vereine werden aktualisiert`() = runTest { val zip = buildZip("VEREIN01.DAT" to vereinZeile(name = "Neuer Name")) - val vorhanden = DomVerein(vereinsNummer = "0001", name = "Alter Name") + val vorhanden = Verein(vereinsNummer = "0001", vereinName = "Alter Name") coEvery { vereinRepository.findByVereinsNummer("0001") } returns vorhanden - coEvery { vereinRepository.save(any()) } answers { firstArg() } + coEvery { vereinRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) assertThat(result.vereineImportiert).isEqualTo(0) assertThat(result.vereineAktualisiert).isEqualTo(1) - coVerify(exactly = 1) { vereinRepository.save(match { it.name == "Neuer Name" }) } + coVerify(exactly = 1) { vereinRepository.save(match { it.vereinName == "Neuer Name" }) } } @Test @@ -143,14 +143,14 @@ class ZnsImportServiceTest { val zip = buildZip("LIZENZ01.DAT" to lizenzZeile()) coEvery { reiterRepository.findBySatznummer(any()) } returns null - coEvery { reiterRepository.save(any()) } answers { firstArg() } + coEvery { reiterRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) assertThat(result.reiterImportiert).isEqualTo(1) assertThat(result.reiterAktualisiert).isEqualTo(0) assertThat(result.fehler).isEmpty() - coVerify(exactly = 1) { reiterRepository.save(any()) } + coVerify(exactly = 1) { reiterRepository.save(any()) } } @Test @@ -159,14 +159,34 @@ class ZnsImportServiceTest { coEvery { horseRepository.findBySatznummer(any()) } returns null coEvery { horseRepository.findByLebensnummer(any()) } returns null - coEvery { horseRepository.save(any()) } answers { firstArg() } + coEvery { horseRepository.findByName(any(), any()) } returns emptyList() + coEvery { horseRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) assertThat(result.pferdeImportiert).isEqualTo(1) assertThat(result.pferdeAktualisiert).isEqualTo(0) assertThat(result.fehler).isEmpty() - coVerify(exactly = 1) { horseRepository.save(any()) } + coVerify(exactly = 1) { horseRepository.save(any()) } + } + + @Test + fun `importiereZip - Pferde werden ueber Kopfnummer und Geburtsjahr aktualisiert, wenn Satznummer fehlt`() = runTest { + // Zeile ohne Satznummer (zu kurz) + val zeile = "0001Blitz " + val zip = buildZip("PFERDE01.DAT" to zeile) + + val existing = Pferd(pferdeName = "Blitz", kopfnummer = "0001", geschlecht = at.mocode.core.domain.model.PferdeGeschlechtE.UNBEKANNT) + + coEvery { horseRepository.findBySatznummer(any()) } returns null + coEvery { horseRepository.findByLebensnummer(any()) } returns null + coEvery { horseRepository.findByName("Blitz", 100) } returns listOf(existing) + coEvery { horseRepository.save(any()) } answers { firstArg() } + + val result = service.importiereZip(zip) + + assertThat(result.pferdeAktualisiert).isEqualTo(1) + coVerify(exactly = 1) { horseRepository.save(match { it.pferdeName == "Blitz" && it.pferdId == existing.pferdId }) } } @Test @@ -174,14 +194,14 @@ class ZnsImportServiceTest { val zip = buildZip("RICHT01.DAT" to funktionaerZeile()) coEvery { funktionaerRepository.findBySatz(any(), any()) } returns null - coEvery { funktionaerRepository.save(any()) } answers { firstArg() } + coEvery { funktionaerRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) assertThat(result.richterImportiert).isEqualTo(1) assertThat(result.richterAktualisiert).isEqualTo(0) assertThat(result.fehler).isEmpty() - coVerify(exactly = 1) { funktionaerRepository.save(any()) } + coVerify(exactly = 1) { funktionaerRepository.save(any()) } } @Test @@ -192,13 +212,13 @@ class ZnsImportServiceTest { ) coEvery { funktionaerRepository.findBySatz(any(), any()) } returns null - coEvery { funktionaerRepository.save(any()) } answers { firstArg() } + coEvery { funktionaerRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) assertThat(result.richterImportiert).isEqualTo(4) assertThat(result.fehler).isEmpty() - coVerify(exactly = 4) { funktionaerRepository.save(any()) } + coVerify(exactly = 4) { funktionaerRepository.save(any()) } } @Test @@ -211,14 +231,14 @@ class ZnsImportServiceTest { ) coEvery { vereinRepository.findByVereinsNummer(any()) } returns null - coEvery { vereinRepository.save(any()) } answers { firstArg() } + coEvery { vereinRepository.save(any()) } answers { firstArg() } coEvery { reiterRepository.findBySatznummer(any()) } returns null - coEvery { reiterRepository.save(any()) } answers { firstArg() } + coEvery { reiterRepository.save(any()) } answers { firstArg() } coEvery { horseRepository.findBySatznummer(any()) } returns null coEvery { horseRepository.findByLebensnummer(any()) } returns null - coEvery { horseRepository.save(any()) } answers { firstArg() } + coEvery { horseRepository.save(any()) } answers { firstArg() } coEvery { funktionaerRepository.findBySatz(any(), any()) } returns null - coEvery { funktionaerRepository.save(any()) } answers { firstArg() } + coEvery { funktionaerRepository.save(any()) } answers { firstArg() } val result = service.importiereZip(zip) diff --git a/backend/services/entries/entries-api/src/commonMain/kotlin/at/mocode/entries/api/NennungDtos.kt b/backend/services/entries/entries-api/src/commonMain/kotlin/at/mocode/entries/api/NennungDtos.kt index 4f6f2d17..da8f6e4f 100644 --- a/backend/services/entries/entries-api/src/commonMain/kotlin/at/mocode/entries/api/NennungDtos.kt +++ b/backend/services/entries/entries-api/src/commonMain/kotlin/at/mocode/entries/api/NennungDtos.kt @@ -2,7 +2,7 @@ package at.mocode.entries.api -import at.mocode.core.domain.model.NennungsStatusE +import at.mocode.core.domain.model.NennStatusE import at.mocode.core.domain.model.StartwunschE import at.mocode.core.domain.serialization.UuidSerializer import kotlinx.serialization.Serializable @@ -13,28 +13,28 @@ import kotlin.uuid.Uuid */ @Serializable data class NennungDetailDto( - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val nennungId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val abteilungId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val bewerbId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val turnierId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val reiterId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val pferdId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val zahlerId: Uuid? = null, - val status: NennungsStatusE, - val startwunsch: StartwunschE, - val istNachnennung: Boolean, - val nachnenngebuehrErlassen: Boolean, - val isNachnenngebuehrFaellig: Boolean, - val bemerkungen: String? = null, - val createdAt: String, - val updatedAt: String + val status: NennStatusE, + val startwunsch: StartwunschE, + val istNachnennung: Boolean, + val nachnenngebuehrErlassen: Boolean, + val isNachnenngebuehrFaellig: Boolean, + val bemerkungen: String? = null, + val createdAt: String, + val updatedAt: String ) /** @@ -42,21 +42,21 @@ data class NennungDetailDto( */ @Serializable data class NennungSummaryDto( - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val nennungId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val turnierId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val bewerbId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val abteilungId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val reiterId: Uuid, - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val pferdId: Uuid, - val status: NennungsStatusE, - val istNachnennung: Boolean, - val createdAt: String + val status: NennStatusE, + val istNachnennung: Boolean, + val createdAt: String ) /** @@ -86,8 +86,8 @@ data class NennungEinreichenRequest( */ @Serializable data class NennungStatusAendernRequest( - val neuerStatus: NennungsStatusE, - val bemerkungen: String? = null + val neuerStatus: NennStatusE, + val bemerkungen: String? = null ) /** diff --git a/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/model/DomNennung.kt b/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/model/DomNennung.kt index dc3f9181..cd22dbb4 100644 --- a/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/model/DomNennung.kt +++ b/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/model/DomNennung.kt @@ -2,7 +2,7 @@ package at.mocode.entries.domain.model -import at.mocode.core.domain.model.NennungsStatusE +import at.mocode.core.domain.model.NennStatusE import at.mocode.core.domain.model.StartwunschE import at.mocode.core.domain.serialization.InstantSerializer import at.mocode.core.domain.serialization.UuidSerializer @@ -28,8 +28,8 @@ import kotlin.uuid.Uuid * @property abteilungId Reference to the Abteilung (smallest entry unit). * @property bewerbId Reference to the parent Bewerb (for display/reporting). * @property turnierId Reference to the Turnier. - * @property reiterId Reference to the DomReiter (actor-context). - * @property pferdId Reference to the DomPferd (actor-context). + * @property reiterId Reference to the Reiter (actor-context). + * @property pferdId Reference to the Pferd (actor-context). * @property zahlerId Reference to the payer (may differ from rider, e.g. club pays). * @property status Current status of this entry. * @property startwunsch Rider's preferred starting position (vorne/hinten). @@ -63,7 +63,7 @@ data class DomNennung( val zahlerId: Uuid? = null, // Entry Details - val status: NennungsStatusE = NennungsStatusE.EINGEGANGEN, + val status: NennStatusE = NennStatusE.EINGEGANGEN, val startwunsch: StartwunschE = StartwunschE.KEIN_WUNSCH, // Late Entry (Nachnennung) @@ -83,8 +83,8 @@ data class DomNennung( * Checks if this entry is still active (not withdrawn or cancelled). */ fun isAktiv(): Boolean = status !in listOf( - NennungsStatusE.ZURUECKGEZOGEN, - NennungsStatusE.NICHT_ANGETRETEN + NennStatusE.ZURUECKGEZOGEN, + NennStatusE.NICHT_ANGETRETEN ) /** diff --git a/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/repository/NennungRepository.kt b/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/repository/NennungRepository.kt index f71178e0..1a303dfe 100644 --- a/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/repository/NennungRepository.kt +++ b/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/repository/NennungRepository.kt @@ -2,7 +2,7 @@ package at.mocode.entries.domain.repository -import at.mocode.core.domain.model.NennungsStatusE +import at.mocode.core.domain.model.NennStatusE import at.mocode.entries.domain.model.DomNennung import kotlin.uuid.Uuid @@ -52,7 +52,7 @@ interface NennungRepository { /** * Sucht alle Nennungen mit einem bestimmten Status. */ - suspend fun findByStatus(status: NennungsStatusE): List + suspend fun findByStatus(status: NennStatusE): List /** * Sucht alle Nachnennungen für einen Bewerb. @@ -84,5 +84,5 @@ interface NennungRepository { /** * Zählt alle Nennungen für ein Turnier mit einem bestimmten Status. */ - suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennungsStatusE): Long + suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennStatusE): Long } diff --git a/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/service/AbteilungsRegelService.kt b/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/service/AbteilungsRegelService.kt index 998168e3..71362c0b 100644 --- a/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/service/AbteilungsRegelService.kt +++ b/backend/services/entries/entries-domain/src/main/kotlin/at/mocode/entries/domain/service/AbteilungsRegelService.kt @@ -3,12 +3,12 @@ package at.mocode.entries.domain.service import at.mocode.core.domain.model.AbteilungsTeilungsTypE -import at.mocode.core.domain.model.LizenzKlasseE +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.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter /** * Service für die Anwendung der Abteilungs-Regeln gemäß ÖTO § 39. @@ -37,14 +37,14 @@ class AbteilungsRegelService { fun bestimmeAbteilung( bewerb: DomBewerb, abteilungen: List, - reiter: DomReiter + reiter: Reiter ): DomAbteilung? { if (abteilungen.isEmpty()) return null if (abteilungen.size == 1) return abteilungen.first() return when (bewerb.teilungsTyp) { AbteilungsTeilungsTypE.NACH_LIZENZ -> { - val istLizenzfrei = reiter.lizenzKlasse == LizenzKlasseE.LIZENZFREI + val istLizenzfrei = reiter.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI if (istLizenzfrei) { // Suche Abteilung für Lizenzfreie (oft Abt. 1 oder explizit benannt) abteilungen.find { it.bezeichnung?.contains("frei", ignoreCase = true) == true } @@ -61,7 +61,7 @@ class AbteilungsRegelService { AbteilungsTeilungsTypE.STRUKTURELL -> { // Bei strukturellen Teilungen (z.B. Caprilli oder CSN-C-NEU) if (bewerb.pruefungsTyp == PruefungsTypE.CAPRILLI) { - val istLizenzfrei = reiter.lizenzKlasse == LizenzKlasseE.LIZENZFREI + val istLizenzfrei = reiter.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI if (istLizenzfrei) abteilungen.find { it.abteilungsNummer == 1 } else abteilungen.find { it.abteilungsNummer == 2 } } else if (bewerb.sparte == SparteE.SPRINGEN && bewerb.hoeheCm != null) { @@ -70,7 +70,7 @@ class AbteilungsRegelService { // ≥ 100 cm: nur R1+ erlaubt → alle in Abt. 1 (R1/R2+) val hoehe = bewerb.hoeheCm!! if (hoehe <= 95) { - val istLizenzfrei = reiter.lizenzKlasse == LizenzKlasseE.LIZENZFREI + val istLizenzfrei = reiter.lizenzKlasse == ReiterLizenzKlasseE.LIZENZFREI if (istLizenzfrei) { abteilungen.find { it.bezeichnung?.contains("ohne", ignoreCase = true) == true } ?: abteilungen.minByOrNull { it.abteilungsNummer } @@ -80,7 +80,7 @@ class AbteilungsRegelService { } } else { // ≥ 100 cm: Pflicht-Teilung R1 / R2+ - val istR1 = reiter.lizenzKlasse == LizenzKlasseE.R1 + val istR1 = reiter.lizenzKlasse == ReiterLizenzKlasseE.R1 if (istR1) { abteilungen.find { it.bezeichnung?.contains("R1", ignoreCase = false) == true } ?: abteilungen.minByOrNull { it.abteilungsNummer } diff --git a/backend/services/entries/entries-domain/src/test/kotlin/at/mocode/entries/domain/service/AbteilungsRegelServiceTest.kt b/backend/services/entries/entries-domain/src/test/kotlin/at/mocode/entries/domain/service/AbteilungsRegelServiceTest.kt index 3aadc276..9235764e 100644 --- a/backend/services/entries/entries-domain/src/test/kotlin/at/mocode/entries/domain/service/AbteilungsRegelServiceTest.kt +++ b/backend/services/entries/entries-domain/src/test/kotlin/at/mocode/entries/domain/service/AbteilungsRegelServiceTest.kt @@ -5,7 +5,7 @@ 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.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter import kotlin.test.Test import kotlin.test.assertEquals import kotlin.uuid.Uuid @@ -30,7 +30,7 @@ class AbteilungsRegelServiceTest { val abt1 = createAbteilung(bewerb.bewerbId, 1, "lizenzfrei") val abt2 = createAbteilung(bewerb.bewerbId, 2, "R1 und hoeher") - val reiter = createReiter(lizenzKlasse = LizenzKlasseE.LIZENZFREI) + val reiter = createReiter(lizenzKlasse = ReiterLizenzKlasseE.LIZENZFREI) val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), reiter) @@ -43,7 +43,7 @@ class AbteilungsRegelServiceTest { val abt1 = createAbteilung(bewerb.bewerbId, 1, "lizenzfrei") val abt2 = createAbteilung(bewerb.bewerbId, 2, "Lizenz") - val reiter = createReiter(lizenzKlasse = LizenzKlasseE.R1) + val reiter = createReiter(lizenzKlasse = ReiterLizenzKlasseE.R1) val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), reiter) @@ -68,7 +68,7 @@ class AbteilungsRegelServiceTest { val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 95) val abtOhne = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz") val abtMit = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz") - val reiter = createReiter(LizenzKlasseE.LIZENZFREI) + val reiter = createReiter(ReiterLizenzKlasseE.LIZENZFREI) val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), reiter) @@ -80,7 +80,7 @@ class AbteilungsRegelServiceTest { val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 95) val abtOhne = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz") val abtMit = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz") - val reiter = createReiter(LizenzKlasseE.R1) + val reiter = createReiter(ReiterLizenzKlasseE.R1) val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), reiter) @@ -93,7 +93,7 @@ class AbteilungsRegelServiceTest { val abtOhne = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz") val abtMit = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz") - val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), createReiter(LizenzKlasseE.LIZENZFREI)) + val result = service.bestimmeAbteilung(bewerb, listOf(abtOhne, abtMit), createReiter(ReiterLizenzKlasseE.LIZENZFREI)) assertEquals(abtOhne.abteilungId, result?.abteilungId) } @@ -105,7 +105,7 @@ class AbteilungsRegelServiceTest { val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 100) val abtR1 = createAbteilung(bewerb.bewerbId, 1, "R1") val abtR2 = createAbteilung(bewerb.bewerbId, 2, "R2+") - val reiter = createReiter(LizenzKlasseE.R1) + val reiter = createReiter(ReiterLizenzKlasseE.R1) val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), reiter) @@ -117,7 +117,7 @@ class AbteilungsRegelServiceTest { val bewerb = createBewerb(teilungsTyp = AbteilungsTeilungsTypE.STRUKTURELL, hoeheCm = 100) val abtR1 = createAbteilung(bewerb.bewerbId, 1, "R1") val abtR2 = createAbteilung(bewerb.bewerbId, 2, "R2+") - val reiter = createReiter(LizenzKlasseE.R2) + val reiter = createReiter(ReiterLizenzKlasseE.R2) val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), reiter) @@ -130,7 +130,7 @@ class AbteilungsRegelServiceTest { val abtR1 = createAbteilung(bewerb.bewerbId, 1, "R1") val abtR2 = createAbteilung(bewerb.bewerbId, 2, "R2+") - val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), createReiter(LizenzKlasseE.R1)) + val result = service.bestimmeAbteilung(bewerb, listOf(abtR1, abtR2), createReiter(ReiterLizenzKlasseE.R1)) assertEquals(abtR1.abteilungId, result?.abteilungId) } @@ -191,7 +191,7 @@ class AbteilungsRegelServiceTest { val abt1 = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz") val abt2 = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz") - val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(LizenzKlasseE.LIZENZFREI)) + val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(ReiterLizenzKlasseE.LIZENZFREI)) assertEquals(abt1.abteilungId, result?.abteilungId) } @@ -202,7 +202,7 @@ class AbteilungsRegelServiceTest { val abt1 = createAbteilung(bewerb.bewerbId, 1, "ohne Lizenz") val abt2 = createAbteilung(bewerb.bewerbId, 2, "mit Lizenz") - val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(LizenzKlasseE.R1)) + val result = service.bestimmeAbteilung(bewerb, listOf(abt1, abt2), createReiter(ReiterLizenzKlasseE.R1)) assertEquals(abt2.abteilungId, result?.abteilungId) } @@ -236,7 +236,7 @@ class AbteilungsRegelServiceTest { starterAnzahl = starterAnzahl ) - private fun createReiter(lizenzKlasse: LizenzKlasseE = LizenzKlasseE.LIZENZFREI) = DomReiter( + private fun createReiter(lizenzKlasse: ReiterLizenzKlasseE = ReiterLizenzKlasseE.LIZENZFREI) = Reiter( personId = Uuid.random(), satznummer = "123456", nachname = "Mustermann", diff --git a/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/persistence/NennungRepositoryImpl.kt b/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/persistence/NennungRepositoryImpl.kt index 4ad4ad8f..c4b1f765 100644 --- a/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/persistence/NennungRepositoryImpl.kt +++ b/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/persistence/NennungRepositoryImpl.kt @@ -2,7 +2,7 @@ package at.mocode.entries.service.persistence -import at.mocode.core.domain.model.NennungsStatusE +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.repository.NennungRepository @@ -32,7 +32,7 @@ class NennungRepositoryImpl : NennungRepository { reiterId = row[NennungTable.reiterId].toKotlinUuid(), pferdId = row[NennungTable.pferdId].toKotlinUuid(), zahlerId = row[NennungTable.zahlerId]?.toKotlinUuid(), - status = NennungsStatusE.valueOf(row[NennungTable.status]), + status = NennStatusE.valueOf(row[NennungTable.status]), startwunsch = StartwunschE.valueOf(row[NennungTable.startwunsch]), istNachnennung = row[NennungTable.istNachnennung], nachnenngebuehrErlassen = row[NennungTable.nachnenngebuehrErlassen], @@ -79,7 +79,7 @@ class NennungRepositoryImpl : NennungRepository { }.map(::rowToNennung) } - override suspend fun findByStatus(status: NennungsStatusE): List = tenantTransaction { + override suspend fun findByStatus(status: NennStatusE): List = tenantTransaction { NennungTable.selectAll().where { NennungTable.status eq status.name } .map(::rowToNennung) } @@ -145,7 +145,7 @@ class NennungRepositoryImpl : NennungRepository { NennungTable.selectAll().where { NennungTable.abteilungId eq abteilungId.toJavaUuid() }.count() } - override suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennungsStatusE): Long = tenantTransaction { + override suspend fun countByTurnierIdAndStatus(turnierId: Uuid, status: NennStatusE): Long = tenantTransaction { NennungTable.selectAll().where { (NennungTable.turnierId eq turnierId.toJavaUuid()) and (NennungTable.status eq status.name) diff --git a/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/usecase/NennungUseCases.kt b/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/usecase/NennungUseCases.kt index ea945c7c..993ddb0e 100644 --- a/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/usecase/NennungUseCases.kt +++ b/backend/services/entries/entries-service/src/main/kotlin/at/mocode/entries/service/usecase/NennungUseCases.kt @@ -2,7 +2,7 @@ package at.mocode.entries.service.usecase -import at.mocode.core.domain.model.NennungsStatusE +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 @@ -101,7 +101,7 @@ class NennungUseCases( val nennung = nennungRepository.findById(nennungId) ?: throw NoSuchElementException("Nennung nicht gefunden: $nennungId") - val updated = nennung.copy(status = NennungsStatusE.ZURUECKGEZOGEN).withUpdatedTimestamp() + val updated = nennung.copy(status = NennStatusE.ZURUECKGEZOGEN).withUpdatedTimestamp() val saved = nennungRepository.save(updated) log.info("Nennung zurückgezogen: nennungId={}", nennungId) return saved.toDetailDto() @@ -131,7 +131,7 @@ class NennungUseCases( } // 1. Ursprungs-Nennung schließen - val geschlosseneNennung = ursprung.copy(status = NennungsStatusE.TRANSFERIERT).withUpdatedTimestamp() + val geschlosseneNennung = ursprung.copy(status = NennStatusE.TRANSFERIERT).withUpdatedTimestamp() nennungRepository.save(geschlosseneNennung) // 2. Neue Nennung anlegen diff --git a/backend/services/entries/entries-service/src/test/kotlin/at/mocode/entries/service/tenant/EntriesIsolationIntegrationTest.kt b/backend/services/entries/entries-service/src/test/kotlin/at/mocode/entries/service/tenant/EntriesIsolationIntegrationTest.kt index e6efbeb2..74375c21 100644 --- a/backend/services/entries/entries-service/src/test/kotlin/at/mocode/entries/service/tenant/EntriesIsolationIntegrationTest.kt +++ b/backend/services/entries/entries-service/src/test/kotlin/at/mocode/entries/service/tenant/EntriesIsolationIntegrationTest.kt @@ -151,7 +151,7 @@ private fun DomNennung.Companion.random(now: kotlin.time.Instant): DomNennung { reiterId = Uuid.random(), pferdId = Uuid.random(), zahlerId = null, - status = at.mocode.core.domain.model.NennungsStatusE.EINGEGANGEN, + status = at.mocode.core.domain.model.NennStatusE.EINGEGANGEN, startwunsch = at.mocode.core.domain.model.StartwunschE.VORNE, istNachnennung = false, nachnenngebuehrErlassen = false, diff --git a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/FunktionaerController.kt b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/FunktionaerController.kt index 01c83de5..999b0bd8 100644 --- a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/FunktionaerController.kt +++ b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/FunktionaerController.kt @@ -3,7 +3,7 @@ package at.mocode.masterdata.api.rest import at.mocode.core.domain.serialization.InstantSerializer -import at.mocode.masterdata.domain.model.DomFunktionaer +import at.mocode.masterdata.domain.model.Funktionaer import at.mocode.masterdata.domain.repository.FunktionaerRepository import io.ktor.http.* import io.ktor.server.request.* @@ -96,8 +96,8 @@ class FunktionaerController(private val funktionaerRepository: FunktionaerReposi */ post { val req = call.receive() - val domFunktionaer = DomFunktionaer( - satzID = req.satzID, + val domFunktionaer = Funktionaer( + satzId = req.satzID, satzNummer = req.satzNummer, name = req.name, qualifikationen = req.qualifikationen, @@ -139,9 +139,9 @@ class FunktionaerController(private val funktionaerRepository: FunktionaerReposi private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() } - private fun DomFunktionaer.toDto() = FunktionaerDto( + private fun Funktionaer.toDto() = FunktionaerDto( funktionaerId = funktionaerId.toString(), - satzID = satzID, + satzID = satzId, satzNummer = satzNummer, name = name, qualifikationen = qualifikationen, diff --git a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/HorseController.kt b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/HorseController.kt index a04aa144..229e438c 100644 --- a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/HorseController.kt +++ b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/HorseController.kt @@ -4,7 +4,7 @@ package at.mocode.masterdata.api.rest import at.mocode.core.domain.model.PferdeGeschlechtE import at.mocode.core.domain.serialization.InstantSerializer -import at.mocode.masterdata.domain.model.DomPferd +import at.mocode.masterdata.domain.model.Pferd import at.mocode.masterdata.domain.repository.HorseRepository import io.ktor.http.* import io.ktor.server.request.* @@ -110,7 +110,7 @@ class HorseController(private val horseRepository: HorseRepository) { val req = call.receive() val geschlecht = runCatching { PferdeGeschlechtE.valueOf(req.geschlecht) }.getOrNull() ?: return@post call.respond(HttpStatusCode.BadRequest, "Ungültiges Geschlecht: ${req.geschlecht}") - val domPferd = DomPferd( + val pferd = Pferd( kopfnummer = req.kopfnummer, pferdeName = req.pferdeName, lebensnummer = req.lebensnummer, @@ -120,7 +120,7 @@ class HorseController(private val horseRepository: HorseRepository) { satznummer = req.satznummer, istAktiv = req.istAktiv ) - val saved = horseRepository.save(domPferd) + val saved = horseRepository.save(pferd) call.respond(HttpStatusCode.Created, saved.toDto()) } @@ -161,7 +161,7 @@ class HorseController(private val horseRepository: HorseRepository) { private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() } - private fun DomPferd.toDto() = HorseDto( + private fun Pferd.toDto() = HorseDto( pferdId = pferdId.toString(), kopfnummer = kopfnummer, pferdeName = pferdeName, diff --git a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/ReiterController.kt b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/ReiterController.kt index f1b74948..86ff1d51 100644 --- a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/ReiterController.kt +++ b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/ReiterController.kt @@ -2,10 +2,10 @@ package at.mocode.masterdata.api.rest -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE 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.model.Reiter import at.mocode.masterdata.domain.repository.ReiterRepository import io.ktor.http.* import io.ktor.server.request.* @@ -132,9 +132,9 @@ class ReiterController(private val reiterRepository: ReiterRepository) { */ post { val req = call.receive() - val lizenzKlasse = runCatching { LizenzKlasseE.valueOf(req.lizenzKlasse) }.getOrNull() + val lizenzKlasse = runCatching { ReiterLizenzKlasseE.valueOf(req.lizenzKlasse) }.getOrNull() ?: return@post call.respond(HttpStatusCode.BadRequest, "Ungültige lizenzKlasse: ${req.lizenzKlasse}") - val domReiter = DomReiter( + val domReiter = Reiter( personId = Uuid.random(), satznummer = req.satznummer, nachname = req.nachname, @@ -165,7 +165,7 @@ class ReiterController(private val reiterRepository: ReiterRepository) { val existing = reiterRepository.findById(id) ?: return@put call.respond(HttpStatusCode.NotFound) val req = call.receive() val lizenzKlasse = req.lizenzKlasse?.let { - runCatching { LizenzKlasseE.valueOf(it) }.getOrNull() + runCatching { ReiterLizenzKlasseE.valueOf(it) }.getOrNull() ?: return@put call.respond(HttpStatusCode.BadRequest, "Ungültige lizenzKlasse: $it") } ?: existing.lizenzKlasse val updated = existing.copy( @@ -202,7 +202,7 @@ class ReiterController(private val reiterRepository: ReiterRepository) { private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() } - private fun DomReiter.toDto() = ReiterDto( + private fun Reiter.toDto() = ReiterDto( reiterId = reiterId.toString(), satznummer = satznummer, nachname = nachname, diff --git a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/VereinController.kt b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/VereinController.kt index dc09b2b6..bc2d8345 100644 --- a/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/VereinController.kt +++ b/backend/services/masterdata/masterdata-api/src/main/kotlin/at/mocode/masterdata/api/rest/VereinController.kt @@ -3,7 +3,7 @@ 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.model.Verein import at.mocode.masterdata.domain.repository.VereinRepository import io.ktor.http.* import io.ktor.server.request.* @@ -24,7 +24,6 @@ class VereinController(private val vereinRepository: VereinRepository) { val vereinId: String, val vereinsNummer: String, val name: String, - val kurzname: String? = null, val bundesland: String? = null, val ort: String? = null, val plz: String? = null, @@ -32,9 +31,9 @@ class VereinController(private val vereinRepository: VereinRepository) { val email: String? = null, val telefon: String? = null, val website: String? = null, - val oepsRegionNummer: String? = null, val istVeranstalter: Boolean, val istAktiv: Boolean, + val imageUrl: String? = null, val bemerkungen: String? = null, @Serializable(with = InstantSerializer::class) val updatedAt: Instant @@ -44,7 +43,6 @@ class VereinController(private val vereinRepository: VereinRepository) { data class VereinCreateRequest( val vereinsNummer: String, val name: String, - val kurzname: String? = null, val bundesland: String? = null, val ort: String? = null, val plz: String? = null, @@ -52,16 +50,15 @@ class VereinController(private val vereinRepository: VereinRepository) { val email: String? = null, val telefon: String? = null, val website: String? = null, - val oepsRegionNummer: String? = null, val istVeranstalter: Boolean = false, val istAktiv: Boolean = true, + val imageUrl: String? = null, val bemerkungen: String? = null ) @Serializable data class VereinUpdateRequest( val name: String? = null, - val kurzname: String? = null, val bundesland: String? = null, val ort: String? = null, val plz: String? = null, @@ -69,9 +66,9 @@ class VereinController(private val vereinRepository: VereinRepository) { val email: String? = null, val telefon: String? = null, val website: String? = null, - val oepsRegionNummer: String? = null, val istVeranstalter: Boolean? = null, val istAktiv: Boolean? = null, + val imageUrl: String? = null, val bemerkungen: String? = null ) @@ -95,7 +92,7 @@ class VereinController(private val vereinRepository: VereinRepository) { } /** - * GET /verein/search?q=... — Sucht Vereine nach Name oder Kurzname. + * GET /verein/search?q=... — Sucht Vereine nach Name. */ get("/search") { val query = call.request.queryParameters["q"] ?: "" @@ -126,10 +123,9 @@ class VereinController(private val vereinRepository: VereinRepository) { */ post { val req = call.receive() - val domVerein = DomVerein( + val domVerein = Verein( vereinsNummer = req.vereinsNummer, - name = req.name, - kurzname = req.kurzname, + vereinName = req.name, bundesland = req.bundesland, ort = req.ort, plz = req.plz, @@ -137,9 +133,9 @@ class VereinController(private val vereinRepository: VereinRepository) { email = req.email, telefon = req.telefon, website = req.website, - oepsRegionNummer = req.oepsRegionNummer, istVeranstalter = req.istVeranstalter, istAktiv = req.istAktiv, + imageUrl = req.imageUrl, bemerkungen = req.bemerkungen ) val saved = vereinRepository.save(domVerein) @@ -154,8 +150,7 @@ class VereinController(private val vereinRepository: VereinRepository) { val existing = vereinRepository.findById(id) ?: return@put call.respond(HttpStatusCode.NotFound) val req = call.receive() val updated = existing.copy( - name = req.name ?: existing.name, - kurzname = req.kurzname ?: existing.kurzname, + vereinName = req.name ?: existing.vereinName, bundesland = req.bundesland ?: existing.bundesland, ort = req.ort ?: existing.ort, plz = req.plz ?: existing.plz, @@ -163,9 +158,9 @@ class VereinController(private val vereinRepository: VereinRepository) { email = req.email ?: existing.email, telefon = req.telefon ?: existing.telefon, website = req.website ?: existing.website, - oepsRegionNummer = req.oepsRegionNummer ?: existing.oepsRegionNummer, istVeranstalter = req.istVeranstalter ?: existing.istVeranstalter, istAktiv = req.istAktiv ?: existing.istAktiv, + imageUrl = req.imageUrl ?: existing.imageUrl, bemerkungen = req.bemerkungen ?: existing.bemerkungen ) val saved = vereinRepository.save(updated) @@ -185,11 +180,10 @@ class VereinController(private val vereinRepository: VereinRepository) { private fun parseUuid(value: String?): Uuid? = value?.let { runCatching { Uuid.parse(it) }.getOrNull() } - private fun DomVerein.toDto() = VereinDto( + private fun Verein.toDto() = VereinDto( vereinId = vereinId.toString(), vereinsNummer = vereinsNummer, - name = name, - kurzname = kurzname, + name = vereinName, bundesland = bundesland, ort = ort, plz = plz, @@ -197,9 +191,9 @@ class VereinController(private val vereinRepository: VereinRepository) { email = email, telefon = telefon, website = website, - oepsRegionNummer = oepsRegionNummer, istVeranstalter = istVeranstalter, istAktiv = istAktiv, + imageUrl = imageUrl, bemerkungen = bemerkungen, updatedAt = updatedAt ) diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomFunktionaer.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Funktionaer.kt similarity index 74% rename from backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomFunktionaer.kt rename to backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Funktionaer.kt index a63558ea..60c78c38 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomFunktionaer.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Funktionaer.kt @@ -18,7 +18,7 @@ import kotlin.uuid.Uuid * aus dem ZNS geprüft. * * @property funktionaerId Eindeutige interne ID (UUID). - * @property satzID Typ des Satzes (X = Richter, Y = Parcoursbauer). Aus ZNS (RICHT01.DAT / PARCO01.DAT). + * @property satzId Typ des Satzes (X = Richter, Y = Parcoursbauer). Aus ZNS (RICHT01.DAT / PARCO01.DAT). * @property satzNummer Satznummer (6-stellig). Aus ZNS (RICHT01.DAT / PARCO01.DAT). * @property name Vollständiger Name (Nachname, Vorname). Aus ZNS (RICHT01.DAT / PARCO01.DAT). * @property qualifikation Qualifikationen (getrennt durch `,`). Aus ZNS (RICHT01.DAT / PARCO01.DAT). @@ -29,24 +29,42 @@ import kotlin.uuid.Uuid * @property updatedAt Letzter Änderungszeitpunkt. */ @Serializable -data class DomFunktionaer( +data class Funktionaer( @Serializable(with = UuidSerializer::class) val funktionaerId: Uuid = Uuid.random(), - val satzID: String, + + // Reference to base person + @Serializable(with = UuidSerializer::class) + val personId: Uuid? = null, + +// === ZNS.zip RICHT01.DAT === ANFANG === + + // Alphanumerisch (1) WERT "X" = RICHTER, "Y" = PARCOURSBAUER + var satzId: String, + + // Numerisch (6) FORMAT: 000000 val satzNummer: Int, + + // Alphanumerisch (75) var name: String? = null, // Nachname, Vorname + + // Alphanumerisch (30+) var qualifikationen: List = emptyList(), // Liste der Qualifikations-Kürzel -// var vorname: String, -// var nachname: String, -// var geburtsdatum: LocalDate? = null, -// val richterNummer: String? = null, -// var rollen: Set = emptySet(), -// var richterQualifikation: RichterQualifikationE? = null, -// var qualifiziertFuerSparten: Set = emptySet(), -// var email: String? = null, -// var telefon: String? = null, -// var vereinsNummer: String? = null, +// === ZNS.zip RICHT01.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 ort: String? = null, + var plz: String? = null, + var bundesland: String? = null, // Status & Verwaltung var istAktiv: Boolean = true, @@ -73,12 +91,12 @@ data class DomFunktionaer( /** * Prüft, ob der Funktionär als Richter qualifiziert ist. */ - fun istRichter(): Boolean = satzID.uppercase() == "X" + fun istRichter(): Boolean = satzId.uppercase() == "X" /** * Prüft, ob der Funktionär als Parcoursbauer qualifiziert ist. */ - fun istParcoursbauer(): Boolean = satzID.uppercase() == "Y" + fun istParcoursbauer(): Boolean = satzId.uppercase() == "Y" /** * Validiert die Pflichtfelder für den Turniereinsatz. @@ -97,5 +115,5 @@ data class DomFunktionaer( /** * Erstellt eine Kopie mit aktualisiertem Zeitstempel. */ - fun withUpdatedTimestamp(): DomFunktionaer = this.copy(updatedAt = Clock.System.now()) + fun withUpdatedTimestamp(): Funktionaer = this.copy(updatedAt = Clock.System.now()) } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/LicenseMatrixEntry.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/LicenseMatrixEntry.kt index b186b434..e7652e9b 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/LicenseMatrixEntry.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/LicenseMatrixEntry.kt @@ -2,7 +2,7 @@ package at.mocode.masterdata.domain.model -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.SparteE import at.mocode.core.domain.serialization.InstantSerializer import at.mocode.core.domain.serialization.UuidSerializer @@ -15,20 +15,20 @@ import kotlin.uuid.Uuid */ @Serializable data class LicenseMatrixEntry( - @Serializable(with = UuidSerializer::class) + @Serializable(with = UuidSerializer::class) val licenseId: Uuid = Uuid.random(), - val sparte: SparteE, - val lizenzKlasse: LizenzKlasseE, - val maxTurnierklasseCode: String, // E, A, L, LM, M, S + val sparte: SparteE, + val lizenzKlasse: ReiterLizenzKlasseE, + val maxTurnierklasseCode: String, // E, A, L, LM, M, S - @Serializable(with = InstantSerializer::class) + @Serializable(with = InstantSerializer::class) val validFrom: Instant, - @Serializable(with = InstantSerializer::class) + @Serializable(with = InstantSerializer::class) val validTo: Instant? = null, - val istAktiv: Boolean = true, - @Serializable(with = InstantSerializer::class) + val istAktiv: Boolean = true, + @Serializable(with = InstantSerializer::class) val createdAt: Instant, - @Serializable(with = InstantSerializer::class) + @Serializable(with = InstantSerializer::class) val updatedAt: Instant ) diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomPferd.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Pferd.kt similarity index 81% rename from backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomPferd.kt rename to backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Pferd.kt index 281e3278..2e1b5322 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomPferd.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Pferd.kt @@ -4,6 +4,7 @@ package at.mocode.masterdata.domain.model import at.mocode.core.domain.model.DatenQuelleE import at.mocode.core.domain.model.PferdeGeschlechtE +import at.mocode.core.domain.serialization.InstantSerializer import at.mocode.core.domain.serialization.UuidSerializer import kotlinx.datetime.todayIn import kotlinx.serialization.Serializable @@ -39,42 +40,67 @@ import kotlin.uuid.Uuid * @property updatedAt Timestamp when this record was last updated. */ @Serializable -data class DomPferd( +data class Pferd( @Serializable(with = UuidSerializer::class) val pferdId: Uuid = Uuid.random(), - // PFERDE01.DAT Information + // Reference to base person + @Serializable(with = UuidSerializer::class) + val personId: Uuid? = null, + +// === ZNS.zip PFERDE01.DAT === ANFANG === + + // Alphanumerisch (4) var kopfnummer: String? = null, + + // Alphanumerisch (30) var pferdeName: String, + + // Alphanumerisch (9) FORMAT: 000000000 var lebensnummer: String? = null, + + // Alphanumerisch (1) var geschlecht: PferdeGeschlechtE, + + // Numerisch (4) FORMAT: 0000 var geburtsjahr: Int? = null, + + // Alphanumerisch (15) var farbe: String? = null, + + // Alphanumerisch (15) var abstammung: String? = null, + + // Numerisch (4) FORMAT: 0000 var vereinNummer: Int? = null, + + // Numerisch (4) FORMAT: 0000 var lastPayYear: Int? = null, + + // Alphanumerisch (75) Standard: BLANK var verantwortlichePersonId: String? = null, + + // Alphanumerisch (30) Standard: BLANK var vater: String? = null, + + // Alphanumerisch (10) Standard: BLANK var feiPass: String? = null, + + // Alphanumerisch (10) FORMAT: 0000000000 var satznummer: String? = null, -// var geburtsdatum: LocalDate? = null, -// var rasse: String? = null, -// @Serializable(with = UuidSerializer::class) -// var besitzerId: Uuid? = null, -// var zuechterName: String? = null, -// var zuchtbuchNummer: String? = null, -// var chipNummer: String? = null, -// var passNummer: String? = null, -// var oepsNummer: String? = null, -// var mutterName: String? = null, -// var mutterVaterName: String? = null, -// var stockmass: Int? = null, // Height in cm +// === ZNS.zip PFERDE01.DAT === ENDE === - var istAktiv: Boolean = true, + + // Status & Verwaltung + val istAktiv: Boolean = true, var bemerkungen: String? = null, - var datenQuelle: DatenQuelleE = DatenQuelleE.MANUELL, - var createdAt: Instant = Clock.System.now(), + val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS, + + // Audit + @Serializable(with = InstantSerializer::class) + val createdAt: Instant = Clock.System.now(), + @Serializable(with = InstantSerializer::class) var updatedAt: Instant = Clock.System.now() ) { /** @@ -141,7 +167,7 @@ data class DomPferd( /** * Creates a copy of this horse with an updated timestamp. */ - fun withUpdatedTimestamp(): DomPferd { + fun withUpdatedTimestamp(): Pferd { return this.copy(updatedAt = Clock.System.now()) } } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomReiter.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Reiter.kt similarity index 69% rename from backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomReiter.kt rename to backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Reiter.kt index 878e4f06..372c4468 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomReiter.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Reiter.kt @@ -3,8 +3,7 @@ package at.mocode.masterdata.domain.model import at.mocode.core.domain.model.DatenQuelleE -import at.mocode.core.domain.model.LizenzKlasseE -import at.mocode.core.domain.model.SparteE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.serialization.InstantSerializer import at.mocode.core.domain.serialization.LocalDateSerializer import at.mocode.core.domain.serialization.UuidSerializer @@ -51,42 +50,101 @@ import kotlin.uuid.Uuid * @property updatedAt Timestamp when this record was last updated. */ @Serializable -data class DomReiter( +data class Reiter( @Serializable(with = UuidSerializer::class) val reiterId: Uuid = Uuid.random(), // Reference to base person @Serializable(with = UuidSerializer::class) - val personId: Uuid, + val personId: Uuid? = null, - // ZNS Identification +// === ZNS.zip LIZENZ01.DAT === ANFANG === + + // Alphanumerisch (6) FORMAT: 000000 var satznummer: String?, + + // Alphanumerisch (50) var nachname: String, + + // Alphanumerisch (25) var vorname: String, + + // Numerisch (2) FORMAT: 99 var bundeslandNummer: Int? = null, + + // Alphanumerisch (50) var vereinsName: String? = null, + + // Alphanumerisch (3) var nation: String? = null, + + // Alphanumerisch (4) Keine Lizenz: BLANK var reiterLizenz: String? = null, + + // Alphanumerisch (1) Keine Startkarte: BLANK var startkarte: String? = null, + + // Alphanumerisch (2) Keine Fahrlizenz: BLANK var fahrLizenz: String? = null, + + // Alphanumerisch (2) WERTE: Standard: BLANK, JG=JUGENDLICHER, JR=JUNIOR, 25=U25 var altersklasseJgJrU25: String? = null, + + // Alphanumerisch (1) WERTE: Standard: BLANK Y=JUNGER-REITER var altersklasseY: String? = null, + + // Numerisch (8) FORMAT: 00000000 var mitgliedsNummer: Int? = null, + + // Alphanumerisch (21) Standard: BLANK var telefonNummer: String? = null, + + // Alphanumerisch (1) Standard: BLANK var kader: String? = null, + + /** Numerisch (4). Letztes Jahr, in dem die Lizenz bezahlt wurde. */ var lastPayYear: Int? = null, + + /** Bestimmte Lizenzklasse (z.B. R1, R2, ...). */ + var lizenzKlasse: ReiterLizenzKlasseE = ReiterLizenzKlasseE.LIZENZFREI, + + // Alphanumerisch (1) WERTE: M=MÄNNLICH, W=WEIBLICH var geschlecht: String? = null, + + // Datum (8) FORMAT: YYYYMMDD @Serializable(with = LocalDateSerializer::class) var geburtsdatum: LocalDate? = null, + + // Alphanumerisch (10) Standard: BLANK var feiId: String? = null, + + // Alphanumerisch (1) Werte: BLANK = nicht auf Sperrliste, S=auf Sperrliste, dort nachsehen! var sperrListe: String? = null, + + // Alphanumerisch (10) Standard: BLANK var lizenzInfo: String? = null, - var lizenzKlasse: LizenzKlasseE = LizenzKlasseE.LIZENZFREI, +// === 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, val datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS, + + // Audit @Serializable(with = InstantSerializer::class) val createdAt: Instant = Clock.System.now(), @Serializable(with = InstantSerializer::class) @@ -112,19 +170,19 @@ data class DomReiter( fun hasLizenz(): Boolean = !reiterLizenz.isNullOrBlank() /** - * Checks if the rider has a license for a specific sparte. + * Checks if the rider holds a license for a specific discipline. + * Simple logic for now: Any non-blank license field counts. */ - fun hasLizenzForSparte(sparte: SparteE): Boolean { - // If we have a license class, check if it's applicable for the sparte - if (lizenzKlasse == LizenzKlasseE.LIZENZFREI) return false - + fun hasLizenzForSparte(sparte: at.mocode.core.domain.model.SparteE): Boolean { return when (sparte) { - SparteE.DRESSUR -> true // Everyone with a license can do dressage (simplified) - SparteE.SPRINGEN -> !listOf(LizenzKlasseE.RD1, LizenzKlasseE.RD2, LizenzKlasseE.RD3).contains(lizenzKlasse) - else -> true + at.mocode.core.domain.model.SparteE.DRESSUR -> !reiterLizenz.isNullOrBlank() + at.mocode.core.domain.model.SparteE.SPRINGEN -> !reiterLizenz.isNullOrBlank() + at.mocode.core.domain.model.SparteE.FAHREN -> !fahrLizenz.isNullOrBlank() + else -> hasLizenz() } } + /** * Validates the rider for competition entry. * Returns a list of warning messages (never hard errors – TBA has final say). @@ -146,5 +204,5 @@ data class DomReiter( /** * Creates a copy of this rider with an updated timestamp. */ - fun withUpdatedTimestamp(): DomReiter = this.copy(updatedAt = Clock.System.now()) + fun withUpdatedTimestamp(): Reiter = this.copy(updatedAt = Clock.System.now()) } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomVerein.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Verein.kt similarity index 82% rename from backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomVerein.kt rename to backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Verein.kt index 3e84c95f..4d4ac9be 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/DomVerein.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/model/Verein.kt @@ -21,8 +21,7 @@ import kotlin.uuid.Uuid * * @property vereinId Eindeutige interne ID (UUID). * @property vereinsNummer ÖPS-Vereinsnummer aus ZNS (VEREIN01.dat), 4-stellig. Primärschlüssel für ZNS-Datenaustausch. - * @property name Offizieller Vereinsname. - * @property kurzname Kurzbezeichnung des Vereins (optional). + * @property vereinName Offizieller Vereinsname. * @property bundesland Bundesland, in dem der Verein ansässig ist. * @property ort Ort / Stadt des Vereinssitzes. * @property plz Postleitzahl. @@ -30,7 +29,6 @@ import kotlin.uuid.Uuid * @property email Offizielle E-Mail-Adresse des Vereins. * @property telefon Telefonnummer des Vereins. * @property website Website-URL des Vereins. - * @property oepsRegionNummer Regionsnummer beim OEPS (Landesverband). * @property istVeranstalter Ob der Verein als Veranstalter von Turnieren zugelassen ist. * @property istAktiv Ob der Verein aktuell aktiv ist. * @property bemerkungen Interne Notizen. @@ -39,35 +37,40 @@ import kotlin.uuid.Uuid * @property updatedAt Letzter Änderungszeitpunkt. */ @Serializable -data class DomVerein( +data class Verein( @Serializable(with = UuidSerializer::class) val vereinId: Uuid = Uuid.random(), - // Identifikation + // Reference to base person + @Serializable(with = UuidSerializer::class) + val personId: Uuid? = null, + +// === ZNS.zip VEREIN01.DAT === ANFANG === + + /** Numerisch (4) FORMAT: 0000. Primärschlüssel für ZNS-Datenaustausch. */ val vereinsNummer: String, - // Stammdaten - var name: String, - var kurzname: String? = null, + /** Alphanumerisch (50). Offizieller Vereinsname. */ + var vereinName: String, - // Adresse - var bundesland: String? = null, - var ort: String? = null, - var plz: String? = null, - var strasse: String? = null, +// === ZNS.zip VEREIN01.DAT === ENDE === // Kontakt + var imageUrl: String? = null, var email: String? = null, var telefon: String? = null, var website: String? = null, - // OEPS-Verwaltung - var oepsRegionNummer: String? = null, - var istVeranstalter: Boolean = false, + // Adresse + var strasse: String? = null, + var hausnummer: String? = null, + var plz: String? = null, + var ort: String? = null, + var bundesland: String? = null, // Status & Verwaltung var istAktiv: Boolean = true, - var logoUrl: String? = null, + var istVeranstalter: Boolean = false, var bemerkungen: String? = null, var datenQuelle: DatenQuelleE = DatenQuelleE.IMPORT_ZNS, @@ -80,7 +83,7 @@ data class DomVerein( /** * Gibt den Anzeigenamen zurück – Kurzname bevorzugt, sonst vollständiger Name. */ - fun getDisplayName(): String = kurzname ?: name + fun getDisplayName(): String = vereinName /** * Gibt den vollständigen Anzeigenamen mit Vereinsnummer zurück. @@ -91,13 +94,13 @@ data class DomVerein( * Prüft, ob vollständige Adressdaten vorhanden sind. */ fun hasCompleteAddress(): Boolean = - !ort.isNullOrBlank() && !plz.isNullOrBlank() && !strasse.isNullOrBlank() + !strasse.isNullOrBlank() && !plz.isNullOrBlank() && !ort.isNullOrBlank() /** * Validiert den Verein für den Einsatz als Veranstalter. * Gibt Warnungen zurück (kein harter Fehler – Override-Event möglich). */ - fun validateFuerVeranstaltung(): List { + fun validateVeranstaltung(): List { val warnings = mutableListOf() if (!istAktiv) { @@ -118,5 +121,5 @@ data class DomVerein( /** * Erstellt eine Kopie mit aktualisiertem Zeitstempel. */ - fun withUpdatedTimestamp(): DomVerein = this.copy(updatedAt = Clock.System.now()) + fun withUpdatedTimestamp(): Verein = this.copy(updatedAt = Clock.System.now()) } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/FunktionaerRepository.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/FunktionaerRepository.kt index 755d2133..cf6fe8d4 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/FunktionaerRepository.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/FunktionaerRepository.kt @@ -2,11 +2,11 @@ package at.mocode.masterdata.domain.repository -import at.mocode.masterdata.domain.model.DomFunktionaer +import at.mocode.masterdata.domain.model.Funktionaer import kotlin.uuid.Uuid /** - * Repository-Interface für DomFunktionaer (Funktionär) Domain-Operationen. + * Repository-Interface für Funktionaer (Funktionär) Domain-Operationen. * * Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit * von konkreten Implementierungsdetails (Datenbank, etc.). @@ -16,22 +16,22 @@ interface FunktionaerRepository { /** * Sucht einen Funktionär anhand seiner eindeutigen ID. */ - suspend fun findById(id: Uuid): DomFunktionaer? + suspend fun findById(id: Uuid): Funktionaer? /** * Sucht einen Funktionär anhand seiner Satz-ID und Satznummer. */ - suspend fun findBySatz(satzID: String, satzNummer: Int): DomFunktionaer? + suspend fun findBySatz(satzID: String, satzNummer: Int): Funktionaer? /** * Gibt alle Funktionäre zurück (paginiert). */ - suspend fun findAll(limit: Int = 100, offset: Int = 0): List + suspend fun findAll(limit: Int = 100, offset: Int = 0): List /** * Speichert einen Funktionär (Insert oder Update). */ - suspend fun save(funktionaer: DomFunktionaer): DomFunktionaer + suspend fun save(funktionaer: Funktionaer): Funktionaer /** * Löscht einen Funktionär anhand seiner ID. diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/HorseRepository.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/HorseRepository.kt index 7c14a781..2f756fb9 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/HorseRepository.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/HorseRepository.kt @@ -3,11 +3,11 @@ package at.mocode.masterdata.domain.repository import at.mocode.core.domain.model.PferdeGeschlechtE -import at.mocode.masterdata.domain.model.DomPferd +import at.mocode.masterdata.domain.model.Pferd import kotlin.uuid.Uuid /** - * Repository interface for DomPferd (Horse) domain operations. + * Repository interface for Pferd (Horse) domain operations. * * This interface defines the contract for horse data access operations * without depending on specific implementation details (database, etc.). @@ -22,7 +22,7 @@ interface HorseRepository { * @param id The unique identifier of the horse * @return The horse if found, null otherwise */ - suspend fun findById(id: Uuid): DomPferd? + suspend fun findById(id: Uuid): Pferd? /** * Finds a horse by its life number (Lebensnummer). @@ -30,7 +30,7 @@ interface HorseRepository { * @param lebensnummer The life number to search for * @return The horse if found, null otherwise */ - suspend fun findByLebensnummer(lebensnummer: String): DomPferd? + suspend fun findByLebensnummer(lebensnummer: String): Pferd? /** * Finds a horse by its chip number. @@ -38,7 +38,7 @@ interface HorseRepository { * @param chipNummer The chip number to search for * @return The horse if found, null otherwise */ - suspend fun findByChipNummer(chipNummer: String): DomPferd? + suspend fun findByChipNummer(chipNummer: String): Pferd? /** * Finds a horse by its passport number. @@ -46,7 +46,7 @@ interface HorseRepository { * @param passNummer The passport number to search for * @return The horse if found, null otherwise */ - suspend fun findByPassNummer(passNummer: String): DomPferd? + suspend fun findByPassNummer(passNummer: String): Pferd? /** * Finds a horse by its OEPS number. @@ -54,7 +54,7 @@ interface HorseRepository { * @param oepsNummer The OEPS number to search for * @return The horse if found, null otherwise */ - suspend fun findByOepsNummer(oepsNummer: String): DomPferd? + suspend fun findByOepsNummer(oepsNummer: String): Pferd? /** * Finds a horse by its FEI number. @@ -62,7 +62,7 @@ interface HorseRepository { * @param feiNummer The FEI number to search for * @return The horse if found, null otherwise */ - suspend fun findByFeiNummer(feiNummer: String): DomPferd? + suspend fun findByFeiNummer(feiNummer: String): Pferd? /** * Finds horses by name (partial match). @@ -71,7 +71,7 @@ interface HorseRepository { * @param limit Maximum number of results to return * @return List of matching horses */ - suspend fun findByName(searchTerm: String, limit: Int = 50): List + suspend fun findByName(searchTerm: String, limit: Int = 50): List /** * Finds all horses owned by a specific person. @@ -80,7 +80,7 @@ interface HorseRepository { * @param activeOnly Whether to return only active horses * @return List of horses owned by the person */ - suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean = true): List + suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean = true): List /** * Finds all horses for which a person is responsible. @@ -89,7 +89,7 @@ interface HorseRepository { * @param activeOnly Whether to return only active horses * @return List of horses for which the person is responsible */ - suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean = true): List + suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean = true): List /** * Finds horses by gender. @@ -103,7 +103,7 @@ interface HorseRepository { geschlecht: PferdeGeschlechtE, activeOnly: Boolean = true, limit: Int = 100 - ): List + ): List /** * Finds horses by breed. @@ -113,7 +113,7 @@ interface HorseRepository { * @param limit Maximum number of results to return * @return List of horses of the specified breed */ - suspend fun findByRasse(rasse: String, activeOnly: Boolean = true, limit: Int = 100): List + suspend fun findByRasse(rasse: String, activeOnly: Boolean = true, limit: Int = 100): List /** * Finds horses by birth year. @@ -122,7 +122,7 @@ interface HorseRepository { * @param activeOnly Whether to return only active horses * @return List of horses born in the specified year */ - suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean = true): List + suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean = true): List /** * Finds horses by birth year range. @@ -132,7 +132,7 @@ interface HorseRepository { * @param activeOnly Whether to return only active horses * @return List of horses born within the specified year range */ - suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean = true): List + suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean = true): List /** * Finds all active horses. @@ -140,7 +140,7 @@ interface HorseRepository { * @param limit Maximum number of results to return * @return List of active horses */ - suspend fun findAllActive(limit: Int = 1000): List + suspend fun findAllActive(limit: Int = 1000): List /** * Finds horses with OEPS registration. @@ -148,7 +148,7 @@ interface HorseRepository { * @param activeOnly Whether to return only active horses * @return List of OEPS registered horses */ - suspend fun findOepsRegistered(activeOnly: Boolean = true): List + suspend fun findOepsRegistered(activeOnly: Boolean = true): List /** * Finds horses with FEI registration. @@ -156,7 +156,7 @@ interface HorseRepository { * @param activeOnly Whether to return only active horses * @return List of FEI registered horses */ - suspend fun findFeiRegistered(activeOnly: Boolean = true): List + suspend fun findFeiRegistered(activeOnly: Boolean = true): List /** * Saves a horse (create or update). @@ -164,7 +164,7 @@ interface HorseRepository { * @param horse The horse to save * @return The saved horse with updated timestamps */ - suspend fun save(horse: DomPferd): DomPferd + suspend fun save(horse: Pferd): Pferd /** * Deletes a horse by ID. @@ -252,7 +252,7 @@ interface HorseRepository { * @param kopfnummer The head number to search for * @return The list of horses found */ - suspend fun findByKopfnummer(kopfnummer: String): List + suspend fun findByKopfnummer(kopfnummer: String): List /** * Finds a horse by its ZNS satznummer. @@ -260,17 +260,17 @@ interface HorseRepository { * @param satznummer The ZNS satznummer to search for * @return The horse if found, null otherwise */ - suspend fun findBySatznummer(satznummer: String): DomPferd? + suspend fun findBySatznummer(satznummer: String): Pferd? /** * Speichert ein Pferd basierend auf der ZNS satznummer (Upsert). * Wenn ein Pferd mit der satznummer existiert, wird es aktualisiert, ansonsten neu angelegt. */ - suspend fun upsertBySatznummer(horse: DomPferd): DomPferd + suspend fun upsertBySatznummer(horse: Pferd): Pferd /** * 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 + suspend fun upsertByLebensnummer(horse: Pferd): Pferd } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/ReiterRepository.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/ReiterRepository.kt index 9bb9ce10..81f506f4 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/ReiterRepository.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/ReiterRepository.kt @@ -2,11 +2,11 @@ package at.mocode.masterdata.domain.repository -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter import kotlin.uuid.Uuid /** - * Repository-Interface für DomReiter (Reiter) Domain-Operationen. + * Repository-Interface für Reiter (Reiter) Domain-Operationen. * * Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit * von konkreten Implementierungsdetails (Datenbank etc.). @@ -16,22 +16,22 @@ interface ReiterRepository { /** * Sucht einen Reiter anhand seiner eindeutigen ID. */ - suspend fun findById(id: Uuid): DomReiter? + suspend fun findById(id: Uuid): Reiter? /** * Sucht einen Reiter anhand seiner Satznummer (OEPS-Mitgliedsnummer). */ - suspend fun findBySatznummer(satznummer: String?): DomReiter? + suspend fun findBySatznummer(satznummer: String?): Reiter? /** * Gibt alle Reiter zurück (paginiert). */ - suspend fun findAll(limit: Int = 100, offset: Int = 0): List + suspend fun findAll(limit: Int = 100, offset: Int = 0): List /** * Speichert einen Reiter (Insert oder Update). */ - suspend fun save(reiter: DomReiter): DomReiter + suspend fun save(reiter: Reiter): Reiter /** * Löscht einen Reiter anhand seiner ID. @@ -54,5 +54,5 @@ interface ReiterRepository { * 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 + suspend fun upsertBySatznummer(reiter: Reiter): Reiter } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/VereinRepository.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/VereinRepository.kt index 26fda534..3908c4d2 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/VereinRepository.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/repository/VereinRepository.kt @@ -2,11 +2,11 @@ package at.mocode.masterdata.domain.repository -import at.mocode.masterdata.domain.model.DomVerein +import at.mocode.masterdata.domain.model.Verein import kotlin.uuid.Uuid /** - * Repository-Interface für DomVerein (Verein) Domain-Operationen. + * Repository-Interface für Verein (Verein) Domain-Operationen. * * Definiert den Vertrag für Datenzugriffs-Operationen ohne Abhängigkeit * von konkreten Implementierungsdetails (Datenbank etc.). @@ -16,42 +16,42 @@ interface VereinRepository { /** * Sucht einen Verein anhand seiner eindeutigen ID. */ - suspend fun findById(id: Uuid): DomVerein? + suspend fun findById(id: Uuid): Verein? /** * Sucht einen Verein anhand seiner OEPS-Vereinsnummer. */ - suspend fun findByVereinsNummer(vereinsNummer: String): DomVerein? + suspend fun findByVereinsNummer(vereinsNummer: String): Verein? /** * Sucht Vereine anhand des Namens (Teilübereinstimmung). */ - suspend fun findByName(searchTerm: String, limit: Int = 50): List + suspend fun findByName(searchTerm: String, limit: Int = 50): List /** * Sucht alle Vereine eines Bundeslandes. */ - suspend fun findByBundesland(bundesland: String, activeOnly: Boolean = true): List + suspend fun findByBundesland(bundesland: String, activeOnly: Boolean = true): List /** * Sucht alle Vereine, die als Veranstalter markiert sind. */ - suspend fun findVeranstalter(activeOnly: Boolean = true): List + suspend fun findVeranstalter(activeOnly: Boolean = true): List /** * Gibt alle aktiven Vereine zurück (paginiert). */ - suspend fun findAllActive(limit: Int = 100, offset: Int = 0): List + suspend fun findAllActive(limit: Int = 100, offset: Int = 0): List /** * Gibt alle Vereine zurück (paginiert). */ - suspend fun findAll(limit: Int = 100, offset: Int = 0): List + suspend fun findAll(limit: Int = 100, offset: Int = 0): List /** * Speichert einen Verein (Insert oder Update). */ - suspend fun save(verein: DomVerein): DomVerein + suspend fun save(verein: Verein): Verein /** * Löscht einen Verein anhand seiner ID. @@ -74,5 +74,5 @@ interface VereinRepository { * 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 + suspend fun upsertByVereinsNummer(verein: Verein): Verein } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelService.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelService.kt index 4c9194b7..e16887a1 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelService.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelService.kt @@ -1,7 +1,7 @@ package at.mocode.masterdata.domain.service -import at.mocode.masterdata.domain.model.DomPferd -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Pferd +import at.mocode.masterdata.domain.model.Reiter /** * Service zur Prüfung von Abteilungs-Regeln gemäß ÖTO § 39. @@ -23,8 +23,8 @@ interface AbteilungsRegelService { * @return Die Abteilungsnummer (1, 2, 3), in die der Teilnehmer fällt. */ fun ermittleAbteilungStrukturell( - reiter: DomReiter, - pferd: DomPferd, + reiter: Reiter, + pferd: Pferd, turnierklasseCode: String, sparte: at.mocode.core.domain.model.SparteE, istCNeu: Boolean = false, diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceImpl.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceImpl.kt index 1ec1d891..c6c93d10 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceImpl.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceImpl.kt @@ -1,9 +1,9 @@ package at.mocode.masterdata.domain.service -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.SparteE -import at.mocode.masterdata.domain.model.DomPferd -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Pferd +import at.mocode.masterdata.domain.model.Reiter /** * Standard-Implementierung des [AbteilungsRegelService] gemäß ÖTO § 39. @@ -11,8 +11,8 @@ import at.mocode.masterdata.domain.model.DomReiter class AbteilungsRegelServiceImpl : AbteilungsRegelService { override fun ermittleAbteilungStrukturell( - reiter: DomReiter, - pferd: DomPferd, + reiter: Reiter, + pferd: Pferd, turnierklasseCode: String, sparte: SparteE, istCNeu: Boolean, @@ -24,13 +24,13 @@ class AbteilungsRegelServiceImpl : AbteilungsRegelService { if (istCNeu && sparte == SparteE.SPRINGEN) { if (hoehe != null && hoehe <= 95) { return when (reiter.lizenzKlasse) { - LizenzKlasseE.LIZENZFREI -> 1 - LizenzKlasseE.R1, LizenzKlasseE.RD1 -> 2 + ReiterLizenzKlasseE.LIZENZFREI -> 1 + ReiterLizenzKlasseE.R1, ReiterLizenzKlasseE.RD1 -> 2 else -> 3 // R2+ } } else if (hoehe != null && hoehe >= 100) { return when (reiter.lizenzKlasse) { - LizenzKlasseE.R1, LizenzKlasseE.RD1 -> 1 + ReiterLizenzKlasseE.R1, ReiterLizenzKlasseE.RD1 -> 1 else -> 2 // R2+ } } @@ -39,7 +39,7 @@ class AbteilungsRegelServiceImpl : AbteilungsRegelService { // Fall 2: Klassen A & L (Standardregelung § 39 Abs. 1) if (turnierklasseCode == "A" || turnierklasseCode == "L") { return when (reiter.lizenzKlasse) { - LizenzKlasseE.R1, LizenzKlasseE.RD1 -> 1 // Abt. 1: R1 + ReiterLizenzKlasseE.R1, ReiterLizenzKlasseE.RD1 -> 1 // Abt. 1: R1 else -> 2 // Abt. 2+: R2 und höher } } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechner.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechner.kt index c102d202..a285fd66 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechner.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechner.kt @@ -2,7 +2,7 @@ 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 at.mocode.masterdata.domain.model.Reiter import kotlinx.datetime.LocalDate /** @@ -30,9 +30,9 @@ interface AltersklasseRechner { * @return Eine Liste der zutreffenden Altersklassen-Definitionen. */ fun ermittleAltersklassen( - reiter: DomReiter, - referenzJahr: Int, - sparte: SparteE? = null, - verfügbareDefinitionen: List + reiter: Reiter, + referenzJahr: Int, + sparte: SparteE? = null, + verfügbareDefinitionen: List ): List } diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerImpl.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerImpl.kt index bfcf9d93..03b1381e 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerImpl.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerImpl.kt @@ -2,7 +2,7 @@ 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 at.mocode.masterdata.domain.model.Reiter import kotlinx.datetime.LocalDate /** @@ -17,10 +17,10 @@ class AltersklasseRechnerImpl : AltersklasseRechner { } override fun ermittleAltersklassen( - reiter: DomReiter, - referenzJahr: Int, - sparte: SparteE?, - verfügbareDefinitionen: List + reiter: Reiter, + referenzJahr: Int, + sparte: SparteE?, + verfügbareDefinitionen: List ): List { val geburtsdatum = reiter.geburtsdatum ?: return emptyList() val alter = berechneOetoAlter(geburtsdatum, referenzJahr) diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixService.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixService.kt index 51585f94..b9f262f8 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixService.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixService.kt @@ -1,7 +1,7 @@ package at.mocode.masterdata.domain.service import at.mocode.core.domain.model.SparteE -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter import at.mocode.masterdata.domain.model.LicenseMatrixEntry import at.mocode.masterdata.domain.model.TurnierklasseDefinition @@ -21,7 +21,7 @@ interface LicenseMatrixService { * @return true, wenn der Reiter startberechtigt ist, sonst false. */ fun isEligible( - reiter: DomReiter, + reiter: Reiter, turnierklasse: TurnierklasseDefinition, sparte: SparteE, matrix: List, @@ -37,7 +37,7 @@ interface LicenseMatrixService { * @return Der Code der maximal erlaubten Turnierklasse oder null, wenn keine Regel gefunden wurde. */ fun getMaxTurnierklasse( - reiter: DomReiter, + reiter: Reiter, sparte: SparteE, matrix: List ): String? diff --git a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceImpl.kt b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceImpl.kt index 66783d59..aa7fdefd 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceImpl.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonMain/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceImpl.kt @@ -1,7 +1,7 @@ package at.mocode.masterdata.domain.service import at.mocode.core.domain.model.SparteE -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter import at.mocode.masterdata.domain.model.LicenseMatrixEntry import at.mocode.masterdata.domain.model.TurnierklasseDefinition @@ -13,7 +13,7 @@ class LicenseMatrixServiceImpl : LicenseMatrixService { private val classHierarchy = listOf("E", "A", "L", "LM", "M", "S") override fun isEligible( - reiter: DomReiter, + reiter: Reiter, turnierklasse: TurnierklasseDefinition, sparte: SparteE, matrix: List, @@ -35,7 +35,7 @@ class LicenseMatrixServiceImpl : LicenseMatrixService { } override fun getMaxTurnierklasse( - reiter: DomReiter, + reiter: Reiter, sparte: SparteE, matrix: List ): String? { diff --git a/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceTest.kt b/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceTest.kt index c5207e89..7913d7ef 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceTest.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AbteilungsRegelServiceTest.kt @@ -2,11 +2,11 @@ package at.mocode.masterdata.domain.service -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.PferdeGeschlechtE import at.mocode.core.domain.model.SparteE -import at.mocode.masterdata.domain.model.DomPferd -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Pferd +import at.mocode.masterdata.domain.model.Reiter import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -17,24 +17,24 @@ class AbteilungsRegelServiceTest { private val service = AbteilungsRegelServiceImpl() - private val standardPferd = DomPferd(pferdeName = "Testpferd", geschlecht = PferdeGeschlechtE.WALLACH) + private val standardPferd = Pferd(pferdeName = "Testpferd", geschlecht = PferdeGeschlechtE.WALLACH) private val dummyPersonId = Uuid.random() @Test fun `ermittleAbteilungStrukturell teilt Klassen A und L nach R1`() { - val r1Reiter = DomReiter( + val r1Reiter = Reiter( personId = dummyPersonId, satznummer = "1", nachname = "R1", vorname = "R1", - lizenzKlasse = LizenzKlasseE.R1 + lizenzKlasse = ReiterLizenzKlasseE.R1 ) - val r2Reiter = DomReiter( + val r2Reiter = Reiter( personId = dummyPersonId, satznummer = "2", nachname = "R2", vorname = "R2", - lizenzKlasse = LizenzKlasseE.R2 + lizenzKlasse = ReiterLizenzKlasseE.R2 ) assertEquals(1, service.ermittleAbteilungStrukturell(r1Reiter, standardPferd, "A", SparteE.SPRINGEN)) @@ -46,26 +46,26 @@ class AbteilungsRegelServiceTest { @Test fun `ermittleAbteilungStrukturell berücksichtigt C-NEU Regeln`() { - val lfReiter = DomReiter( + val lfReiter = Reiter( personId = dummyPersonId, satznummer = "0", nachname = "LF", vorname = "LF", - lizenzKlasse = LizenzKlasseE.LIZENZFREI + lizenzKlasse = ReiterLizenzKlasseE.LIZENZFREI ) - val r1Reiter = DomReiter( + val r1Reiter = Reiter( personId = dummyPersonId, satznummer = "1", nachname = "R1", vorname = "R1", - lizenzKlasse = LizenzKlasseE.R1 + lizenzKlasse = ReiterLizenzKlasseE.R1 ) - val r2Reiter = DomReiter( + val r2Reiter = Reiter( personId = dummyPersonId, satznummer = "2", nachname = "R2", vorname = "R2", - lizenzKlasse = LizenzKlasseE.R2 + lizenzKlasse = ReiterLizenzKlasseE.R2 ) // Bis 95cm diff --git a/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerTest.kt b/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerTest.kt index 7dadde69..767a6b78 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerTest.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/AltersklasseRechnerTest.kt @@ -4,7 +4,7 @@ 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 at.mocode.masterdata.domain.model.Reiter import kotlinx.datetime.LocalDate import kotlin.test.Test import kotlin.test.assertEquals @@ -30,7 +30,7 @@ class AltersklasseRechnerTest { @Test fun `ermittleAltersklassen findet passende Definitionen`() { - val reiter = DomReiter( + val reiter = Reiter( personId = Uuid.random(), satznummer = "123456", nachname = "Mustermann", @@ -73,7 +73,7 @@ class AltersklasseRechnerTest { @Test fun `ermittleAltersklassen beruecksichtigt SpartenFilter`() { - val reiter = DomReiter( + val reiter = Reiter( personId = Uuid.random(), satznummer = "123456", nachname = "Mustermann", diff --git a/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceTest.kt b/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceTest.kt index 4d9937dd..c679e91a 100644 --- a/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceTest.kt +++ b/backend/services/masterdata/masterdata-domain/src/commonTest/kotlin/at/mocode/masterdata/domain/service/LicenseMatrixServiceTest.kt @@ -2,9 +2,9 @@ package at.mocode.masterdata.domain.service -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.SparteE -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter import at.mocode.masterdata.domain.model.LicenseMatrixEntry import at.mocode.masterdata.domain.model.TurnierklasseDefinition import kotlin.test.Test @@ -21,7 +21,7 @@ class LicenseMatrixServiceTest { private val matrix = listOf( LicenseMatrixEntry( sparte = SparteE.SPRINGEN, - lizenzKlasse = LizenzKlasseE.R1, + lizenzKlasse = ReiterLizenzKlasseE.R1, maxTurnierklasseCode = "L", validFrom = nun, createdAt = nun, @@ -29,7 +29,7 @@ class LicenseMatrixServiceTest { ), LicenseMatrixEntry( sparte = SparteE.SPRINGEN, - lizenzKlasse = LizenzKlasseE.R2, + lizenzKlasse = ReiterLizenzKlasseE.R2, maxTurnierklasseCode = "M", validFrom = nun, createdAt = nun, @@ -37,7 +37,7 @@ class LicenseMatrixServiceTest { ), LicenseMatrixEntry( sparte = SparteE.DRESSUR, - lizenzKlasse = LizenzKlasseE.RD1, + lizenzKlasse = ReiterLizenzKlasseE.RD1, maxTurnierklasseCode = "L", validFrom = nun, createdAt = nun, @@ -90,12 +90,12 @@ class LicenseMatrixServiceTest { @Test fun `isEligible erlaubt Starts bis zum Limit`() { - val r1Reiter = DomReiter( + val r1Reiter = Reiter( personId = Uuid.random(), satznummer = "1", nachname = "R1", vorname = "Reiter", - lizenzKlasse = LizenzKlasseE.R1 + lizenzKlasse = ReiterLizenzKlasseE.R1 ) val klasseA = turnierklassen.find { it.code == "A" }!! @@ -109,12 +109,12 @@ class LicenseMatrixServiceTest { @Test fun `isEligible verweigert Start ohne passende Spartenlizenz`() { - val rd1Reiter = DomReiter( + val rd1Reiter = Reiter( personId = Uuid.random(), satznummer = "2", nachname = "RD1", vorname = "Reiter", - lizenzKlasse = LizenzKlasseE.RD1 + lizenzKlasse = ReiterLizenzKlasseE.RD1 ) val klasseA = turnierklassen.find { it.code == "A" }!! diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedRegulationRepository.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedRegulationRepository.kt index 7e2d207e..5dd5830c 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedRegulationRepository.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedRegulationRepository.kt @@ -2,7 +2,7 @@ package at.mocode.masterdata.infrastructure.persistence -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.SparteE import at.mocode.core.utils.database.DatabaseFactory import at.mocode.masterdata.domain.model.* @@ -72,7 +72,7 @@ class ExposedRegulationRepository : RegulationRepository { private fun ResultRow.toLicenseMatrixEntry() = LicenseMatrixEntry( licenseId = this[LicenseTable.id], sparte = SparteE.valueOf(this[LicenseTable.sparte]), - lizenzKlasse = LizenzKlasseE.valueOf(this[LicenseTable.lizenzKlasse]), + lizenzKlasse = ReiterLizenzKlasseE.valueOf(this[LicenseTable.lizenzKlasse]), maxTurnierklasseCode = this[LicenseTable.maxTurnierklasseCode], validFrom = this[LicenseTable.validFrom].toKtInstant(), validTo = this[LicenseTable.validTo]?.toOptionalKtInstant(), diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterSparteTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterSparteTable.kt index 2d9fdcf9..66d80bb3 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterSparteTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterSparteTable.kt @@ -2,6 +2,7 @@ package at.mocode.masterdata.infrastructure.persistence +import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.datetime.CurrentTimestamp import org.jetbrains.exposed.v1.datetime.timestamp diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinAnsprechpersonTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinAnsprechpersonTable.kt index 32197e3a..e3f7e06e 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinAnsprechpersonTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinAnsprechpersonTable.kt @@ -2,6 +2,8 @@ package at.mocode.masterdata.infrastructure.persistence +import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable +import at.mocode.masterdata.infrastructure.persistence.verein.VereinTable import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.datetime.CurrentTimestamp import org.jetbrains.exposed.v1.datetime.timestamp diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedFunktionaerRepository.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/funktionaer/FunktionaerExposedRepository.kt similarity index 72% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedFunktionaerRepository.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/funktionaer/FunktionaerExposedRepository.kt index 81405b9e..41555b7b 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedFunktionaerRepository.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/funktionaer/FunktionaerExposedRepository.kt @@ -1,10 +1,9 @@ @file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) - -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.funktionaer import at.mocode.core.domain.model.DatenQuelleE import at.mocode.core.utils.database.DatabaseFactory -import at.mocode.masterdata.domain.model.DomFunktionaer +import at.mocode.masterdata.domain.model.Funktionaer import at.mocode.masterdata.domain.repository.FunktionaerRepository import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.and @@ -19,24 +18,25 @@ import kotlin.uuid.Uuid /** * Exposed-basierte Implementierung des Funktionaer-Repositorys. */ -class ExposedFunktionaerRepository : FunktionaerRepository { +class FunktionaerExposedRepository : FunktionaerRepository { - private fun rowToDomFunktionaer(row: ResultRow, qualifikationen: List = emptyList()): DomFunktionaer { - return DomFunktionaer( - funktionaerId = row[FunktionaerTable.id], - satzID = row[FunktionaerTable.satzID] ?: "X", - satzNummer = row[FunktionaerTable.satzNummer] ?: 0, - name = row[FunktionaerTable.name], - qualifikationen = qualifikationen, - istAktiv = row[FunktionaerTable.istAktiv], - bemerkungen = row[FunktionaerTable.bemerkungen], - datenQuelle = DatenQuelleE.valueOf(row[FunktionaerTable.datenQuelle]), - createdAt = row[FunktionaerTable.createdAt], - updatedAt = row[FunktionaerTable.updatedAt] + private fun rowToDomFunktionaer(row: ResultRow, qualifikationen: List = emptyList()): Funktionaer { + return Funktionaer( + funktionaerId = row[FunktionaerTable.id], + personId = row[FunktionaerTable.personId], + satzId = row[FunktionaerTable.satzId] ?: "X", + satzNummer = row[FunktionaerTable.satzNummer] ?: 0, + name = row[FunktionaerTable.name], + qualifikationen = qualifikationen, + istAktiv = row[FunktionaerTable.istAktiv], + bemerkungen = row[FunktionaerTable.bemerkungen], + datenQuelle = DatenQuelleE.valueOf(row[FunktionaerTable.datenQuelle]), + createdAt = row[FunktionaerTable.createdAt], + updatedAt = row[FunktionaerTable.updatedAt] ) } - override suspend fun findById(id: Uuid): DomFunktionaer? = DatabaseFactory.dbQuery { + override suspend fun findById(id: Uuid): Funktionaer? = DatabaseFactory.dbQuery { val qualifikationen = FunktionaerQualifikationTable .selectAll().where { FunktionaerQualifikationTable.funktionaerId eq id } .map { it[FunktionaerQualifikationTable.qualifikation] } @@ -46,9 +46,9 @@ class ExposedFunktionaerRepository : FunktionaerRepository { .singleOrNull() } - override suspend fun findBySatz(satzID: String, satzNummer: Int): DomFunktionaer? = DatabaseFactory.dbQuery { + override suspend fun findBySatz(satzID: String, satzNummer: Int): Funktionaer? = DatabaseFactory.dbQuery { val row = FunktionaerTable.selectAll() - .where { (FunktionaerTable.satzID eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) } + .where { (FunktionaerTable.satzId eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) } .singleOrNull() ?: return@dbQuery null val qualifikationen = FunktionaerQualifikationTable @@ -58,7 +58,7 @@ class ExposedFunktionaerRepository : FunktionaerRepository { rowToDomFunktionaer(row, qualifikationen) } - override suspend fun findAll(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { + override suspend fun findAll(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { val funktionaere = FunktionaerTable.selectAll() .limit(limit).offset(offset.toLong()) .toList() @@ -73,11 +73,12 @@ class ExposedFunktionaerRepository : FunktionaerRepository { } } - override suspend fun save(funktionaer: DomFunktionaer): DomFunktionaer = DatabaseFactory.dbQuery { + override suspend fun save(funktionaer: Funktionaer): Funktionaer = DatabaseFactory.dbQuery { val exists = FunktionaerTable.selectAll().where { FunktionaerTable.id eq funktionaer.funktionaerId }.any() if (exists) { FunktionaerTable.update({ FunktionaerTable.id eq funktionaer.funktionaerId }) { - it[satzID] = funktionaer.satzID + it[personId] = funktionaer.personId + it[satzId] = funktionaer.satzId it[satzNummer] = funktionaer.satzNummer it[name] = funktionaer.name it[istAktiv] = funktionaer.istAktiv @@ -88,7 +89,8 @@ class ExposedFunktionaerRepository : FunktionaerRepository { } else { FunktionaerTable.insert { it[id] = funktionaer.funktionaerId - it[satzID] = funktionaer.satzID + it[personId] = funktionaer.personId + it[satzId] = funktionaer.satzId it[satzNummer] = funktionaer.satzNummer it[name] = funktionaer.name it[istAktiv] = funktionaer.istAktiv @@ -121,7 +123,7 @@ class ExposedFunktionaerRepository : FunktionaerRepository { override suspend fun existsBySatz(satzID: String, satzNummer: Int): Boolean = DatabaseFactory.dbQuery { FunktionaerTable.selectAll() - .where { (FunktionaerTable.satzID eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) } + .where { (FunktionaerTable.satzId eq satzID) and (FunktionaerTable.satzNummer eq satzNummer) } .any() } } diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/FunktionaerTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/funktionaer/FunktionaerTable.kt similarity index 55% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/FunktionaerTable.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/funktionaer/FunktionaerTable.kt index 7f291721..54d0fc6f 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/FunktionaerTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/funktionaer/FunktionaerTable.kt @@ -1,10 +1,11 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.funktionaer import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.datetime.CurrentTimestamp import org.jetbrains.exposed.v1.datetime.timestamp +import kotlin.uuid.ExperimentalUuidApi /** @@ -12,19 +13,42 @@ import org.jetbrains.exposed.v1.datetime.timestamp */ object FunktionaerTable : Table("funktionaer") { val id = uuid("funktionaer_id") - val satzID = varchar("satz_id", 1).nullable() + val personId = uuid("person_id").nullable() + +// === ZNS.zip RICHT01.DAT === ANFANG === + + val satzId = varchar("satz_id", 1) val satzNummer = integer("satz_nummer").nullable() val name = varchar("name", 200).nullable() + +// === ZNS.zip RICHT01.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() val datenQuelle = varchar("daten_quelle", 50) + + // Audit val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp) val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp) override val primaryKey = PrimaryKey(id) init { - index("idx_funktionaer_satz", isUnique = true, satzID, satzNummer) + index("idx_funktionaer_satz", isUnique = true, satzId, satzNummer) } } diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/HorseRepositoryImpl.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/pferd/HorseExposedRepository.kt similarity index 81% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/HorseRepositoryImpl.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/pferd/HorseExposedRepository.kt index e3559073..c7b21921 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/HorseRepositoryImpl.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/pferd/HorseExposedRepository.kt @@ -1,11 +1,11 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.pferd import at.mocode.core.domain.model.DatenQuelleE import at.mocode.core.domain.model.PferdeGeschlechtE import at.mocode.core.utils.database.DatabaseFactory -import at.mocode.masterdata.domain.model.DomPferd +import at.mocode.masterdata.domain.model.Pferd import at.mocode.masterdata.domain.repository.HorseRepository import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.eq @@ -14,12 +14,13 @@ 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 kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid -class HorseRepositoryImpl : HorseRepository { +class HorseExposedRepository : HorseRepository { - private fun rowToDomPferd(row: ResultRow): DomPferd { - return DomPferd( + private fun rowToPferd(row: ResultRow): Pferd { + return Pferd( pferdId = row[HorseTable.id], kopfnummer = row[HorseTable.kopfnummer], pferdeName = row[HorseTable.pferdeName], @@ -42,43 +43,43 @@ class HorseRepositoryImpl : HorseRepository { ) } - override suspend fun findById(id: Uuid): DomPferd? = DatabaseFactory.dbQuery { + override suspend fun findById(id: Uuid): Pferd? = DatabaseFactory.dbQuery { HorseTable.selectAll().where { HorseTable.id eq id } - .map(::rowToDomPferd) + .map(::rowToPferd) .singleOrNull() } - override suspend fun findByLebensnummer(lebensnummer: String): DomPferd? = DatabaseFactory.dbQuery { + override suspend fun findByLebensnummer(lebensnummer: String): Pferd? = DatabaseFactory.dbQuery { HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer } - .map(::rowToDomPferd) + .map(::rowToPferd) .singleOrNull() } - override suspend fun findBySatznummer(satznummer: String): DomPferd? = DatabaseFactory.dbQuery { + override suspend fun findBySatznummer(satznummer: String): Pferd? = DatabaseFactory.dbQuery { HorseTable.selectAll().where { HorseTable.satznummer eq satznummer } - .map(::rowToDomPferd) + .map(::rowToPferd) .singleOrNull() } - override suspend fun findByKopfnummer(kopfnummer: String): List = DatabaseFactory.dbQuery { + override suspend fun findByKopfnummer(kopfnummer: String): List = DatabaseFactory.dbQuery { HorseTable.selectAll().where { HorseTable.kopfnummer eq kopfnummer } - .map(::rowToDomPferd) + .map(::rowToPferd) } - override suspend fun findByName(searchTerm: String, limit: Int): List = DatabaseFactory.dbQuery { + override suspend fun findByName(searchTerm: String, limit: Int): List = DatabaseFactory.dbQuery { val pattern = "%$searchTerm%" HorseTable.selectAll().where { HorseTable.pferdeName like pattern } .limit(limit) - .map(::rowToDomPferd) + .map(::rowToPferd) } - override suspend fun findAllActive(limit: Int): List = DatabaseFactory.dbQuery { + override suspend fun findAllActive(limit: Int): List = DatabaseFactory.dbQuery { HorseTable.selectAll().where { HorseTable.istAktiv eq true } .limit(limit) - .map(::rowToDomPferd) + .map(::rowToPferd) } - override suspend fun save(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery { + override suspend fun save(horse: Pferd): Pferd = DatabaseFactory.dbQuery { val exists = HorseTable.selectAll().where { HorseTable.id eq horse.pferdId }.any() if (exists) { HorseTable.update({ HorseTable.id eq horse.pferdId }) { @@ -139,11 +140,11 @@ class HorseRepositoryImpl : HorseRepository { HorseTable.selectAll().where { HorseTable.istAktiv eq true }.count() } - override suspend fun upsertByLebensnummer(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery { + override suspend fun upsertByLebensnummer(horse: Pferd): Pferd = DatabaseFactory.dbQuery { val lebensnummer = horse.lebensnummer ?: return@dbQuery save(horse) val existing = HorseTable.selectAll().where { HorseTable.lebensnummer eq lebensnummer } - .map(::rowToDomPferd) + .map(::rowToPferd) .singleOrNull() if (existing != null) { @@ -173,11 +174,11 @@ class HorseRepositoryImpl : HorseRepository { } } - override suspend fun upsertBySatznummer(horse: DomPferd): DomPferd = DatabaseFactory.dbQuery { + override suspend fun upsertBySatznummer(horse: Pferd): Pferd = DatabaseFactory.dbQuery { val satznummer = horse.satznummer ?: return@dbQuery save(horse) val existing = HorseTable.selectAll().where { HorseTable.satznummer eq satznummer } - .map(::rowToDomPferd) + .map(::rowToPferd) .singleOrNull() if (existing != null) { @@ -207,28 +208,28 @@ class HorseRepositoryImpl : HorseRepository { } } - // Not implemented or needed based on current requirements/DomPferd state - override suspend fun findByChipNummer(chipNummer: String): DomPferd? = null - override suspend fun findByPassNummer(passNummer: String): DomPferd? = null - override suspend fun findByOepsNummer(oepsNummer: String): DomPferd? = null - override suspend fun findByFeiNummer(feiNummer: String): DomPferd? = null - override suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean): List = emptyList() - override suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean): List = + // Not implemented or needed based on current requirements/Pferd state + override suspend fun findByChipNummer(chipNummer: String): Pferd? = null + override suspend fun findByPassNummer(passNummer: String): Pferd? = null + override suspend fun findByOepsNummer(oepsNummer: String): Pferd? = null + override suspend fun findByFeiNummer(feiNummer: String): Pferd? = null + override suspend fun findByOwnerId(ownerId: Uuid, activeOnly: Boolean): List = emptyList() + override suspend fun findByResponsiblePersonId(responsiblePersonId: Uuid, activeOnly: Boolean): List = emptyList() override suspend fun findByGeschlecht( geschlecht: PferdeGeschlechtE, activeOnly: Boolean, limit: Int - ): List = emptyList() + ): List = emptyList() - override suspend fun findByRasse(rasse: String, activeOnly: Boolean, limit: Int): List = emptyList() - override suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean): List = emptyList() - override suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean): List = + override suspend fun findByRasse(rasse: String, activeOnly: Boolean, limit: Int): List = emptyList() + override suspend fun findByBirthYear(birthYear: Int, activeOnly: Boolean): List = emptyList() + override suspend fun findByBirthYearRange(fromYear: Int, toYear: Int, activeOnly: Boolean): List = emptyList() - override suspend fun findOepsRegistered(activeOnly: Boolean): List = emptyList() - override suspend fun findFeiRegistered(activeOnly: Boolean): List = emptyList() + override suspend fun findOepsRegistered(activeOnly: Boolean): List = emptyList() + override suspend fun findFeiRegistered(activeOnly: Boolean): List = emptyList() override suspend fun existsByChipNummer(chipNummer: String): Boolean = false override suspend fun existsByPassNummer(passNummer: String): Boolean = false override suspend fun existsByOepsNummer(oepsNummer: String): Boolean = false diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/HorseTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/pferd/HorseTable.kt similarity index 82% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/HorseTable.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/pferd/HorseTable.kt index fbd531be..93240712 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/HorseTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/pferd/HorseTable.kt @@ -1,16 +1,21 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.pferd import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.datetime.CurrentTimestamp import org.jetbrains.exposed.v1.datetime.timestamp +import kotlin.uuid.ExperimentalUuidApi /** * Exposed-Tabellendefinition für die Pferd-Entität basierend auf PFERDE01.DAT. */ object HorseTable : Table("horse") { val id = uuid("horse_id") + val personId = uuid("person_id").nullable() + +// === ZNS.zip PFERDE01.DAT === ANFANG === + val kopfnummer = varchar("kopfnummer", 4).nullable().index() val pferdeName = varchar("pferde_name", 200).index() val lebensnummer = varchar("lebensnummer", 50).nullable().index() @@ -25,9 +30,14 @@ object HorseTable : Table("horse") { val feiPass = varchar("fei_pass", 50).nullable() val satznummer = varchar("satznummer", 10).nullable() +// === ZNS.zip PFERDE01.DAT === ENDE === + + // Status & Verwaltung val istAktiv = bool("ist_aktiv").default(true) val bemerkungen = text("bemerkungen").nullable() val datenQuelle = varchar("daten_quelle", 50) + + // Audit val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp) val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp) diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedReiterRepository.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/reiter/ReiterExposedRepository.kt similarity index 85% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedReiterRepository.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/reiter/ReiterExposedRepository.kt index 3a64b495..3e0e5251 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedReiterRepository.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/reiter/ReiterExposedRepository.kt @@ -1,24 +1,25 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.reiter import at.mocode.core.domain.model.DatenQuelleE -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.utils.database.DatabaseFactory -import at.mocode.masterdata.domain.model.DomReiter +import at.mocode.masterdata.domain.model.Reiter import at.mocode.masterdata.domain.repository.ReiterRepository import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.jdbc.* +import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid /** * Exposed-basierte Implementierung des Reiter-Repositorys. */ -class ExposedReiterRepository : ReiterRepository { +class ReiterExposedRepository : ReiterRepository { - private fun rowToDomReiter(row: ResultRow): DomReiter { - return DomReiter( + private fun rowToDomReiter(row: ResultRow): Reiter { + return Reiter( reiterId = row[ReiterTable.id], personId = row[ReiterTable.personId], satznummer = row[ReiterTable.satznummer], @@ -41,7 +42,7 @@ class ExposedReiterRepository : ReiterRepository { feiId = row[ReiterTable.feiId], sperrListe = row[ReiterTable.sperrListe], lizenzInfo = row[ReiterTable.lizenzInfo], - lizenzKlasse = LizenzKlasseE.valueOf(row[ReiterTable.lizenzKlasse]), + lizenzKlasse = ReiterLizenzKlasseE.valueOf(row[ReiterTable.lizenzKlasse]), istAktiv = row[ReiterTable.istAktiv], bemerkungen = row[ReiterTable.bemerkungen], datenQuelle = DatenQuelleE.valueOf(row[ReiterTable.datenQuelle]), @@ -50,25 +51,25 @@ class ExposedReiterRepository : ReiterRepository { ) } - override suspend fun findById(id: Uuid): DomReiter? = DatabaseFactory.dbQuery { + override suspend fun findById(id: Uuid): Reiter? = DatabaseFactory.dbQuery { ReiterTable.selectAll().where { ReiterTable.id eq id } .map { rowToDomReiter(it) } .singleOrNull() } - override suspend fun findBySatznummer(satznummer: String?): DomReiter? = DatabaseFactory.dbQuery { + override suspend fun findBySatznummer(satznummer: String?): Reiter? = DatabaseFactory.dbQuery { ReiterTable.selectAll().where { ReiterTable.satznummer eq satznummer } .map { row -> rowToDomReiter(row) } .singleOrNull() } - override suspend fun findAll(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { + override suspend fun findAll(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { ReiterTable.selectAll() .limit(limit).offset(offset.toLong()) .map { row -> rowToDomReiter(row) } } - override suspend fun save(reiter: DomReiter): DomReiter = DatabaseFactory.dbQuery { + override suspend fun save(reiter: Reiter): Reiter = DatabaseFactory.dbQuery { val exists = ReiterTable.selectAll().where { ReiterTable.id eq reiter.reiterId }.any() if (exists) { ReiterTable.update({ ReiterTable.id eq reiter.reiterId }) { @@ -147,7 +148,7 @@ class ExposedReiterRepository : ReiterRepository { ReiterTable.selectAll().where { ReiterTable.satznummer eq satznummer }.any() } - override suspend fun upsertBySatznummer(reiter: DomReiter): DomReiter = DatabaseFactory.dbQuery { + override suspend fun upsertBySatznummer(reiter: Reiter): Reiter = DatabaseFactory.dbQuery { val existing = findBySatznummer(reiter.satznummer) if (existing != null) { diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/reiter/ReiterTable.kt similarity index 71% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterTable.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/reiter/ReiterTable.kt index 6b695ea8..5cc9ad1f 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ReiterTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/reiter/ReiterTable.kt @@ -1,19 +1,22 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.reiter -import at.mocode.core.domain.model.LizenzKlasseE import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.datetime.CurrentTimestamp import org.jetbrains.exposed.v1.datetime.date import org.jetbrains.exposed.v1.datetime.timestamp +import kotlin.uuid.ExperimentalUuidApi /** * Exposed-Tabellendefinition für die Reiter-Entität. */ object ReiterTable : Table("reiter") { val id = uuid("reiter_id") - val personId = uuid("person_id") + val personId = uuid("person_id").nullable() + +// === ZNS.zip LITENZ01.DAT === ANFANG === + val satznummer = varchar("satznummer", 10).nullable() val nachname = varchar("nachname", 100) val vorname = varchar("vorname", 100) @@ -34,11 +37,28 @@ object ReiterTable : Table("reiter") { val feiId = varchar("fei_id", 20).nullable() val sperrListe = varchar("sperr_liste", 50).nullable() val lizenzInfo = varchar("lizenz_info", 100).nullable() - val lizenzKlasse = varchar("lizenz_klasse", 50).default(LizenzKlasseE.LIZENZFREI.name) +// === 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() val datenQuelle = varchar("daten_quelle", 50) + + // Audit val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp) val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp) diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedVereinRepository.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/verein/VereinExposedRepository.kt similarity index 72% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedVereinRepository.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/verein/VereinExposedRepository.kt index d8df5941..0203a4fa 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/ExposedVereinRepository.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/verein/VereinExposedRepository.kt @@ -1,102 +1,113 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.verein import at.mocode.core.domain.model.DatenQuelleE import at.mocode.core.utils.database.DatabaseFactory -import at.mocode.masterdata.domain.model.DomVerein +import at.mocode.masterdata.domain.model.Verein import at.mocode.masterdata.domain.repository.VereinRepository import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.core.like import org.jetbrains.exposed.v1.core.or import org.jetbrains.exposed.v1.jdbc.* +import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid /** * Exposed-basierte Implementierung des Verein-Repositorys. */ -class ExposedVereinRepository : VereinRepository { +class VereinExposedRepository : VereinRepository { - private fun rowToDomVerein(row: ResultRow): DomVerein { - return DomVerein( + private fun rowToVereinDomain(row: ResultRow): Verein { + return Verein( vereinId = row[VereinTable.id], + personId = row[VereinTable.personId], + + // === ZNS.zip VEREIN01.DAT === ANFANG === vereinsNummer = row[VereinTable.vereinsNummer], - name = row[VereinTable.name], - kurzname = row[VereinTable.kurzname], - bundesland = row[VereinTable.bundesland], - ort = row[VereinTable.ort], - plz = row[VereinTable.plz], - strasse = row[VereinTable.strasse], + vereinName = row[VereinTable.vereinName], + // === ZNS.zip VEREIN01.DAT === ENDE === + + // Kontakt + imageUrl = row[VereinTable.imageUrl], email = row[VereinTable.email], telefon = row[VereinTable.telefon], website = row[VereinTable.website], - oepsRegionNummer = row[VereinTable.oepsRegionNummer], - istVeranstalter = row[VereinTable.istVeranstalter], + + // Adresse + strasse = row[VereinTable.strasse], + hausnummer = row[VereinTable.hausnummer], + plz = row[VereinTable.plz], + ort = row[VereinTable.ort], + bundesland = row[VereinTable.bundesland], + + // Status & Verwaltung istAktiv = row[VereinTable.istAktiv], - logoUrl = row[VereinTable.logoUrl], + istVeranstalter = row[VereinTable.istVeranstalter], bemerkungen = row[VereinTable.bemerkungen], datenQuelle = DatenQuelleE.valueOf(row[VereinTable.datenQuelle]), + + // Audit createdAt = row[VereinTable.createdAt], updatedAt = row[VereinTable.updatedAt] ) } - override suspend fun findById(id: Uuid): DomVerein? = DatabaseFactory.dbQuery { + override suspend fun findById(id: Uuid): Verein? = DatabaseFactory.dbQuery { VereinTable.selectAll().where { VereinTable.id eq id } - .map(::rowToDomVerein) + .map(::rowToVereinDomain) .singleOrNull() } - override suspend fun findByVereinsNummer(vereinsNummer: String): DomVerein? = DatabaseFactory.dbQuery { + override suspend fun findByVereinsNummer(vereinsNummer: String): Verein? = DatabaseFactory.dbQuery { VereinTable.selectAll().where { VereinTable.vereinsNummer eq vereinsNummer } - .map(::rowToDomVerein) + .map(::rowToVereinDomain) .singleOrNull() } - override suspend fun findByName(searchTerm: String, limit: Int): List = DatabaseFactory.dbQuery { + override suspend fun findByName(searchTerm: String, limit: Int): List = DatabaseFactory.dbQuery { val pattern = "%$searchTerm%" - VereinTable.selectAll().where { (VereinTable.name like pattern) or (VereinTable.kurzname like pattern) } + VereinTable.selectAll().where { VereinTable.vereinName like pattern } .limit(limit) - .map(::rowToDomVerein) + .map(::rowToVereinDomain) } - override suspend fun findByBundesland(bundesland: String, activeOnly: Boolean): List = + override suspend fun findByBundesland(bundesland: String, activeOnly: Boolean): List = DatabaseFactory.dbQuery { val query = VereinTable.selectAll().where { VereinTable.bundesland eq bundesland } if (activeOnly) { query.andWhere { VereinTable.istAktiv eq true } } - query.map(::rowToDomVerein) + query.map(::rowToVereinDomain) } - override suspend fun findVeranstalter(activeOnly: Boolean): List = DatabaseFactory.dbQuery { + override suspend fun findVeranstalter(activeOnly: Boolean): List = DatabaseFactory.dbQuery { val query = VereinTable.selectAll().where { VereinTable.istVeranstalter eq true } if (activeOnly) { query.andWhere { VereinTable.istAktiv eq true } } - query.map(::rowToDomVerein) + query.map(::rowToVereinDomain) } - override suspend fun findAllActive(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { + override suspend fun findAllActive(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { VereinTable.selectAll().where { VereinTable.istAktiv eq true } .limit(limit).offset(offset.toLong()) - .map(::rowToDomVerein) + .map(::rowToVereinDomain) } - override suspend fun findAll(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { + override suspend fun findAll(limit: Int, offset: Int): List = DatabaseFactory.dbQuery { VereinTable.selectAll() .limit(limit).offset(offset.toLong()) - .map(::rowToDomVerein) + .map(::rowToVereinDomain) } - override suspend fun save(verein: DomVerein): DomVerein = DatabaseFactory.dbQuery { + override suspend fun save(verein: Verein): Verein = DatabaseFactory.dbQuery { val exists = VereinTable.selectAll().where { VereinTable.id eq verein.vereinId }.any() if (exists) { VereinTable.update({ VereinTable.id eq verein.vereinId }) { it[vereinsNummer] = verein.vereinsNummer - it[name] = verein.name - it[kurzname] = verein.kurzname + it[vereinName] = verein.vereinName it[bundesland] = verein.bundesland it[ort] = verein.ort it[plz] = verein.plz @@ -104,10 +115,9 @@ class ExposedVereinRepository : VereinRepository { it[email] = verein.email it[telefon] = verein.telefon it[website] = verein.website - it[oepsRegionNummer] = verein.oepsRegionNummer it[istVeranstalter] = verein.istVeranstalter it[istAktiv] = verein.istAktiv - it[logoUrl] = verein.logoUrl + it[imageUrl] = verein.imageUrl it[bemerkungen] = verein.bemerkungen it[datenQuelle] = verein.datenQuelle.name it[updatedAt] = verein.updatedAt @@ -116,9 +126,9 @@ class ExposedVereinRepository : VereinRepository { } else { VereinTable.insert { it[id] = verein.vereinId + it[personId] = verein.personId it[vereinsNummer] = verein.vereinsNummer - it[name] = verein.name - it[kurzname] = verein.kurzname + it[vereinName] = verein.vereinName it[bundesland] = verein.bundesland it[ort] = verein.ort it[plz] = verein.plz @@ -126,10 +136,9 @@ class ExposedVereinRepository : VereinRepository { it[email] = verein.email it[telefon] = verein.telefon it[website] = verein.website - it[oepsRegionNummer] = verein.oepsRegionNummer it[istVeranstalter] = verein.istVeranstalter it[istAktiv] = verein.istAktiv - it[logoUrl] = verein.logoUrl + it[imageUrl] = verein.imageUrl it[bemerkungen] = verein.bemerkungen it[datenQuelle] = verein.datenQuelle.name it[createdAt] = verein.createdAt @@ -151,17 +160,16 @@ class ExposedVereinRepository : VereinRepository { VereinTable.selectAll().where { VereinTable.vereinsNummer eq vereinsNummer }.any() } - override suspend fun upsertByVereinsNummer(verein: DomVerein): DomVerein = DatabaseFactory.dbQuery { + override suspend fun upsertByVereinsNummer(verein: Verein): Verein = DatabaseFactory.dbQuery { val existing = VereinTable.selectAll().where { VereinTable.vereinsNummer eq verein.vereinsNummer } - .map(::rowToDomVerein) + .map(::rowToVereinDomain) .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[vereinName] = toUpdate.vereinName it[bundesland] = toUpdate.bundesland it[ort] = toUpdate.ort it[plz] = toUpdate.plz @@ -169,10 +177,9 @@ class ExposedVereinRepository : VereinRepository { 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[logoUrl] = toUpdate.logoUrl + it[imageUrl] = toUpdate.imageUrl it[bemerkungen] = toUpdate.bemerkungen it[datenQuelle] = toUpdate.datenQuelle.name it[updatedAt] = toUpdate.updatedAt diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/verein/VereinTable.kt similarity index 68% rename from backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinTable.kt rename to backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/verein/VereinTable.kt index 50823b44..937980df 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/VereinTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/verein/VereinTable.kt @@ -1,32 +1,47 @@ -@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class) +@file:OptIn(ExperimentalUuidApi::class) -package at.mocode.masterdata.infrastructure.persistence +package at.mocode.masterdata.infrastructure.persistence.verein import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.datetime.CurrentTimestamp import org.jetbrains.exposed.v1.datetime.timestamp +import kotlin.uuid.ExperimentalUuidApi /** * Exposed-Tabellendefinition für die Verein-Entität. */ object VereinTable : Table("verein") { val id = uuid("verein_id") + + val personId = uuid("person_id").nullable() + +// === ZNS.zip VEREIN01.DAT === ANFANG === + val vereinsNummer = varchar("vereins_nummer", 10).uniqueIndex() - val name = varchar("name", 200) - val kurzname = varchar("kurzname", 100).nullable() - val bundesland = varchar("bundesland", 100).nullable() - val ort = varchar("ort", 100).nullable() - val plz = varchar("plz", 10).nullable() - val strasse = varchar("strasse", 200).nullable() + val vereinName = varchar("verein_name", 200) + +// === ZNS.zip VEREIN01.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() - val oepsRegionNummer = varchar("oeps_region_nummer", 10).nullable() - val istVeranstalter = bool("ist_veranstalter").default(false) + + // 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 logoUrl = varchar("logo_url", 255).nullable() + val istVeranstalter = bool("ist_veranstalter").default(false) val bemerkungen = text("bemerkungen").nullable() val datenQuelle = varchar("daten_quelle", 50) + + // Audit val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp) val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp) diff --git a/backend/services/masterdata/masterdata-infrastructure/src/test/kotlin/at/mocode/masterdata/infrastructure/persistence/RegulationSeedVerificationTest.kt b/backend/services/masterdata/masterdata-infrastructure/src/test/kotlin/at/mocode/masterdata/infrastructure/persistence/RegulationSeedVerificationTest.kt index 3d965d8c..3f1cbcc2 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/test/kotlin/at/mocode/masterdata/infrastructure/persistence/RegulationSeedVerificationTest.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/test/kotlin/at/mocode/masterdata/infrastructure/persistence/RegulationSeedVerificationTest.kt @@ -2,7 +2,7 @@ package at.mocode.masterdata.infrastructure.persistence -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.SparteE import at.mocode.masterdata.domain.model.LicenseMatrixEntry import at.mocode.masterdata.domain.model.TurnierklasseDefinition @@ -77,7 +77,7 @@ class RegulationSeedVerificationTest { val oetoMatrix = listOf( LicenseMatrixEntry( sparte = SparteE.SPRINGEN, - lizenzKlasse = LizenzKlasseE.R1, + lizenzKlasse = ReiterLizenzKlasseE.R1, maxTurnierklasseCode = "L", validFrom = now, createdAt = now, @@ -85,7 +85,7 @@ class RegulationSeedVerificationTest { ), LicenseMatrixEntry( sparte = SparteE.SPRINGEN, - lizenzKlasse = LizenzKlasseE.R2, + lizenzKlasse = ReiterLizenzKlasseE.R2, maxTurnierklasseCode = "M", validFrom = now, createdAt = now, @@ -93,12 +93,12 @@ class RegulationSeedVerificationTest { ) ) - val r1Reiter = at.mocode.masterdata.domain.model.DomReiter( + val r1Reiter = at.mocode.masterdata.domain.model.Reiter( personId = Uuid.random(), satznummer = "123456", nachname = "Müller", vorname = "Hans", - lizenzKlasse = LizenzKlasseE.R1 + lizenzKlasse = ReiterLizenzKlasseE.R1 ) val klasseL = TurnierklasseDefinition( diff --git a/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataConfiguration.kt b/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataConfiguration.kt index b56b3e4d..0df576ba 100644 --- a/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataConfiguration.kt +++ b/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataConfiguration.kt @@ -4,6 +4,10 @@ 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.infrastructure.persistence.funktionaer.FunktionaerExposedRepository +import at.mocode.masterdata.infrastructure.persistence.pferd.HorseExposedRepository +import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterExposedRepository +import at.mocode.masterdata.infrastructure.persistence.verein.VereinExposedRepository import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Profile @@ -40,22 +44,22 @@ class MasterdataConfiguration { @Bean fun reiterRepository(): ReiterRepository { - return ExposedReiterRepository() + return ReiterExposedRepository() } @Bean fun vereinRepository(): VereinRepository { - return ExposedVereinRepository() + return VereinExposedRepository() } @Bean fun horseRepository(): HorseRepository { - return HorseRepositoryImpl() + return HorseExposedRepository() } @Bean fun funktionaerRepository(): FunktionaerRepository { - return ExposedFunktionaerRepository() + return FunktionaerExposedRepository() } @Bean diff --git a/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataDatabaseConfiguration.kt b/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataDatabaseConfiguration.kt index f19c5acc..609f08af 100644 --- a/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataDatabaseConfiguration.kt +++ b/backend/services/masterdata/masterdata-service/src/main/kotlin/at/mocode/masterdata/service/config/MasterdataDatabaseConfiguration.kt @@ -2,6 +2,10 @@ package at.mocode.masterdata.service.config import at.mocode.masterdata.infrastructure.persistence.* +import at.mocode.masterdata.infrastructure.persistence.funktionaer.FunktionaerTable +import at.mocode.masterdata.infrastructure.persistence.pferd.HorseTable +import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable +import at.mocode.masterdata.infrastructure.persistence.verein.VereinTable import jakarta.annotation.PostConstruct import jakarta.annotation.PreDestroy import org.jetbrains.exposed.v1.jdbc.SchemaUtils diff --git a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql index 023910c3..e0081a49 100644 --- a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql +++ b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V008__Seed_OETO_2026_Data.sql @@ -1,6 +1,6 @@ -- V008: Seed OETO 2026 Data (Turnierklassen, Lizenz-Matrix, Altersklassen) -- Basierend auf ÖTO 2026 --- Lizenz-Keys entsprechen LizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3 +-- Lizenz-Keys entsprechen ReiterLizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3 -- HINWEIS: RD4 existiert NICHT im Enum – nur RD1/RD2/RD3 sind gültige Dressur-Lizenzen (ÖTO 2026) -- 1. Turnierklassen (Springen & Dressur) diff --git a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql index 129c7beb..8b8ec36d 100644 --- a/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql +++ b/backend/services/masterdata/masterdata-service/src/main/resources/db/migration/V009__Add_HorseAge_And_LicenseHeight_Matrix.sql @@ -1,6 +1,6 @@ -- V009: Regulation-as-Data – Höhen-Lizenz-Matrix (Springen) & Mindestalter-Pferd-Matrix -- Basierend auf ÖTO 2026 (§ 231 Springen, § 103 Dressur) und FEI GR Art. 136 --- Lizenz-Keys entsprechen LizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3 +-- Lizenz-Keys entsprechen ReiterLizenzKlasseE-Enum (core-domain): LIZENZFREI, R1, R2, R3, R4, RD1, RD2, RD3 -- Status: DRAFT – wird auf STABLE angehoben nach Fachfreigabe durch ÖTO-Fachreferat -- ───────────────────────────────────────────────────────────────────────────── diff --git a/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/RepositoryConfiguration.kt b/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/RepositoryConfiguration.kt index a1711f3b..feb37d99 100644 --- a/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/RepositoryConfiguration.kt +++ b/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/RepositoryConfiguration.kt @@ -1,7 +1,10 @@ package at.mocode.zns.import.service.config import at.mocode.masterdata.domain.repository.* -import at.mocode.masterdata.infrastructure.persistence.* +import at.mocode.masterdata.infrastructure.persistence.funktionaer.FunktionaerExposedRepository +import at.mocode.masterdata.infrastructure.persistence.pferd.HorseExposedRepository +import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterExposedRepository +import at.mocode.masterdata.infrastructure.persistence.verein.VereinExposedRepository import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -9,14 +12,14 @@ import org.springframework.context.annotation.Configuration class RepositoryConfiguration { @Bean - fun vereinRepository(): VereinRepository = ExposedVereinRepository() + fun vereinRepository(): VereinRepository = VereinExposedRepository() @Bean - fun reiterRepository(): ReiterRepository = ExposedReiterRepository() + fun reiterRepository(): ReiterRepository = ReiterExposedRepository() @Bean - fun horseRepository(): HorseRepository = HorseRepositoryImpl() + fun horseRepository(): HorseRepository = HorseExposedRepository() @Bean - fun funktionaerRepository(): FunktionaerRepository = ExposedFunktionaerRepository() + fun funktionaerRepository(): FunktionaerRepository = FunktionaerExposedRepository() } diff --git a/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/ZnsImportDatabaseConfiguration.kt b/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/ZnsImportDatabaseConfiguration.kt index a20c6650..c377602c 100644 --- a/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/ZnsImportDatabaseConfiguration.kt +++ b/backend/services/zns-import/zns-import-service/src/main/kotlin/at/mocode/zns/import/service/config/ZnsImportDatabaseConfiguration.kt @@ -1,9 +1,9 @@ package at.mocode.zns.import.service.config -import at.mocode.masterdata.infrastructure.persistence.FunktionaerTable -import at.mocode.masterdata.infrastructure.persistence.HorseTable -import at.mocode.masterdata.infrastructure.persistence.ReiterTable -import at.mocode.masterdata.infrastructure.persistence.VereinTable +import at.mocode.masterdata.infrastructure.persistence.funktionaer.FunktionaerTable +import at.mocode.masterdata.infrastructure.persistence.pferd.HorseTable +import at.mocode.masterdata.infrastructure.persistence.reiter.ReiterTable +import at.mocode.masterdata.infrastructure.persistence.verein.VereinTable import jakarta.annotation.PostConstruct import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.v1.jdbc.transactions.transaction diff --git a/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt b/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt index 18ef9048..20ea7ec0 100644 --- a/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt +++ b/core/core-domain/src/commonMain/kotlin/at/mocode/core/domain/model/Enums.kt @@ -159,7 +159,7 @@ enum class VeranstaltungsTypE { * Lizenzklasse eines Reiters gemäß ÖTO Teilnahmeberechtigung. */ @Serializable -enum class LizenzKlasseE { +enum class ReiterLizenzKlasseE { /** Lizenzfrei – keine Lizenz erforderlich */ LIZENZFREI, @@ -174,6 +174,7 @@ enum class LizenzKlasseE { /** Reiter-Lizenz Klasse 4 */ R4, + /** Dressur-Reiter Klasse 1 */ RD1, @@ -183,21 +184,35 @@ enum class LizenzKlasseE { /** Dressur-Reiter Klasse 3 */ RD3, - /** Jugend/Nachwuchs */ - JN, +} - /** Junioren */ +/** + * Altersklasse eines Reiters gemäß ÖTO Teilnahmeberechtigung. + */ +@Serializable +enum class ReiterAltersKlasseE { + + // JUGEND/JUNIOR/U25 + + /** Jugend */ JG, - /** Young Rider */ - YR + /** Junioren */ + JR, + + /** Junge-Reiter */ + Y, + + /** U25 */ + U25, + } /** * Status einer Nennung im registration-context. */ @Serializable -enum class NennungsStatusE { +enum class NennStatusE { /** Nennung eingegangen, noch nicht bestätigt */ EINGEGANGEN, diff --git a/core/zns-parser/src/commonMain/kotlin/at/mocode/zns/parser/ZnsLegacyParsers.kt b/core/zns-parser/src/commonMain/kotlin/at/mocode/zns/parser/ZnsLegacyParsers.kt index 7576b6d4..e0f2f659 100644 --- a/core/zns-parser/src/commonMain/kotlin/at/mocode/zns/parser/ZnsLegacyParsers.kt +++ b/core/zns-parser/src/commonMain/kotlin/at/mocode/zns/parser/ZnsLegacyParsers.kt @@ -1,13 +1,13 @@ package at.mocode.zns.parser import at.mocode.core.domain.model.DatenQuelleE -import at.mocode.core.domain.model.LizenzKlasseE +import at.mocode.core.domain.model.ReiterLizenzKlasseE import at.mocode.core.domain.model.PferdeGeschlechtE import at.mocode.core.utils.parser.FixedWidthLineReader -import at.mocode.masterdata.domain.model.DomFunktionaer -import at.mocode.masterdata.domain.model.DomPferd -import at.mocode.masterdata.domain.model.DomReiter -import at.mocode.masterdata.domain.model.DomVerein +import at.mocode.masterdata.domain.model.Funktionaer +import at.mocode.masterdata.domain.model.Pferd +import at.mocode.masterdata.domain.model.Reiter +import at.mocode.masterdata.domain.model.Verein import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid @@ -23,7 +23,7 @@ object ZnsLegacyParsers { /** * Parses a line from VEREIN01.DAT. */ - fun parseVerein(line: String): DomVerein? { + fun parseVerein(line: String): Verein? { if (line.isBlank() || line.length < 5) return null val reader = FixedWidthLineReader(line) @@ -33,9 +33,9 @@ object ZnsLegacyParsers { if (vereinsNummer.isBlank() || vereinsName.isBlank()) return null - return DomVerein( + return Verein( vereinsNummer = vereinsNummer, - name = vereinsName, + vereinName = vereinsName, datenQuelle = DatenQuelleE.IMPORT_ZNS ) } @@ -43,7 +43,7 @@ object ZnsLegacyParsers { /** * Parses a line from LIZENZ01.DAT. */ - fun parseLizenz(line: String): DomReiter? { + fun parseLizenz(line: String): Reiter? { if (line.isBlank() || line.length < 57) return null val reader = FixedWidthLineReader(line) @@ -79,8 +79,9 @@ object ZnsLegacyParsers { val feiId = reader.getString(190, 8) val sperrListe = reader.getString(198, 1) val lizenzInfo = reader.getString(201, 10) + val lizenzKlasse = mapLizenz(reiterLizenz) - return DomReiter( + return Reiter( personId = Uuid.random(), satznummer = satznummer, nachname = nachname, @@ -102,7 +103,7 @@ object ZnsLegacyParsers { feiId = feiId.ifBlank { null }, sperrListe = sperrListe.ifBlank { null }, lizenzInfo = lizenzInfo.ifBlank { null }, - lizenzKlasse = mapLizenz(reiterLizenz), + lizenzKlasse = lizenzKlasse, datenQuelle = DatenQuelleE.IMPORT_ZNS ) } @@ -110,12 +111,16 @@ object ZnsLegacyParsers { /** * Parses a line from PFERDE01.DAT. */ - fun parsePferd(line: String): DomPferd? { - if (line.isBlank() || line.trim().length < 40) return null + fun parsePferd(line: String): Pferd? { + if (line.isBlank() || line.trim().length < 4) return null val reader = FixedWidthLineReader(line) val kopfnummer = reader.getString(1, 4) val name = reader.getString(5, 30) + + // We need at least a name to identify a horse record + if (name.isBlank()) return null + val lebensnummer = reader.getString(35, 9) val geschlechtChar = reader.getString(44, 1) val geschlecht = mapGeschlecht(geschlechtChar) @@ -128,16 +133,15 @@ object ZnsLegacyParsers { val vaterName = reader.getString(162, 30) val feiPass = reader.getString(192, 10) val satznummer = reader.getString(202, 10) - // Some lines might not have a satznummer, but we need at least a name to identify it - if (satznummer.isBlank() && name.isBlank()) return null - return DomPferd( + return Pferd( + personId = Uuid.random(), pferdeName = name, geschlecht = geschlecht, geburtsjahr = geburtsjahr, lebensnummer = lebensnummer.ifBlank { null }, kopfnummer = kopfnummer.ifBlank { null }, - satznummer = satznummer, + satznummer = satznummer.ifBlank { null }, farbe = farbe.ifBlank { null }, abstammung = abstammung.ifBlank { null }, vereinNummer = vereinNummer, @@ -152,7 +156,7 @@ object ZnsLegacyParsers { /** * Parses a line from RICHT01.DAT (Richter oder Parcoursbauer). */ - fun parseFunktionaer(line: String): DomFunktionaer? { + fun parseFunktionaer(line: String): Funktionaer? { if (line.isBlank() || line.length < 8) return null val reader = FixedWidthLineReader(line) @@ -171,8 +175,9 @@ object ZnsLegacyParsers { .map { it.trim() } .filter { it.isNotBlank() } - return DomFunktionaer( - satzID = satzID, + return Funktionaer( + personId = null, + satzId = satzID, satzNummer = satzNummer, name = name.ifBlank { null }, qualifikationen = qualifikationen, @@ -180,18 +185,15 @@ object ZnsLegacyParsers { ) } - private fun mapLizenz(lizenz: String): LizenzKlasseE { + private fun mapLizenz(lizenz: String): ReiterLizenzKlasseE { return when (lizenz.uppercase()) { - "R1" -> LizenzKlasseE.R1 - "R2" -> LizenzKlasseE.R2 - "R3" -> LizenzKlasseE.R3 - "RD1" -> LizenzKlasseE.RD1 - "RD2" -> LizenzKlasseE.RD2 - "RD3" -> LizenzKlasseE.RD3 - "JN" -> LizenzKlasseE.JN - "JG" -> LizenzKlasseE.JG - "YR" -> LizenzKlasseE.YR - else -> LizenzKlasseE.LIZENZFREI + "R1" -> ReiterLizenzKlasseE.R1 + "R2" -> ReiterLizenzKlasseE.R2 + "R3" -> ReiterLizenzKlasseE.R3 + "RD1" -> ReiterLizenzKlasseE.RD1 + "RD2" -> ReiterLizenzKlasseE.RD2 + "RD3" -> ReiterLizenzKlasseE.RD3 + else -> ReiterLizenzKlasseE.LIZENZFREI } } diff --git a/core/zns-parser/src/commonTest/kotlin/at/mocode/zns/parser/ZnsLegacyParsersTest.kt b/core/zns-parser/src/commonTest/kotlin/at/mocode/zns/parser/ZnsLegacyParsersTest.kt index 0ad6c766..7d885ce0 100644 --- a/core/zns-parser/src/commonTest/kotlin/at/mocode/zns/parser/ZnsLegacyParsersTest.kt +++ b/core/zns-parser/src/commonTest/kotlin/at/mocode/zns/parser/ZnsLegacyParsersTest.kt @@ -14,7 +14,7 @@ class ZnsLegacyParsersTest { assertNotNull(result) assertEquals("1234", result.vereinsNummer) - assertEquals("Reitverein Test", result.name) + assertEquals("Reitverein Test", result.vereinName) } @Test @@ -82,7 +82,7 @@ class ZnsLegacyParsersTest { val result = ZnsLegacyParsers.parseFunktionaer(line) assertNotNull(result) - assertEquals("X", result.satzID) + assertEquals("X", result.satzId) assertEquals(10128, result.satzNummer) assertEquals("Zitterbart Rainer", result.name) assertEquals(listOf("PI-A"), result.qualifikationen) @@ -94,7 +94,7 @@ class ZnsLegacyParsersTest { val line1 = "X139552Mc Mullen Elizabeth DIOR" val result1 = ZnsLegacyParsers.parseFunktionaer(line1) assertNotNull(result1) - assertEquals("X", result1.satzID) + assertEquals("X", result1.satzId) assertEquals(139552, result1.satzNummer) assertEquals("Mc Mullen Elizabeth", result1.name) assertEquals(listOf("DIOR"), result1.qualifikationen) @@ -111,10 +111,28 @@ class ZnsLegacyParsersTest { val line3 = "Y002211Salusek Andreas Christian P3,PL2" val result3 = ZnsLegacyParsers.parseFunktionaer(line3) assertNotNull(result3) - assertEquals("Y", result3.satzID) + assertEquals("Y", result3.satzId) assertEquals(2211, result3.satzNummer) assertEquals("Salusek Andreas Christian", result3.name) assertEquals(listOf("P3", "PL2"), result3.qualifikationen) + + // X001061Kager Franz DPF,DSGP,GAR-SP,GAR-VS,SPF + val line4 = "X001061Kager Franz DPF,DSGP,GAR-SP,GAR-VS,SPF" + val result4 = ZnsLegacyParsers.parseFunktionaer(line4) + assertNotNull(result4) + assertEquals("X", result4.satzId) + assertEquals(1061, result4.satzNummer) + assertEquals("Kager Franz", result4.name) + assertEquals(listOf("DPF", "DSGP", "GAR-SP", "GAR-VS", "SPF"), result4.qualifikationen) + + // X001112Keiblinger Brigitta DPF,DSGP,SPF,SS,VS,VSILEV1" + val line5 = "X001112Keiblinger Brigitta DPF,DSGP,SPF,SS,VS,VSILEV1" + val result5 = ZnsLegacyParsers.parseFunktionaer(line5) + assertNotNull(result5) + assertEquals("X", result5.satzId) + assertEquals(1112, result5.satzNummer) + assertEquals("Keiblinger Brigitta", result5.name) + assertEquals(listOf("DPF", "DSGP", "SPF", "SS", "VS", "VSILEV1"), result5.qualifikationen) } @Test @@ -145,6 +163,20 @@ class ZnsLegacyParsersTest { assertEquals("5637401268", result.satznummer) } + @Test + fun `parsePferd should extract shortened PFERDE01 correctly`() { + // A line that ends after the name + val line = "1234Fuchur" + val result = ZnsLegacyParsers.parsePferd(line) + + assertNotNull(result) + assertEquals("1234", result.kopfnummer) + assertEquals("Fuchur", result.pferdeName) + assertEquals(null, result.satznummer) + assertEquals(null, result.lebensnummer) + assertEquals(PferdeGeschlechtE.UNBEKANNT, result.geschlecht) + } + @Test fun `parseLizenz should extract real LIZENZ01 correctly for Ebner Sarah`() { // Real example from user: diff --git a/docs/01_Architecture/MASTER_ROADMAP.md b/docs/01_Architecture/MASTER_ROADMAP.md index 885302d7..baff0b17 100644 --- a/docs/01_Architecture/MASTER_ROADMAP.md +++ b/docs/01_Architecture/MASTER_ROADMAP.md @@ -23,7 +23,7 @@ Vollständige Self-Hosted Infrastruktur (Gitea, Pangolin, Zora). Datensouveräni * **CI/CD:** ✅ Gitea Actions mit ARM64-Runner (VM 102) aktiv. Docker-Publish Pipeline grün. * **Code-Basis:** ✅ Backend (Java 25 / Spring Boot / Kotlin), Frontend (KMP/Compose Desktop). * **Domain-Design:** ✅ 6 Bounded Contexts (SCS-Architektur) definiert. Ubiquitous Language erstellt. -* **Domain-Modelle:** ✅ `DomReiter`, `DomNennung`, `DomNennungsTransfer`, `DomPferd`, `DomFunktionaer`, `DomVerein`, +* **Domain-Modelle:** ✅ `Reiter`, `DomNennung`, `DomNennungsTransfer`, `Pferd`, `Funktionaer`, `Verein`, `DomBewerb`, `DomAbteilung`, `DomStartliste`, `DomVeranstaltung`, `DomTurnier`, `DomAusschreibung` implementiert. Enums ÖTO-konform. * **Dokumentation:** ✅ Konsolidiert. ÖTO-Regelwerk-Referenzen (Abteilungs-Schwellenwerte) dokumentiert. @@ -124,7 +124,7 @@ und über definierte Schnittstellen kommunizieren. #### 👷 Agent: Backend Developer -* [x] **`actor-context`:** Domain-Modelle für `DomPferd`, `DomFunktionaer`, `DomVerein` implementiert. +* [x] **`actor-context`:** Domain-Modelle für `Pferd`, `Funktionaer`, `Verein` implementiert. * [x] **`registration-context`:** `DomBewerb`, `DomAbteilung`, `DomStartliste` implementiert. * [x] **`event-management-context`:** `DomVeranstaltung`, `DomTurnier`, `DomAusschreibung` implementiert. * [x] **Persistenz:** Repository-Interfaces und erste DB-Migrationen (Flyway/Liquibase). diff --git a/docs/01_Architecture/Roadmap_ZNS_Importer.md b/docs/01_Architecture/Roadmap_ZNS_Importer.md index 9dfba2a2..b814e56a 100644 --- a/docs/01_Architecture/Roadmap_ZNS_Importer.md +++ b/docs/01_Architecture/Roadmap_ZNS_Importer.md @@ -7,9 +7,10 @@ last_update: 2026-03-25 # Roadmap: ZNS-Importer (MVP) -🧹 **[Curator]** | 25. März 2026 +10. **🏗️ [Lead Architect]** | 5. April 2026 **Kontext:** +Der ZNS-Importer wurde für die Verarbeitung der Datei `RICHT01.dat` optimiert. Es wurde klargestellt, dass Richter (SatzID 'X') und Parcoursbauer (SatzID 'Y') gemeinsam in dieser Datei geliefert werden. Eine separate `PARCO01.dat` existiert nicht. Um den `registration-context` und `actor-context` unter realistischen Bedingungen testen zu können, benötigen wir echte Stammdaten (Reiter, Pferde, Vereine, Funktionäre). Diese Daten werden vom ÖPS (Österreichischer Pferdesportverband) in Form einer `ZNS.zip` bereitgestellt. @@ -29,7 +30,7 @@ gesteuert wird und die Daten persistent im Backend (`actor-context`) ablegt. 1. `VEREIN01.dat` (Vereine) 2. `LIZENZ01.dat` (Reiter/Lizenzen) 3. `PFERDE01.dat` (Pferde - benötigt Vereins-ID) - 4. `RICHT01.dat` (Richter/Parcoursbauer) + 4. `RICHT01.dat` (Richter & Parcoursbauer kombiniert in einer Datei) --- @@ -50,7 +51,7 @@ gesteuert wird und die Daten persistent im Backend (`actor-context`) ablegt. * ZIP-Entpackung in-memory implementiert (`ZnsImportService`). * [x] **Legacy-Parser (CP850 Fixed-Width):** * `ZnsLegacyParsers` in `core:zns-parser` (KMP-Modul) implementiert. - * Alle 4 Dateitypen (VEREIN01, LIZENZ01, PFERDE01, RICHT01) bytegenau gemappt. 4 Unit-Tests grün. + * Alle 4 Dateitypen (VEREIN01, LIZENZ01, PFERDE01, RICHT01) bytegenau gemappt. RICHT01 verarbeitet Richter ('X') und Parcoursbauer ('Y'). ✅ ### Phase 2: Domain-Mapping & Persistenz (👷 Backend Developer) diff --git a/docs/03_Domain/01_Glossary/Ubiquitous_Language.md b/docs/03_Domain/01_Glossary/Ubiquitous_Language.md index 12b8c954..091f0b87 100644 --- a/docs/03_Domain/01_Glossary/Ubiquitous_Language.md +++ b/docs/03_Domain/01_Glossary/Ubiquitous_Language.md @@ -2,7 +2,7 @@ type: Reference status: ACTIVE owner: Lead Architect & ÖTO/FEI Rulebook Expert -last_update: 2026-04-02 +last_update: 2026-04-05 sources: - ÖTO 2026, Abschnitt A I, § 2 & § 3 & § 4 - Domain Workshop 2026-03-17 @@ -71,7 +71,7 @@ Die ÖTO definiert sparten- und klassenabhängige Schwellenwerte, ab wievielen S | Begriff | Definition | ÖTO-Referenz | |----------------|------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------| | **FEI-ID** | Eindeutige Identifikationsnummer der Internationalen Reiterlichen Vereinigung (FEI) für Reiter und Pferde. | FEI General Regulations | -| **Funktionär** | Person mit einer definierten Rolle bei einem Turnier (Richter, Parcoursbauer, TBA, ...). Qualifikation wird gegen `RICHT01.DAT` geprüft. | ÖTO Funktionärs-Qualifikation | +| **Funktionär** | Person mit einer definierten Rolle bei einem Turnier (Richter, Parcoursbauer, TBA, ...). Qualifikation wird gegen `RICHT01.DAT` geprüft. Im Code als Entität **`Funktionaer`** abgebildet. | ÖTO Funktionärs-Qualifikation | ### G @@ -136,6 +136,7 @@ Die ÖTO definiert sparten- und klassenabhängige Schwellenwerte, ab wievielen S | Begriff | Definition | ÖTO-Referenz | |------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------| +| **Reiter** | Eine Person, die an einem Bewerb teilnimmt. Spezialisierung einer Person mit pferdesportlichen Attributen (Lizenz, Startkarte). Im Code als Entität **`Reiter`** abgebildet. Datenquelle primär `LIZENZ01.DAT`. | ÖTO § 2 Abs. 11 | | **Satznummer** | *Pferd:* 10-stellige, rein numerische ID (`0000123456`). **Primärer Schlüssel für den Datenaustausch.** *Reiter:* 6-stellige, rein numerische ID. | ZNS-Schnittstelle | | **Serie** | Synonym für → *Cup*. Übergeordneter Wettbewerb, der Ergebnisse aus Bewerben bei **mindestens zwei Turnieren** aggregiert. Besitzt ein **eigenes Reglement** (siehe Abschnitt 4). | ÖTO § 2 Abs. 8 | | **Sparte** | Die unterschiedlichen Arten von Turnieren oder Bewerben (z.B. Dressur = CDN, Springen = CSN). Bestimmt das anzuwendende Richtverfahren und das Regelwerk. | ÖTO § 2 Abs. 9, § 3 Abs. 2 | @@ -162,7 +163,7 @@ Die ÖTO definiert sparten- und klassenabhängige Schwellenwerte, ab wievielen S | Begriff | Definition | ÖTO-Referenz | |-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------| | **Veranstaltung** | In unserer Software: Der Oberbegriff für jede Art von pferdesportlicher Veranstaltung, die von einem Verein durchgeführt wird. Erhält eine **intern vergebene ID**. Entspricht dem ÖTO-Oberbegriff „Pferdesportliche Veranstaltung" (§ 2 Abs. 1). Kann vom Typ Turnier, Reitertreffen, Sonderprüfung, PS&S oder Turnierartig sein. | ÖTO § 2 Abs. 1 | -| **Veranstalter** | OEPS-Mitgliedsverein (über LFV angeschlossen), der eine Veranstaltung ausrichtet. Besitzt eine **Vereinsnummer**. | ÖTO § 2 Abs. 12 | +| **Veranstalter** | OEPS-Mitgliedsverein (über LFV angeschlossen), der eine Veranstaltung ausrichtet. Besitzt eine **Vereinsnummer**. Im Code als Entität **`Verein`** abgebildet. | ÖTO § 2 Abs. 12 | | **Veranstaltungs‑Kassa** | Kassen- und Abrechnungsführer auf Ebene der → *Veranstaltung*. Konsolidiert alle Zahlungen, Belege und Rückgelder über mehrere → *Turniere* derselben Veranstaltung. Dient als zentrale Sammelkasse; kann Zahlvorgänge turnierübergreifend splitten und konsolidieren. | Billing Context | ### Z @@ -181,7 +182,7 @@ Die ÖTO definiert sparten- und klassenabhängige Schwellenwerte, ab wievielen S | Veranstaltung, Turnier, Ausschreibung, Veranstalter | `event-management-context` | | Bewerb, Abteilung, Startliste, Ergebnis, Richtverfahren | `competition-context` | | Nennung, Nennungs-Transfer, Startwunsch, ZNS-Import | `registration-context` | -| Reiter, Pferd, Lizenz, Funktionär, Kopfnummer, Satznummer | `actor-context` | +| Reiter, Pferd, Lizenz, Funktionär, Kopfnummer, Satznummer, Verein | `actor-context` | | Nenngeld, Startgeld, Konto, Transaktion, Sportförderbeitrag | `billing-context` | | Cup, Serie, Meisterschaft, Reglement, Endklassement | `series-context` *(Phase 2+)* | | Login, Rolle, Berechtigung | `identity-context` | @@ -234,4 +235,4 @@ Ein Reglement definiert typischerweise: --- *Erstellt: 2026-03-24 | Autoren: Lead Architect, ÖTO/FEI Rulebook Expert, Curator* -*Basiert auf: ÖTO 2026 § 2, § 3, § 4 | Domain Workshop 2026-03-17 | Session 2026-03-24* +*Basiert auf: ÖTO 2026 § 2, § 3, § 4 | Domain Workshop 2026-03-17 | Session 2026-03-24 | Update: 2026-04-05 (Verein-Renaming & Bereinigung)*