(fix) Implementiere einen Service-Layer
Erstellung von DTOs für alle Ressourcen Implement a versioning system
This commit is contained in:
@@ -0,0 +1,272 @@
|
|||||||
|
# API Versioning Implementation
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt die implementierte Versionierungsstrategie für die Meldestelle API. Das System unterstützt sowohl DTO-Versionierung als auch API-Versionierung für eine saubere Evolution der API.
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
|
||||||
|
### 1. DTO Versionierung
|
||||||
|
|
||||||
|
Alle DTOs implementieren das `VersionedDto` Interface, welches folgende Eigenschaften bereitstellt:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
interface VersionedDto {
|
||||||
|
val schemaVersion: String
|
||||||
|
val dataVersion: Long?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Beispiel Implementation:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class ArtikelDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
// ... andere Felder
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Version Manager
|
||||||
|
|
||||||
|
Der `VersionManager` verwaltet API-Versionen und Kompatibilität:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
object VersionManager {
|
||||||
|
const val CURRENT_API_VERSION = "1.0"
|
||||||
|
val SUPPORTED_VERSIONS = listOf("1.0")
|
||||||
|
val DEPRECATED_VERSIONS = emptyList<String>()
|
||||||
|
const val MINIMUM_CLIENT_VERSION = "1.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. API Versioning Plugin
|
||||||
|
|
||||||
|
Das Ktor-Plugin `VersioningPlugin` behandelt:
|
||||||
|
- Version-Header Validierung
|
||||||
|
- Automatische Version-Header in Responses
|
||||||
|
- Deprecation Warnings
|
||||||
|
- Unsupported Version Errors
|
||||||
|
|
||||||
|
## Verwendung
|
||||||
|
|
||||||
|
### Client-seitige Version Headers
|
||||||
|
|
||||||
|
Clients können die API-Version über Header spezifizieren:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/artikel
|
||||||
|
API-Version: 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
oder
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/artikel
|
||||||
|
X-API-Version: 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Response Headers
|
||||||
|
|
||||||
|
Der Server antwortet mit Version-Informationen:
|
||||||
|
|
||||||
|
```http
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
API-Version: 1.0
|
||||||
|
X-Supported-Versions: 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Versioned Responses
|
||||||
|
|
||||||
|
Verwende die Extension-Funktionen für versionierte Antworten:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Einzelnes DTO
|
||||||
|
call.respondVersioned(HttpStatusCode.OK, artikelDto)
|
||||||
|
|
||||||
|
// Liste von DTOs
|
||||||
|
call.respondVersionedList(HttpStatusCode.OK, artikelList)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration System
|
||||||
|
|
||||||
|
### VersionMigrator Interface
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
interface VersionMigrator<T : VersionedDto> {
|
||||||
|
fun migrate(dto: T, fromVersion: String, toVersion: String): T
|
||||||
|
fun canMigrate(fromVersion: String, toVersion: String): Boolean
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beispiel Migrator
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class ArtikelDtoMigrator : VersionMigrator<ArtikelDto> {
|
||||||
|
override fun migrate(dto: ArtikelDto, fromVersion: String, toVersion: String): ArtikelDto {
|
||||||
|
return when {
|
||||||
|
fromVersion == "1.0" && toVersion == "1.1" -> migrateFrom1_0To1_1(dto)
|
||||||
|
else -> throw IllegalArgumentException("Unsupported migration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateFrom1_0To1_1(dto: ArtikelDto): ArtikelDto {
|
||||||
|
return dto.copy(
|
||||||
|
schemaVersion = "1.1",
|
||||||
|
// Neue Felder mit Standardwerten hinzufügen
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Annotations
|
||||||
|
|
||||||
|
### @Since(version)
|
||||||
|
Markiert, seit welcher Version ein DTO oder Feld verfügbar ist.
|
||||||
|
|
||||||
|
### @Deprecated(version, message)
|
||||||
|
Markiert veraltete DTOs oder Felder.
|
||||||
|
|
||||||
|
### @Until(version)
|
||||||
|
Markiert, bis zu welcher Version ein DTO oder Feld verfügbar war.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Neue API Version hinzufügen
|
||||||
|
|
||||||
|
1. **VersionManager aktualisieren:**
|
||||||
|
```kotlin
|
||||||
|
const val CURRENT_API_VERSION = "1.1"
|
||||||
|
val SUPPORTED_VERSIONS = listOf("1.1", "1.0")
|
||||||
|
val DEPRECATED_VERSIONS = listOf("1.0")
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **DTOs erweitern:**
|
||||||
|
```kotlin
|
||||||
|
@Serializable
|
||||||
|
@Since("1.1")
|
||||||
|
data class ArtikelDto(
|
||||||
|
// Bestehende Felder...
|
||||||
|
@Since("1.1")
|
||||||
|
val neuesFeld: String? = null,
|
||||||
|
override val schemaVersion: String = "1.1"
|
||||||
|
) : VersionedDto
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Migrator implementieren:**
|
||||||
|
```kotlin
|
||||||
|
class ArtikelDtoMigrator : VersionMigrator<ArtikelDto> {
|
||||||
|
override fun migrate(dto: ArtikelDto, fromVersion: String, toVersion: String): ArtikelDto {
|
||||||
|
return when {
|
||||||
|
fromVersion == "1.0" && toVersion == "1.1" -> migrateFrom1_0To1_1(dto)
|
||||||
|
// Weitere Migrationen...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Backward Compatibility
|
||||||
|
|
||||||
|
- Neue Felder sollten optional sein (nullable oder mit Standardwerten)
|
||||||
|
- Bestehende Felder nicht entfernen, sondern als @Deprecated markieren
|
||||||
|
- Migratoren für alle unterstützten Versionsübergänge bereitstellen
|
||||||
|
|
||||||
|
### 3. Breaking Changes
|
||||||
|
|
||||||
|
Bei Breaking Changes:
|
||||||
|
1. Neue Major Version erstellen
|
||||||
|
2. Alte Version als deprecated markieren
|
||||||
|
3. Migration Path bereitstellen
|
||||||
|
4. Dokumentation aktualisieren
|
||||||
|
|
||||||
|
## Beispiel API Calls
|
||||||
|
|
||||||
|
### Erfolgreiche Anfrage
|
||||||
|
```http
|
||||||
|
GET /api/artikel
|
||||||
|
API-Version: 1.0
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
API-Version: 1.0
|
||||||
|
X-Supported-Versions: 1.0
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unsupported Version
|
||||||
|
```http
|
||||||
|
GET /api/artikel
|
||||||
|
API-Version: 2.0
|
||||||
|
|
||||||
|
HTTP/1.1 400 Bad Request
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deprecated Version Warning
|
||||||
|
```http
|
||||||
|
GET /api/artikel
|
||||||
|
API-Version: 0.9
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
API-Version: 1.0
|
||||||
|
X-API-Version-Warning: Version 0.9 is deprecated
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Das Versioning System wird durch `VersioningTest.kt` getestet:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew test --tests "at.mocode.VersioningTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementierte DTOs
|
||||||
|
|
||||||
|
Folgende DTOs wurden bereits mit Versionierung ausgestattet:
|
||||||
|
|
||||||
|
- ✅ `ArtikelDto`, `CreateArtikelDto`, `UpdateArtikelDto`
|
||||||
|
- ✅ `VereinDto`, `CreateVereinDto`, `UpdateVereinDto`
|
||||||
|
|
||||||
|
### Noch zu implementieren:
|
||||||
|
|
||||||
|
- `AbteilungDto`
|
||||||
|
- `BewerbDto`
|
||||||
|
- `DomaeneDto`
|
||||||
|
- `StammdatenDto`
|
||||||
|
- `TurnierDto`
|
||||||
|
- `VeranstaltungDto`
|
||||||
|
- `CommonDto` (alle Klassen)
|
||||||
|
- `SpecializedDto`
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
1. Alle verbleibenden DTOs mit Versionierung ausstatten
|
||||||
|
2. API Routes auf DTO-Verwendung umstellen
|
||||||
|
3. Versioning Plugin in Application.kt aktivieren
|
||||||
|
4. Client-seitige Version-Header Implementation
|
||||||
|
5. Monitoring für Version-Usage implementieren
|
||||||
|
|
||||||
|
### Noch zu implementieren:
|
||||||
|
|
||||||
|
- `AbteilungDto`
|
||||||
|
- `BewerbDto`
|
||||||
|
- `DomaeneDto`
|
||||||
|
- `StammdatenDto`
|
||||||
|
- `TurnierDto`
|
||||||
|
- `VeranstaltungDto`
|
||||||
|
- `CommonDto` (alle Klassen)
|
||||||
|
- `SpecializedDto`
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
1. Alle verbleibenden DTOs mit Versionierung ausstatten
|
||||||
|
2. API Routes auf DTO-Verwendung umstellen
|
||||||
|
3. Versioning Plugin in Application.kt aktivieren
|
||||||
|
4. Client-seitige Version-Header Implementation
|
||||||
|
5. Monitoring für Version-Usage implementieren
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package at.mocode.plugins
|
||||||
|
|
||||||
|
import at.mocode.dto.base.VersionManager
|
||||||
|
import at.mocode.dto.base.VersionValidationResult
|
||||||
|
import at.mocode.dto.base.VersionedDto
|
||||||
|
import at.mocode.dto.base.VersionedResponse
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.plugins.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.util.*
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin for handling API versioning
|
||||||
|
*/
|
||||||
|
val VersioningPlugin = createApplicationPlugin(name = "VersioningPlugin") {
|
||||||
|
|
||||||
|
onCall { call ->
|
||||||
|
// Extract version from headers
|
||||||
|
val clientVersion = call.request.header("API-Version")
|
||||||
|
?: call.request.header("X-API-Version")
|
||||||
|
?: VersionManager.CURRENT_API_VERSION
|
||||||
|
|
||||||
|
// Validate version
|
||||||
|
when (val result = VersionManager.validateClientVersion(clientVersion)) {
|
||||||
|
is VersionValidationResult.Valid -> {
|
||||||
|
call.attributes.put(ClientVersionKey, result.version)
|
||||||
|
}
|
||||||
|
is VersionValidationResult.DeprecatedVersion -> {
|
||||||
|
call.attributes.put(ClientVersionKey, result.version)
|
||||||
|
call.response.header("X-API-Version-Warning", "Version ${result.version} is deprecated")
|
||||||
|
}
|
||||||
|
is VersionValidationResult.UnsupportedVersion -> {
|
||||||
|
call.respond(
|
||||||
|
HttpStatusCode.BadRequest,
|
||||||
|
mapOf(
|
||||||
|
"error" to "Unsupported API version: ${result.version}",
|
||||||
|
"supportedVersions" to VersionManager.SUPPORTED_VERSIONS,
|
||||||
|
"currentVersion" to VersionManager.CURRENT_API_VERSION
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return@onCall
|
||||||
|
}
|
||||||
|
is VersionValidationResult.MissingVersion -> {
|
||||||
|
call.attributes.put(ClientVersionKey, VersionManager.CURRENT_API_VERSION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add version info to response headers
|
||||||
|
call.response.header("API-Version", VersionManager.CURRENT_API_VERSION)
|
||||||
|
call.response.header("X-Supported-Versions", VersionManager.SUPPORTED_VERSIONS.joinToString(","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key for storing client version in call attributes
|
||||||
|
*/
|
||||||
|
val ClientVersionKey = AttributeKey<String>("ClientVersion")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension function to get client version from call
|
||||||
|
*/
|
||||||
|
fun ApplicationCall.getClientVersion(): String {
|
||||||
|
return attributes.getOrNull(ClientVersionKey) ?: VersionManager.CURRENT_API_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension function to respond with versioned data
|
||||||
|
*/
|
||||||
|
suspend inline fun <reified T : VersionedDto> ApplicationCall.respondVersioned(
|
||||||
|
status: HttpStatusCode = HttpStatusCode.OK,
|
||||||
|
data: T
|
||||||
|
) {
|
||||||
|
val versionedResponse = VersionedResponse(
|
||||||
|
data = data,
|
||||||
|
version = VersionManager.getVersionInfo(),
|
||||||
|
timestamp = Clock.System.now().toString()
|
||||||
|
)
|
||||||
|
respond(status, versionedResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension function to respond with versioned list data
|
||||||
|
*/
|
||||||
|
suspend inline fun <reified T : VersionedDto> ApplicationCall.respondVersionedList(
|
||||||
|
status: HttpStatusCode = HttpStatusCode.OK,
|
||||||
|
data: List<T>
|
||||||
|
) {
|
||||||
|
val response = mapOf(
|
||||||
|
"items" to data,
|
||||||
|
"count" to data.size,
|
||||||
|
"version" to VersionManager.getVersionInfo(),
|
||||||
|
"timestamp" to Clock.System.now().toString()
|
||||||
|
)
|
||||||
|
respond(status, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure versioning for the application
|
||||||
|
*/
|
||||||
|
fun Application.configureVersioning() {
|
||||||
|
install(VersioningPlugin)
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package at.mocode.routes
|
package at.mocode.routes
|
||||||
|
|
||||||
import at.mocode.repositories.PostgresVereinRepository
|
import at.mocode.services.ServiceLocator
|
||||||
import at.mocode.repositories.VereinRepository
|
|
||||||
import at.mocode.stammdaten.Verein
|
import at.mocode.stammdaten.Verein
|
||||||
import com.benasher44.uuid.uuidFrom
|
import com.benasher44.uuid.uuidFrom
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
@@ -10,13 +9,13 @@ import io.ktor.server.response.*
|
|||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Route.vereinRoutes() {
|
fun Route.vereinRoutes() {
|
||||||
val vereinRepository: VereinRepository = PostgresVereinRepository()
|
val vereinService = ServiceLocator.vereinService
|
||||||
|
|
||||||
route("/api/vereine") {
|
route("/api/vereine") {
|
||||||
// GET /api/vereine - Get all clubs
|
// GET /api/vereine - Get all clubs
|
||||||
get {
|
get {
|
||||||
try {
|
try {
|
||||||
val vereine = vereinRepository.findAll()
|
val vereine = vereinService.getAllVereine()
|
||||||
call.respond(HttpStatusCode.OK, vereine)
|
call.respond(HttpStatusCode.OK, vereine)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message))
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message))
|
||||||
@@ -31,7 +30,7 @@ fun Route.vereinRoutes() {
|
|||||||
mapOf("error" to "Missing verein ID")
|
mapOf("error" to "Missing verein ID")
|
||||||
)
|
)
|
||||||
val uuid = uuidFrom(id)
|
val uuid = uuidFrom(id)
|
||||||
val verein = vereinRepository.findById(uuid)
|
val verein = vereinService.getVereinById(uuid)
|
||||||
if (verein != null) {
|
if (verein != null) {
|
||||||
call.respond(HttpStatusCode.OK, verein)
|
call.respond(HttpStatusCode.OK, verein)
|
||||||
} else {
|
} else {
|
||||||
@@ -51,7 +50,7 @@ fun Route.vereinRoutes() {
|
|||||||
HttpStatusCode.BadRequest,
|
HttpStatusCode.BadRequest,
|
||||||
mapOf("error" to "Missing OEPS Vereins number")
|
mapOf("error" to "Missing OEPS Vereins number")
|
||||||
)
|
)
|
||||||
val verein = vereinRepository.findByOepsVereinsNr(oepsVereinsNr)
|
val verein = vereinService.getVereinByOepsNr(oepsVereinsNr)
|
||||||
if (verein != null) {
|
if (verein != null) {
|
||||||
call.respond(HttpStatusCode.OK, verein)
|
call.respond(HttpStatusCode.OK, verein)
|
||||||
} else {
|
} else {
|
||||||
@@ -69,7 +68,7 @@ fun Route.vereinRoutes() {
|
|||||||
HttpStatusCode.BadRequest,
|
HttpStatusCode.BadRequest,
|
||||||
mapOf("error" to "Missing search query parameter 'q'")
|
mapOf("error" to "Missing search query parameter 'q'")
|
||||||
)
|
)
|
||||||
val vereine = vereinRepository.search(query)
|
val vereine = vereinService.searchVereine(query)
|
||||||
call.respond(HttpStatusCode.OK, vereine)
|
call.respond(HttpStatusCode.OK, vereine)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message))
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message))
|
||||||
@@ -83,7 +82,7 @@ fun Route.vereinRoutes() {
|
|||||||
HttpStatusCode.BadRequest,
|
HttpStatusCode.BadRequest,
|
||||||
mapOf("error" to "Missing bundesland")
|
mapOf("error" to "Missing bundesland")
|
||||||
)
|
)
|
||||||
val vereine = vereinRepository.findByBundesland(bundesland)
|
val vereine = vereinService.getVereineByBundesland(bundesland)
|
||||||
call.respond(HttpStatusCode.OK, vereine)
|
call.respond(HttpStatusCode.OK, vereine)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message))
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to e.message))
|
||||||
@@ -94,7 +93,7 @@ fun Route.vereinRoutes() {
|
|||||||
post {
|
post {
|
||||||
try {
|
try {
|
||||||
val verein = call.receive<Verein>()
|
val verein = call.receive<Verein>()
|
||||||
val createdVerein = vereinRepository.create(verein)
|
val createdVerein = vereinService.createVerein(verein)
|
||||||
call.respond(HttpStatusCode.Created, createdVerein)
|
call.respond(HttpStatusCode.Created, createdVerein)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
call.respond(HttpStatusCode.BadRequest, mapOf("error" to e.message))
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to e.message))
|
||||||
@@ -110,7 +109,7 @@ fun Route.vereinRoutes() {
|
|||||||
)
|
)
|
||||||
val uuid = uuidFrom(id)
|
val uuid = uuidFrom(id)
|
||||||
val verein = call.receive<Verein>()
|
val verein = call.receive<Verein>()
|
||||||
val updatedVerein = vereinRepository.update(uuid, verein)
|
val updatedVerein = vereinService.updateVerein(uuid, verein)
|
||||||
if (updatedVerein != null) {
|
if (updatedVerein != null) {
|
||||||
call.respond(HttpStatusCode.OK, updatedVerein)
|
call.respond(HttpStatusCode.OK, updatedVerein)
|
||||||
} else {
|
} else {
|
||||||
@@ -131,7 +130,7 @@ fun Route.vereinRoutes() {
|
|||||||
mapOf("error" to "Missing verein ID")
|
mapOf("error" to "Missing verein ID")
|
||||||
)
|
)
|
||||||
val uuid = uuidFrom(id)
|
val uuid = uuidFrom(id)
|
||||||
val deleted = vereinRepository.delete(uuid)
|
val deleted = vereinService.deleteVerein(uuid)
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
call.respond(HttpStatusCode.NoContent)
|
call.respond(HttpStatusCode.NoContent)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.Abteilung
|
||||||
|
import at.mocode.repositories.AbteilungRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Abteilung (Division/Section) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class AbteilungService(private val abteilungRepository: AbteilungRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all divisions
|
||||||
|
*/
|
||||||
|
suspend fun getAllAbteilungen(): List<Abteilung> {
|
||||||
|
return abteilungRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a division by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getAbteilungById(id: Uuid): Abteilung? {
|
||||||
|
return abteilungRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find divisions by competition (Bewerb) ID
|
||||||
|
*/
|
||||||
|
suspend fun getAbteilungenByBewerbId(bewerbId: Uuid): List<Abteilung> {
|
||||||
|
return abteilungRepository.findByBewerbId(bewerbId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find divisions by active status
|
||||||
|
*/
|
||||||
|
suspend fun getAbteilungenByAktiv(istAktiv: Boolean): List<Abteilung> {
|
||||||
|
return abteilungRepository.findByAktiv(istAktiv)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all active divisions
|
||||||
|
*/
|
||||||
|
suspend fun getActiveAbteilungen(): List<Abteilung> {
|
||||||
|
return getAbteilungenByAktiv(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all inactive divisions
|
||||||
|
*/
|
||||||
|
suspend fun getInactiveAbteilungen(): List<Abteilung> {
|
||||||
|
return getAbteilungenByAktiv(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for divisions by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchAbteilungen(query: String): List<Abteilung> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return abteilungRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new division with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createAbteilung(abteilung: Abteilung): Abteilung {
|
||||||
|
validateAbteilung(abteilung)
|
||||||
|
return abteilungRepository.create(abteilung)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing division
|
||||||
|
*/
|
||||||
|
suspend fun updateAbteilung(id: Uuid, abteilung: Abteilung): Abteilung? {
|
||||||
|
validateAbteilung(abteilung)
|
||||||
|
return abteilungRepository.update(id, abteilung)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a division by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteAbteilung(id: Uuid): Boolean {
|
||||||
|
return abteilungRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate a division (soft delete)
|
||||||
|
*/
|
||||||
|
suspend fun deactivateAbteilung(id: Uuid): Abteilung? {
|
||||||
|
val abteilung = getAbteilungById(id)
|
||||||
|
return if (abteilung != null) {
|
||||||
|
val updatedAbteilung = abteilung.copy(istAktiv = false)
|
||||||
|
updateAbteilung(id, updatedAbteilung)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate a division
|
||||||
|
*/
|
||||||
|
suspend fun activateAbteilung(id: Uuid): Abteilung? {
|
||||||
|
val abteilung = getAbteilungById(id)
|
||||||
|
return if (abteilung != null) {
|
||||||
|
val updatedAbteilung = abteilung.copy(istAktiv = true)
|
||||||
|
updateAbteilung(id, updatedAbteilung)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get divisions for a specific competition ordered by sequence
|
||||||
|
*/
|
||||||
|
suspend fun getAbteilungenForBewerbOrdered(bewerbId: Uuid): List<Abteilung> {
|
||||||
|
val abteilungen = getAbteilungenByBewerbId(bewerbId)
|
||||||
|
// Sort by abteilungsKennzeichen for basic ordering
|
||||||
|
return abteilungen.sortedBy { it.abteilungsKennzeichen }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate division data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateAbteilung(abteilung: Abteilung) {
|
||||||
|
if (abteilung.abteilungsKennzeichen.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Division identifier (abteilungsKennzeichen) cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.abteilungsKennzeichen.length > 50) {
|
||||||
|
throw IllegalArgumentException("Division identifier cannot exceed 50 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate participant count constraints
|
||||||
|
if (abteilung.teilungsKriteriumAnzahlMin != null && abteilung.teilungsKriteriumAnzahlMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Minimum participant count cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.teilungsKriteriumAnzahlMax != null && abteilung.teilungsKriteriumAnzahlMax!! < 0) {
|
||||||
|
throw IllegalArgumentException("Maximum participant count cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.teilungsKriteriumAnzahlMin != null && abteilung.teilungsKriteriumAnzahlMax != null) {
|
||||||
|
if (abteilung.teilungsKriteriumAnzahlMin!! > abteilung.teilungsKriteriumAnzahlMax!!) {
|
||||||
|
throw IllegalArgumentException("Minimum participant count cannot be greater than maximum")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate timing constraints
|
||||||
|
if (abteilung.dauerProStartGeschaetztSek != null && abteilung.dauerProStartGeschaetztSek!! < 0) {
|
||||||
|
throw IllegalArgumentException("Estimated duration per start cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.umbauzeitNachAbteilungMin != null && abteilung.umbauzeitNachAbteilungMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Setup time after division cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.besichtigungszeitVorAbteilungMin != null && abteilung.besichtigungszeitVorAbteilungMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Inspection time before division cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.stechzeitZusaetzlichMin != null && abteilung.stechzeitZusaetzlichMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Additional jump-off time cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abteilung.anzahlStarter < 0) {
|
||||||
|
throw IllegalArgumentException("Number of starters cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate text field lengths
|
||||||
|
abteilung.bezeichnungIntern?.let { bezeichnung ->
|
||||||
|
if (bezeichnung.length > 255) {
|
||||||
|
throw IllegalArgumentException("Internal designation cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abteilung.bezeichnungAufStartliste?.let { bezeichnung ->
|
||||||
|
if (bezeichnung.length > 255) {
|
||||||
|
throw IllegalArgumentException("Start list designation cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abteilung.teilungsKriteriumFreiText?.let { freiText ->
|
||||||
|
if (freiText.length > 500) {
|
||||||
|
throw IllegalArgumentException("Free text division criterion cannot exceed 500 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.Artikel
|
||||||
|
import at.mocode.repositories.ArtikelRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Artikel (Article) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class ArtikelService(private val artikelRepository: ArtikelRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all articles
|
||||||
|
*/
|
||||||
|
suspend fun getAllArtikel(): List<Artikel> {
|
||||||
|
return artikelRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an article by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getArtikelById(id: Uuid): Artikel? {
|
||||||
|
return artikelRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find articles by Verbandsabgabe status
|
||||||
|
*/
|
||||||
|
suspend fun getArtikelByVerbandsabgabe(istVerbandsabgabe: Boolean): List<Artikel> {
|
||||||
|
return artikelRepository.findByVerbandsabgabe(istVerbandsabgabe)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for articles by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchArtikel(query: String): List<Artikel> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return artikelRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new article with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createArtikel(artikel: Artikel): Artikel {
|
||||||
|
validateArtikel(artikel)
|
||||||
|
return artikelRepository.create(artikel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing article
|
||||||
|
*/
|
||||||
|
suspend fun updateArtikel(id: Uuid, artikel: Artikel): Artikel? {
|
||||||
|
validateArtikel(artikel)
|
||||||
|
return artikelRepository.update(id, artikel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an article by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteArtikel(id: Uuid): Boolean {
|
||||||
|
return artikelRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all Verbandsabgabe articles (federation fee articles)
|
||||||
|
*/
|
||||||
|
suspend fun getVerbandsabgabeArtikel(): List<Artikel> {
|
||||||
|
return getArtikelByVerbandsabgabe(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all non-Verbandsabgabe articles
|
||||||
|
*/
|
||||||
|
suspend fun getNonVerbandsabgabeArtikel(): List<Artikel> {
|
||||||
|
return getArtikelByVerbandsabgabe(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate article data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateArtikel(artikel: Artikel) {
|
||||||
|
if (artikel.bezeichnung.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Article bezeichnung cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artikel.bezeichnung.length > 255) {
|
||||||
|
throw IllegalArgumentException("Article bezeichnung cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artikel.preis < BigDecimal.ZERO) {
|
||||||
|
throw IllegalArgumentException("Article price cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artikel.einheit.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Article einheit cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artikel.einheit.length > 50) {
|
||||||
|
throw IllegalArgumentException("Article einheit cannot exceed 50 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.Bewerb
|
||||||
|
import at.mocode.repositories.BewerbRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Bewerb (Competition) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class BewerbService(private val bewerbRepository: BewerbRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all competitions
|
||||||
|
*/
|
||||||
|
suspend fun getAllBewerbe(): List<Bewerb> {
|
||||||
|
return bewerbRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a competition by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbById(id: Uuid): Bewerb? {
|
||||||
|
return bewerbRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find competitions by tournament ID
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeByTurnierId(turnierId: Uuid): List<Bewerb> {
|
||||||
|
return bewerbRepository.findByTurnierId(turnierId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find competitions by sport category (Sparte)
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeBySparte(sparte: String): List<Bewerb> {
|
||||||
|
if (sparte.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Sparte cannot be blank")
|
||||||
|
}
|
||||||
|
return bewerbRepository.findBySparte(sparte.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find competitions by class
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeByKlasse(klasse: String): List<Bewerb> {
|
||||||
|
if (klasse.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Klasse cannot be blank")
|
||||||
|
}
|
||||||
|
return bewerbRepository.findByKlasse(klasse.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find competitions by start list finalization status
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeByStartlisteFinal(istFinal: Boolean): List<Bewerb> {
|
||||||
|
return bewerbRepository.findByStartlisteFinal(istFinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find competitions by result list finalization status
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeByErgebnislisteFinal(istFinal: Boolean): List<Bewerb> {
|
||||||
|
return bewerbRepository.findByErgebnislisteFinal(istFinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get competitions with finalized start lists
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeWithFinalStartliste(): List<Bewerb> {
|
||||||
|
return getBewerbeByStartlisteFinal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get competitions with finalized result lists
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeWithFinalErgebnisliste(): List<Bewerb> {
|
||||||
|
return getBewerbeByErgebnislisteFinal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for competitions by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchBewerbe(query: String): List<Bewerb> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return bewerbRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new competition with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createBewerb(bewerb: Bewerb): Bewerb {
|
||||||
|
validateBewerb(bewerb)
|
||||||
|
return bewerbRepository.create(bewerb)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing competition
|
||||||
|
*/
|
||||||
|
suspend fun updateBewerb(id: Uuid, bewerb: Bewerb): Bewerb? {
|
||||||
|
validateBewerb(bewerb)
|
||||||
|
return bewerbRepository.update(id, bewerb)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a competition by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteBewerb(id: Uuid): Boolean {
|
||||||
|
return bewerbRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize start list for a competition
|
||||||
|
*/
|
||||||
|
suspend fun finalizeStartliste(id: Uuid): Bewerb? {
|
||||||
|
val bewerb = getBewerbById(id)
|
||||||
|
return if (bewerb != null) {
|
||||||
|
val updatedBewerb = bewerb.copy(istStartlisteFinal = true)
|
||||||
|
updateBewerb(id, updatedBewerb)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize result list for a competition
|
||||||
|
*/
|
||||||
|
suspend fun finalizeErgebnisliste(id: Uuid): Bewerb? {
|
||||||
|
val bewerb = getBewerbById(id)
|
||||||
|
return if (bewerb != null) {
|
||||||
|
val updatedBewerb = bewerb.copy(istErgebnislisteFinal = true)
|
||||||
|
updateBewerb(id, updatedBewerb)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopen start list for a competition
|
||||||
|
*/
|
||||||
|
suspend fun reopenStartliste(id: Uuid): Bewerb? {
|
||||||
|
val bewerb = getBewerbById(id)
|
||||||
|
return if (bewerb != null) {
|
||||||
|
val updatedBewerb = bewerb.copy(istStartlisteFinal = false)
|
||||||
|
updateBewerb(id, updatedBewerb)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopen result list for a competition
|
||||||
|
*/
|
||||||
|
suspend fun reopenErgebnisliste(id: Uuid): Bewerb? {
|
||||||
|
val bewerb = getBewerbById(id)
|
||||||
|
return if (bewerb != null) {
|
||||||
|
val updatedBewerb = bewerb.copy(istErgebnislisteFinal = false)
|
||||||
|
updateBewerb(id, updatedBewerb)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get competitions for a specific tournament ordered by number
|
||||||
|
*/
|
||||||
|
suspend fun getBewerbeForTurnierOrdered(turnierId: Uuid): List<Bewerb> {
|
||||||
|
val bewerbe = getBewerbeByTurnierId(turnierId)
|
||||||
|
return bewerbe.sortedBy { it.nummer }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate competition data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateBewerb(bewerb: Bewerb) {
|
||||||
|
if (bewerb.nummer.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Competition number cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.nummer.length > 50) {
|
||||||
|
throw IllegalArgumentException("Competition number cannot exceed 50 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.bezeichnungOffiziell.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Official designation cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.bezeichnungOffiziell.length > 255) {
|
||||||
|
throw IllegalArgumentException("Official designation cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate participant constraints
|
||||||
|
if (bewerb.maxPferdeProReiter != null && bewerb.maxPferdeProReiter!! < 1) {
|
||||||
|
throw IllegalArgumentException("Maximum horses per rider must be at least 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.anzahlRichterGeplant != null && bewerb.anzahlRichterGeplant!! < 1) {
|
||||||
|
throw IllegalArgumentException("Number of planned judges must be at least 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate timing constraints
|
||||||
|
if (bewerb.standardDauerProStartGeschaetztSek != null && bewerb.standardDauerProStartGeschaetztSek!! < 0) {
|
||||||
|
throw IllegalArgumentException("Estimated duration per start cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.standardUmbauzeitNachBewerbMin != null && bewerb.standardUmbauzeitNachBewerbMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Setup time after competition cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.standardBesichtigungszeitVorBewerbMin != null && bewerb.standardBesichtigungszeitVorBewerbMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Inspection time before competition cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bewerb.standardStechzeitZusaetzlichMin != null && bewerb.standardStechzeitZusaetzlichMin!! < 0) {
|
||||||
|
throw IllegalArgumentException("Additional jump-off time cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate text field lengths
|
||||||
|
bewerb.internerName?.let { name ->
|
||||||
|
if (name.length > 255) {
|
||||||
|
throw IllegalArgumentException("Internal name cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bewerb.klasse?.let { klasse ->
|
||||||
|
if (klasse.length > 100) {
|
||||||
|
throw IllegalArgumentException("Class cannot exceed 100 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bewerb.kategorieOetoDesBewerbs?.let { kategorie ->
|
||||||
|
if (kategorie.length > 100) {
|
||||||
|
throw IllegalArgumentException("ÖTO category cannot exceed 100 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bewerb.teilnahmebedingungenText?.let { text ->
|
||||||
|
if (text.length > 1000) {
|
||||||
|
throw IllegalArgumentException("Participation conditions text cannot exceed 1000 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bewerb.notizenIntern?.let { notizen ->
|
||||||
|
if (notizen.length > 1000) {
|
||||||
|
throw IllegalArgumentException("Internal notes cannot exceed 1000 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.domaene.DomLizenz
|
||||||
|
import at.mocode.repositories.DomLizenzRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for DomLizenz (Domain License) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class DomLizenzService(private val domLizenzRepository: DomLizenzRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all licenses
|
||||||
|
*/
|
||||||
|
suspend fun getAllLizenzen(): List<DomLizenz> {
|
||||||
|
return domLizenzRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a license by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getLizenzById(id: Uuid): DomLizenz? {
|
||||||
|
return domLizenzRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find licenses by person ID
|
||||||
|
*/
|
||||||
|
suspend fun getLizenzenByPersonId(personId: Uuid): List<DomLizenz> {
|
||||||
|
return domLizenzRepository.findByPersonId(personId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find licenses by license type global ID
|
||||||
|
*/
|
||||||
|
suspend fun getLizenzenByLizenzTypGlobalId(lizenzTypGlobalId: Uuid): List<DomLizenz> {
|
||||||
|
return domLizenzRepository.findByLizenzTypGlobalId(lizenzTypGlobalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find active licenses by person ID
|
||||||
|
*/
|
||||||
|
suspend fun getActiveLizenzenByPersonId(personId: Uuid): List<DomLizenz> {
|
||||||
|
return domLizenzRepository.findActiveByPersonId(personId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find licenses by validity year
|
||||||
|
*/
|
||||||
|
suspend fun getLizenzenByValidityYear(year: Int): List<DomLizenz> {
|
||||||
|
if (year < 1900 || year > 2100) {
|
||||||
|
throw IllegalArgumentException("Year must be between 1900 and 2100")
|
||||||
|
}
|
||||||
|
return domLizenzRepository.findByValidityYear(year)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for licenses by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchLizenzen(query: String): List<DomLizenz> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return domLizenzRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new license with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createLizenz(domLizenz: DomLizenz): DomLizenz {
|
||||||
|
validateLizenz(domLizenz)
|
||||||
|
return domLizenzRepository.create(domLizenz)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing license
|
||||||
|
*/
|
||||||
|
suspend fun updateLizenz(id: Uuid, domLizenz: DomLizenz): DomLizenz? {
|
||||||
|
validateLizenz(domLizenz)
|
||||||
|
return domLizenzRepository.update(id, domLizenz)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a license by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteLizenz(id: Uuid): Boolean {
|
||||||
|
return domLizenzRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a person has an active license of a specific type
|
||||||
|
*/
|
||||||
|
suspend fun hasActiveLicense(personId: Uuid, lizenzTypGlobalId: Uuid): Boolean {
|
||||||
|
val activeLicenses = getActiveLizenzenByPersonId(personId)
|
||||||
|
return activeLicenses.any { it.lizenzTypGlobalId == lizenzTypGlobalId }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current year licenses for a person
|
||||||
|
*/
|
||||||
|
suspend fun getCurrentYearLizenzenByPersonId(personId: Uuid): List<DomLizenz> {
|
||||||
|
val currentYear = java.time.LocalDate.now().year
|
||||||
|
val allPersonLicenses = getLizenzenByPersonId(personId)
|
||||||
|
return allPersonLicenses.filter { license ->
|
||||||
|
license.gueltigBisJahr == currentYear || license.ausgestelltAm?.year == currentYear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate license data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateLizenz(domLizenz: DomLizenz) {
|
||||||
|
// Validate that gueltigBisJahr is reasonable if provided
|
||||||
|
domLizenz.gueltigBisJahr?.let { year ->
|
||||||
|
if (year < 1900 || year > 2100) {
|
||||||
|
throw IllegalArgumentException("License validity year must be between 1900 and 2100")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that ausgestelltAm is not in the future if provided
|
||||||
|
domLizenz.ausgestelltAm?.let { date ->
|
||||||
|
val currentYear = java.time.LocalDate.now().year
|
||||||
|
if (date.year > currentYear) {
|
||||||
|
throw IllegalArgumentException("License issue date cannot be in the future")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
// For example, checking if the license type is valid, person exists, etc.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.domaene.DomPferd
|
||||||
|
import at.mocode.repositories.DomPferdRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for DomPferd (Domain Horse) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class DomPferdService(private val domPferdRepository: DomPferdRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all horses
|
||||||
|
*/
|
||||||
|
suspend fun getAllPferde(): List<DomPferd> {
|
||||||
|
return domPferdRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a horse by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getPferdById(id: Uuid): DomPferd? {
|
||||||
|
return domPferdRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a horse by its OEPS Satz number
|
||||||
|
*/
|
||||||
|
suspend fun getPferdByOepsSatzNr(oepsSatzNr: String): DomPferd? {
|
||||||
|
if (oepsSatzNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS Satz number cannot be blank")
|
||||||
|
}
|
||||||
|
return domPferdRepository.findByOepsSatzNr(oepsSatzNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find horses by name
|
||||||
|
*/
|
||||||
|
suspend fun getPferdeByName(name: String): List<DomPferd> {
|
||||||
|
if (name.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Horse name cannot be blank")
|
||||||
|
}
|
||||||
|
return domPferdRepository.findByName(name.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a horse by its life number (Lebensnummer)
|
||||||
|
*/
|
||||||
|
suspend fun getPferdByLebensnummer(lebensnummer: String): DomPferd? {
|
||||||
|
if (lebensnummer.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Life number cannot be blank")
|
||||||
|
}
|
||||||
|
return domPferdRepository.findByLebensnummer(lebensnummer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find horses by owner ID
|
||||||
|
*/
|
||||||
|
suspend fun getPferdeByBesitzerId(besitzerId: Uuid): List<DomPferd> {
|
||||||
|
return domPferdRepository.findByBesitzerId(besitzerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find horses by responsible person ID
|
||||||
|
*/
|
||||||
|
suspend fun getPferdeByVerantwortlichePersonId(personId: Uuid): List<DomPferd> {
|
||||||
|
return domPferdRepository.findByVerantwortlichePersonId(personId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find horses by home club ID
|
||||||
|
*/
|
||||||
|
suspend fun getPferdeByHeimatVereinId(vereinId: Uuid): List<DomPferd> {
|
||||||
|
return domPferdRepository.findByHeimatVereinId(vereinId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find horses by breed
|
||||||
|
*/
|
||||||
|
suspend fun getPferdeByRasse(rasse: String): List<DomPferd> {
|
||||||
|
if (rasse.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Breed cannot be blank")
|
||||||
|
}
|
||||||
|
return domPferdRepository.findByRasse(rasse.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find horses by birth year
|
||||||
|
*/
|
||||||
|
suspend fun getPferdeByGeburtsjahr(geburtsjahr: Int): List<DomPferd> {
|
||||||
|
if (geburtsjahr < 1900 || geburtsjahr > java.time.LocalDate.now().year) {
|
||||||
|
throw IllegalArgumentException("Birth year must be between 1900 and current year")
|
||||||
|
}
|
||||||
|
return domPferdRepository.findByGeburtsjahr(geburtsjahr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all active horses
|
||||||
|
*/
|
||||||
|
suspend fun getActivePferde(): List<DomPferd> {
|
||||||
|
return domPferdRepository.findActiveHorses()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for horses by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchPferde(query: String): List<DomPferd> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return domPferdRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new horse with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createPferd(domPferd: DomPferd): DomPferd {
|
||||||
|
validatePferd(domPferd)
|
||||||
|
|
||||||
|
// Check if OEPS Satz number already exists
|
||||||
|
domPferd.oepsSatzNrPferd?.let { oepsNr ->
|
||||||
|
val existing = domPferdRepository.findByOepsSatzNr(oepsNr)
|
||||||
|
if (existing != null) {
|
||||||
|
throw IllegalArgumentException("A horse with OEPS Satz number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if life number already exists
|
||||||
|
domPferd.lebensnummer?.let { lebensnummer ->
|
||||||
|
val existing = domPferdRepository.findByLebensnummer(lebensnummer)
|
||||||
|
if (existing != null) {
|
||||||
|
throw IllegalArgumentException("A horse with life number '$lebensnummer' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return domPferdRepository.create(domPferd)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing horse
|
||||||
|
*/
|
||||||
|
suspend fun updatePferd(id: Uuid, domPferd: DomPferd): DomPferd? {
|
||||||
|
validatePferd(domPferd)
|
||||||
|
|
||||||
|
// Check if OEPS Satz number conflicts with another horse
|
||||||
|
domPferd.oepsSatzNrPferd?.let { oepsNr ->
|
||||||
|
val existing = domPferdRepository.findByOepsSatzNr(oepsNr)
|
||||||
|
if (existing != null && existing.pferdId != id) {
|
||||||
|
throw IllegalArgumentException("A horse with OEPS Satz number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if life number conflicts with another horse
|
||||||
|
domPferd.lebensnummer?.let { lebensnummer ->
|
||||||
|
val existing = domPferdRepository.findByLebensnummer(lebensnummer)
|
||||||
|
if (existing != null && existing.pferdId != id) {
|
||||||
|
throw IllegalArgumentException("A horse with life number '$lebensnummer' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return domPferdRepository.update(id, domPferd)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a horse by ID
|
||||||
|
*/
|
||||||
|
suspend fun deletePferd(id: Uuid): Boolean {
|
||||||
|
return domPferdRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate horse data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validatePferd(domPferd: DomPferd) {
|
||||||
|
if (domPferd.name.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Horse name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domPferd.name.length > 100) {
|
||||||
|
throw IllegalArgumentException("Horse name cannot exceed 100 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate birth year if provided
|
||||||
|
domPferd.geburtsjahr?.let { year ->
|
||||||
|
if (year < 1900 || year > java.time.LocalDate.now().year) {
|
||||||
|
throw IllegalArgumentException("Birth year must be between 1900 and current year")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
domPferd.oepsSatzNrPferd?.let { oepsNr ->
|
||||||
|
if (oepsNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS Satz number cannot be blank if provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
domPferd.lebensnummer?.let { lebensnummer ->
|
||||||
|
if (lebensnummer.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Life number cannot be blank if provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.domaene.DomQualifikation
|
||||||
|
import at.mocode.repositories.DomQualifikationRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for DomQualifikation (Domain Qualification) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class DomQualifikationService(private val domQualifikationRepository: DomQualifikationRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all qualifications
|
||||||
|
*/
|
||||||
|
suspend fun getAllQualifikationen(): List<DomQualifikation> {
|
||||||
|
return domQualifikationRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a qualification by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getQualifikationById(id: Uuid): DomQualifikation? {
|
||||||
|
return domQualifikationRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find qualifications by person ID
|
||||||
|
*/
|
||||||
|
suspend fun getQualifikationenByPersonId(personId: Uuid): List<DomQualifikation> {
|
||||||
|
return domQualifikationRepository.findByPersonId(personId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find qualifications by qualification type ID
|
||||||
|
*/
|
||||||
|
suspend fun getQualifikationenByQualTypId(qualTypId: Uuid): List<DomQualifikation> {
|
||||||
|
return domQualifikationRepository.findByQualTypId(qualTypId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find active qualifications by person ID
|
||||||
|
*/
|
||||||
|
suspend fun getActiveQualifikationenByPersonId(personId: Uuid): List<DomQualifikation> {
|
||||||
|
return domQualifikationRepository.findActiveByPersonId(personId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find qualifications by validity period
|
||||||
|
*/
|
||||||
|
suspend fun getQualifikationenByValidityPeriod(fromDate: LocalDate?, toDate: LocalDate?): List<DomQualifikation> {
|
||||||
|
// Validate date range if both dates are provided
|
||||||
|
if (fromDate != null && toDate != null && fromDate > toDate) {
|
||||||
|
throw IllegalArgumentException("From date must be before or equal to to date")
|
||||||
|
}
|
||||||
|
return domQualifikationRepository.findByValidityPeriod(fromDate, toDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for qualifications by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchQualifikationen(query: String): List<DomQualifikation> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return domQualifikationRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new qualification with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createQualifikation(domQualifikation: DomQualifikation): DomQualifikation {
|
||||||
|
validateQualifikation(domQualifikation)
|
||||||
|
return domQualifikationRepository.create(domQualifikation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing qualification
|
||||||
|
*/
|
||||||
|
suspend fun updateQualifikation(id: Uuid, domQualifikation: DomQualifikation): DomQualifikation? {
|
||||||
|
validateQualifikation(domQualifikation)
|
||||||
|
return domQualifikationRepository.update(id, domQualifikation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a qualification by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteQualifikation(id: Uuid): Boolean {
|
||||||
|
return domQualifikationRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a person has an active qualification of a specific type
|
||||||
|
*/
|
||||||
|
suspend fun hasActiveQualification(personId: Uuid, qualTypId: Uuid): Boolean {
|
||||||
|
val activeQualifications = getActiveQualifikationenByPersonId(personId)
|
||||||
|
return activeQualifications.any { it.qualTypId == qualTypId }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current valid qualifications for a person
|
||||||
|
*/
|
||||||
|
suspend fun getCurrentValidQualifikationenByPersonId(personId: Uuid): List<DomQualifikation> {
|
||||||
|
val currentJavaDate = java.time.LocalDate.now()
|
||||||
|
val currentLocalDate = kotlinx.datetime.LocalDate(currentJavaDate.year, currentJavaDate.monthValue, currentJavaDate.dayOfMonth)
|
||||||
|
|
||||||
|
val allPersonQualifications = getQualifikationenByPersonId(personId)
|
||||||
|
return allPersonQualifications.filter { qualification ->
|
||||||
|
qualification.istAktiv &&
|
||||||
|
(qualification.gueltigVon == null || qualification.gueltigVon!! <= currentLocalDate) &&
|
||||||
|
(qualification.gueltigBis == null || qualification.gueltigBis!! >= currentLocalDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate a qualification (soft delete)
|
||||||
|
*/
|
||||||
|
suspend fun deactivateQualifikation(id: Uuid): DomQualifikation? {
|
||||||
|
val qualification = getQualifikationById(id)
|
||||||
|
return if (qualification != null) {
|
||||||
|
val updatedQualification = qualification.copy(istAktiv = false)
|
||||||
|
updateQualifikation(id, updatedQualification)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate qualification data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateQualifikation(domQualifikation: DomQualifikation) {
|
||||||
|
// Validate validity date range if both dates are provided
|
||||||
|
if (domQualifikation.gueltigVon != null && domQualifikation.gueltigBis != null) {
|
||||||
|
if (domQualifikation.gueltigVon!! > domQualifikation.gueltigBis!!) {
|
||||||
|
throw IllegalArgumentException("Qualification validity start date must be before or equal to end date")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that gueltigBis is not in the past for new active qualifications
|
||||||
|
if (domQualifikation.istAktiv && domQualifikation.gueltigBis != null) {
|
||||||
|
val currentJavaDate = java.time.LocalDate.now()
|
||||||
|
val currentLocalDate = kotlinx.datetime.LocalDate(currentJavaDate.year, currentJavaDate.monthValue, currentJavaDate.dayOfMonth)
|
||||||
|
|
||||||
|
if (domQualifikation.gueltigBis!! < currentLocalDate) {
|
||||||
|
throw IllegalArgumentException("Cannot create active qualification with end date in the past")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
domQualifikation.bemerkung?.let { bemerkung ->
|
||||||
|
if (bemerkung.length > 1000) {
|
||||||
|
throw IllegalArgumentException("Qualification remark cannot exceed 1000 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.stammdaten.Person
|
||||||
|
import at.mocode.repositories.PersonRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Person business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class PersonService(private val personRepository: PersonRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all persons
|
||||||
|
*/
|
||||||
|
suspend fun getAllPersons(): List<Person> {
|
||||||
|
return personRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a person by their unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getPersonById(id: Uuid): Person? {
|
||||||
|
return personRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a person by their OEPS Satz number
|
||||||
|
*/
|
||||||
|
suspend fun getPersonByOepsSatzNr(oepsSatzNr: String): Person? {
|
||||||
|
if (oepsSatzNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS Satz number cannot be blank")
|
||||||
|
}
|
||||||
|
return personRepository.findByOepsSatzNr(oepsSatzNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find persons by Verein (club) ID
|
||||||
|
*/
|
||||||
|
suspend fun getPersonsByVereinId(vereinId: Uuid): List<Person> {
|
||||||
|
return personRepository.findByVereinId(vereinId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for persons by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchPersons(query: String): List<Person> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return personRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new person with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createPerson(person: Person): Person {
|
||||||
|
validatePerson(person)
|
||||||
|
|
||||||
|
// Check if OEPS Satz number already exists
|
||||||
|
person.oepsSatzNr?.let { oepsNr ->
|
||||||
|
val existing = personRepository.findByOepsSatzNr(oepsNr)
|
||||||
|
if (existing != null) {
|
||||||
|
throw IllegalArgumentException("A person with OEPS Satz number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return personRepository.create(person)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing person
|
||||||
|
*/
|
||||||
|
suspend fun updatePerson(id: Uuid, person: Person): Person? {
|
||||||
|
validatePerson(person)
|
||||||
|
|
||||||
|
// Check if OEPS Satz number conflicts with another person
|
||||||
|
person.oepsSatzNr?.let { oepsNr ->
|
||||||
|
val existing = personRepository.findByOepsSatzNr(oepsNr)
|
||||||
|
if (existing != null && existing.id != id) {
|
||||||
|
throw IllegalArgumentException("A person with OEPS Satz number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return personRepository.update(id, person)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a person by ID
|
||||||
|
*/
|
||||||
|
suspend fun deletePerson(id: Uuid): Boolean {
|
||||||
|
return personRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate person data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validatePerson(person: Person) {
|
||||||
|
if (person.vorname.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Person first name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (person.nachname.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Person last name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (person.vorname.length > 100) {
|
||||||
|
throw IllegalArgumentException("Person first name cannot exceed 100 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (person.nachname.length > 100) {
|
||||||
|
throw IllegalArgumentException("Person last name cannot exceed 100 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
person.oepsSatzNr?.let { oepsNr ->
|
||||||
|
if (oepsNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS Satz number cannot be blank if provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@ package at.mocode.services
|
|||||||
import at.mocode.repositories.*
|
import at.mocode.repositories.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service locator pattern for managing repository instances.
|
* Service locator pattern for managing repository and service instances.
|
||||||
* This provides a centralized way to access repository implementations
|
* This provides a centralized way to access repository and service implementations
|
||||||
* and makes it easier to switch implementations or add caching/decorators.
|
* and makes it easier to switch implementations or add caching/decorators.
|
||||||
*/
|
*/
|
||||||
object ServiceLocator {
|
object ServiceLocator {
|
||||||
@@ -21,10 +21,23 @@ object ServiceLocator {
|
|||||||
val turnierRepository: TurnierRepository by lazy { PostgresTurnierRepository() }
|
val turnierRepository: TurnierRepository by lazy { PostgresTurnierRepository() }
|
||||||
val veranstaltungRepository: VeranstaltungRepository by lazy { PostgresVeranstaltungRepository() }
|
val veranstaltungRepository: VeranstaltungRepository by lazy { PostgresVeranstaltungRepository() }
|
||||||
|
|
||||||
|
// Service instances - lazy initialization with dependency injection
|
||||||
|
val artikelService: ArtikelService by lazy { ArtikelService(artikelRepository) }
|
||||||
|
val vereinService: VereinService by lazy { VereinService(vereinRepository) }
|
||||||
|
val personService: PersonService by lazy { PersonService(personRepository) }
|
||||||
|
val domLizenzService: DomLizenzService by lazy { DomLizenzService(domLizenzRepository) }
|
||||||
|
val domPferdService: DomPferdService by lazy { DomPferdService(domPferdRepository) }
|
||||||
|
val domQualifikationService: DomQualifikationService by lazy { DomQualifikationService(domQualifikationRepository) }
|
||||||
|
val abteilungService: AbteilungService by lazy { AbteilungService(abteilungRepository) }
|
||||||
|
val bewerbService: BewerbService by lazy { BewerbService(bewerbRepository) }
|
||||||
|
val turnierService: TurnierService by lazy { TurnierService(turnierRepository) }
|
||||||
|
val veranstaltungService: VeranstaltungService by lazy { VeranstaltungService(veranstaltungRepository) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all repositories - useful for eager loading or validation
|
* Initialize all repositories and services - useful for eager loading or validation
|
||||||
*/
|
*/
|
||||||
fun initializeAll() {
|
fun initializeAll() {
|
||||||
|
// Initialize repositories
|
||||||
artikelRepository
|
artikelRepository
|
||||||
vereinRepository
|
vereinRepository
|
||||||
personRepository
|
personRepository
|
||||||
@@ -35,5 +48,17 @@ object ServiceLocator {
|
|||||||
bewerbRepository
|
bewerbRepository
|
||||||
turnierRepository
|
turnierRepository
|
||||||
veranstaltungRepository
|
veranstaltungRepository
|
||||||
|
|
||||||
|
// Initialize services
|
||||||
|
artikelService
|
||||||
|
vereinService
|
||||||
|
personService
|
||||||
|
domLizenzService
|
||||||
|
domPferdService
|
||||||
|
domQualifikationService
|
||||||
|
abteilungService
|
||||||
|
bewerbService
|
||||||
|
turnierService
|
||||||
|
veranstaltungService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.Turnier
|
||||||
|
import at.mocode.repositories.TurnierRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Turnier (Tournament) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class TurnierService(private val turnierRepository: TurnierRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all tournaments
|
||||||
|
*/
|
||||||
|
suspend fun getAllTurniere(): List<Turnier> {
|
||||||
|
return turnierRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a tournament by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getTurnierById(id: Uuid): Turnier? {
|
||||||
|
return turnierRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find tournaments by event (Veranstaltung) ID
|
||||||
|
*/
|
||||||
|
suspend fun getTurniereByVeranstaltungId(veranstaltungId: Uuid): List<Turnier> {
|
||||||
|
return turnierRepository.findByVeranstaltungId(veranstaltungId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a tournament by its OEPS tournament number
|
||||||
|
*/
|
||||||
|
suspend fun getTurnierByOepsTurnierNr(oepsTurnierNr: String): Turnier? {
|
||||||
|
if (oepsTurnierNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS tournament number cannot be blank")
|
||||||
|
}
|
||||||
|
return turnierRepository.findByOepsTurnierNr(oepsTurnierNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for tournaments by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchTurniere(query: String): List<Turnier> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return turnierRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new tournament with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createTurnier(turnier: Turnier): Turnier {
|
||||||
|
validateTurnier(turnier)
|
||||||
|
|
||||||
|
// Check if OEPS tournament number already exists
|
||||||
|
turnier.oepsTurnierNr?.let { oepsNr ->
|
||||||
|
val existing = turnierRepository.findByOepsTurnierNr(oepsNr)
|
||||||
|
if (existing != null) {
|
||||||
|
throw IllegalArgumentException("A tournament with OEPS number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return turnierRepository.create(turnier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing tournament
|
||||||
|
*/
|
||||||
|
suspend fun updateTurnier(id: Uuid, turnier: Turnier): Turnier? {
|
||||||
|
validateTurnier(turnier)
|
||||||
|
|
||||||
|
// Check if OEPS tournament number conflicts with another tournament
|
||||||
|
turnier.oepsTurnierNr?.let { oepsNr ->
|
||||||
|
val existing = turnierRepository.findByOepsTurnierNr(oepsNr)
|
||||||
|
if (existing != null && existing.id != id) {
|
||||||
|
throw IllegalArgumentException("A tournament with OEPS number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return turnierRepository.update(id, turnier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a tournament by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteTurnier(id: Uuid): Boolean {
|
||||||
|
return turnierRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tournaments for a specific event
|
||||||
|
*/
|
||||||
|
suspend fun getTurniereForEvent(veranstaltungId: Uuid): List<Turnier> {
|
||||||
|
return getTurniereByVeranstaltungId(veranstaltungId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate tournament data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateTurnier(turnier: Turnier) {
|
||||||
|
if (turnier.titel.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Tournament title cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnier.titel.length > 255) {
|
||||||
|
throw IllegalArgumentException("Tournament title cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate dates
|
||||||
|
if (turnier.datumVon > turnier.datumBis) {
|
||||||
|
throw IllegalArgumentException("Tournament start date must be before or equal to end date")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
if (turnier.oepsTurnierNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS tournament number cannot be blank")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.model.Veranstaltung
|
||||||
|
import at.mocode.repositories.VeranstaltungRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Veranstaltung (Event) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class VeranstaltungService(private val veranstaltungRepository: VeranstaltungRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all events
|
||||||
|
*/
|
||||||
|
suspend fun getAllVeranstaltungen(): List<Veranstaltung> {
|
||||||
|
return veranstaltungRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an event by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getVeranstaltungById(id: Uuid): Veranstaltung? {
|
||||||
|
return veranstaltungRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find events by name
|
||||||
|
*/
|
||||||
|
suspend fun getVeranstaltungenByName(name: String): List<Veranstaltung> {
|
||||||
|
if (name.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Event name cannot be blank")
|
||||||
|
}
|
||||||
|
return veranstaltungRepository.findByName(name.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find events by organizer OEPS number
|
||||||
|
*/
|
||||||
|
suspend fun getVeranstaltungenByVeranstalterOepsNummer(oepsNummer: String): List<Veranstaltung> {
|
||||||
|
if (oepsNummer.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Organizer OEPS number cannot be blank")
|
||||||
|
}
|
||||||
|
return veranstaltungRepository.findByVeranstalterOepsNummer(oepsNummer.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for events by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchVeranstaltungen(query: String): List<Veranstaltung> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return veranstaltungRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createVeranstaltung(veranstaltung: Veranstaltung): Veranstaltung {
|
||||||
|
validateVeranstaltung(veranstaltung)
|
||||||
|
return veranstaltungRepository.create(veranstaltung)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing event
|
||||||
|
*/
|
||||||
|
suspend fun updateVeranstaltung(id: Uuid, veranstaltung: Veranstaltung): Veranstaltung? {
|
||||||
|
validateVeranstaltung(veranstaltung)
|
||||||
|
return veranstaltungRepository.update(id, veranstaltung)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an event by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteVeranstaltung(id: Uuid): Boolean {
|
||||||
|
return veranstaltungRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get events happening in a specific year
|
||||||
|
*/
|
||||||
|
suspend fun getVeranstaltungenByYear(year: Int): List<Veranstaltung> {
|
||||||
|
if (year < 1900 || year > 2100) {
|
||||||
|
throw IllegalArgumentException("Year must be between 1900 and 2100")
|
||||||
|
}
|
||||||
|
|
||||||
|
val allEvents = getAllVeranstaltungen()
|
||||||
|
return allEvents.filter { event ->
|
||||||
|
event.datumVon.year == year || event.datumBis.year == year ||
|
||||||
|
(event.datumVon.year < year && event.datumBis.year > year)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current events (happening now or in the future)
|
||||||
|
*/
|
||||||
|
suspend fun getCurrentAndFutureVeranstaltungen(): List<Veranstaltung> {
|
||||||
|
val currentJavaDate = java.time.LocalDate.now()
|
||||||
|
val currentLocalDate = kotlinx.datetime.LocalDate(currentJavaDate.year, currentJavaDate.monthValue, currentJavaDate.dayOfMonth)
|
||||||
|
|
||||||
|
val allEvents = getAllVeranstaltungen()
|
||||||
|
return allEvents.filter { event ->
|
||||||
|
event.datumBis >= currentLocalDate
|
||||||
|
}.sortedBy { it.datumVon }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get past events
|
||||||
|
*/
|
||||||
|
suspend fun getPastVeranstaltungen(): List<Veranstaltung> {
|
||||||
|
val currentJavaDate = java.time.LocalDate.now()
|
||||||
|
val currentLocalDate = kotlinx.datetime.LocalDate(currentJavaDate.year, currentJavaDate.monthValue, currentJavaDate.dayOfMonth)
|
||||||
|
|
||||||
|
val allEvents = getAllVeranstaltungen()
|
||||||
|
return allEvents.filter { event ->
|
||||||
|
event.datumBis < currentLocalDate
|
||||||
|
}.sortedByDescending { it.datumVon }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get events by organizer name
|
||||||
|
*/
|
||||||
|
suspend fun getVeranstaltungenByVeranstalterName(veranstalterName: String): List<Veranstaltung> {
|
||||||
|
if (veranstalterName.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Organizer name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
val allEvents = getAllVeranstaltungen()
|
||||||
|
return allEvents.filter { event ->
|
||||||
|
event.veranstalterName.contains(veranstalterName.trim(), ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get events by venue name
|
||||||
|
*/
|
||||||
|
suspend fun getVeranstaltungenByVenanstaltungsort(veranstaltungsortName: String): List<Veranstaltung> {
|
||||||
|
if (veranstaltungsortName.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Venue name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
val allEvents = getAllVeranstaltungen()
|
||||||
|
return allEvents.filter { event ->
|
||||||
|
event.veranstaltungsortName.contains(veranstaltungsortName.trim(), ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate event data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateVeranstaltung(veranstaltung: Veranstaltung) {
|
||||||
|
if (veranstaltung.name.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Event name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.name.length > 255) {
|
||||||
|
throw IllegalArgumentException("Event name cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.veranstalterName.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Organizer name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.veranstalterName.length > 255) {
|
||||||
|
throw IllegalArgumentException("Organizer name cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.veranstaltungsortName.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Venue name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.veranstaltungsortName.length > 255) {
|
||||||
|
throw IllegalArgumentException("Venue name cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.veranstaltungsortAdresse.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Venue address cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (veranstaltung.veranstaltungsortAdresse.length > 500) {
|
||||||
|
throw IllegalArgumentException("Venue address cannot exceed 500 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate date range
|
||||||
|
if (veranstaltung.datumVon > veranstaltung.datumBis) {
|
||||||
|
throw IllegalArgumentException("Event start date must be before or equal to end date")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate optional fields
|
||||||
|
veranstaltung.veranstalterOepsNummer?.let { oepsNr ->
|
||||||
|
if (oepsNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Organizer OEPS number cannot be blank if provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.kontaktpersonName?.let { name ->
|
||||||
|
if (name.length > 255) {
|
||||||
|
throw IllegalArgumentException("Contact person name cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.kontaktTelefon?.let { telefon ->
|
||||||
|
if (telefon.length > 50) {
|
||||||
|
throw IllegalArgumentException("Contact phone cannot exceed 50 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.kontaktEmail?.let { email ->
|
||||||
|
if (email.length > 255) {
|
||||||
|
throw IllegalArgumentException("Contact email cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
// Basic email validation
|
||||||
|
if (!email.contains("@") || !email.contains(".")) {
|
||||||
|
throw IllegalArgumentException("Contact email must be a valid email address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.webseite?.let { webseite ->
|
||||||
|
if (webseite.length > 500) {
|
||||||
|
throw IllegalArgumentException("Website URL cannot exceed 500 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.dsgvoText?.let { text ->
|
||||||
|
if (text.length > 2000) {
|
||||||
|
throw IllegalArgumentException("DSGVO text cannot exceed 2000 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.haftungsText?.let { text ->
|
||||||
|
if (text.length > 2000) {
|
||||||
|
throw IllegalArgumentException("Liability text cannot exceed 2000 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
veranstaltung.sonstigeBesondereBestimmungen?.let { text ->
|
||||||
|
if (text.length > 2000) {
|
||||||
|
throw IllegalArgumentException("Special provisions text cannot exceed 2000 characters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package at.mocode.services
|
||||||
|
|
||||||
|
import at.mocode.stammdaten.Verein
|
||||||
|
import at.mocode.repositories.VereinRepository
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service layer for Verein (Club) business logic.
|
||||||
|
* Handles business rules, validation, and coordinates with the repository layer.
|
||||||
|
*/
|
||||||
|
class VereinService(private val vereinRepository: VereinRepository) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all clubs
|
||||||
|
*/
|
||||||
|
suspend fun getAllVereine(): List<Verein> {
|
||||||
|
return vereinRepository.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a club by its unique identifier
|
||||||
|
*/
|
||||||
|
suspend fun getVereinById(id: Uuid): Verein? {
|
||||||
|
return vereinRepository.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a club by its OEPS (Austrian Equestrian Federation) number
|
||||||
|
*/
|
||||||
|
suspend fun getVereinByOepsNr(oepsVereinsNr: String): Verein? {
|
||||||
|
if (oepsVereinsNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS Vereins number cannot be blank")
|
||||||
|
}
|
||||||
|
return vereinRepository.findByOepsVereinsNr(oepsVereinsNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for clubs by query string
|
||||||
|
*/
|
||||||
|
suspend fun searchVereine(query: String): List<Verein> {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Search query cannot be blank")
|
||||||
|
}
|
||||||
|
return vereinRepository.search(query.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find clubs by federal state (Bundesland)
|
||||||
|
*/
|
||||||
|
suspend fun getVereineByBundesland(bundesland: String): List<Verein> {
|
||||||
|
if (bundesland.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Bundesland cannot be blank")
|
||||||
|
}
|
||||||
|
return vereinRepository.findByBundesland(bundesland)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new club with business validation
|
||||||
|
*/
|
||||||
|
suspend fun createVerein(verein: Verein): Verein {
|
||||||
|
validateVerein(verein)
|
||||||
|
|
||||||
|
// Check if OEPS number already exists
|
||||||
|
verein.oepsVereinsNr?.let { oepsNr ->
|
||||||
|
val existing = vereinRepository.findByOepsVereinsNr(oepsNr)
|
||||||
|
if (existing != null) {
|
||||||
|
throw IllegalArgumentException("A club with OEPS number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vereinRepository.create(verein)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing club
|
||||||
|
*/
|
||||||
|
suspend fun updateVerein(id: Uuid, verein: Verein): Verein? {
|
||||||
|
validateVerein(verein)
|
||||||
|
|
||||||
|
// Check if OEPS number conflicts with another club
|
||||||
|
verein.oepsVereinsNr?.let { oepsNr ->
|
||||||
|
val existing = vereinRepository.findByOepsVereinsNr(oepsNr)
|
||||||
|
if (existing != null && existing.id != id) {
|
||||||
|
throw IllegalArgumentException("A club with OEPS number '$oepsNr' already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vereinRepository.update(id, verein)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a club by ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteVerein(id: Uuid): Boolean {
|
||||||
|
return vereinRepository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate club data according to business rules
|
||||||
|
*/
|
||||||
|
private fun validateVerein(verein: Verein) {
|
||||||
|
if (verein.name.isBlank()) {
|
||||||
|
throw IllegalArgumentException("Club name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verein.name.length > 255) {
|
||||||
|
throw IllegalArgumentException("Club name cannot exceed 255 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation rules can be added here
|
||||||
|
verein.oepsVereinsNr?.let { oepsNr ->
|
||||||
|
if (oepsNr.isBlank()) {
|
||||||
|
throw IllegalArgumentException("OEPS Vereins number cannot be blank if provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package at.mocode
|
||||||
|
|
||||||
|
import at.mocode.dto.ArtikelDto
|
||||||
|
import at.mocode.dto.VereinDto
|
||||||
|
import at.mocode.dto.base.VersionManager
|
||||||
|
import at.mocode.dto.base.VersionValidationResult
|
||||||
|
import at.mocode.dto.migrations.ArtikelDtoMigrator
|
||||||
|
import com.benasher44.uuid.uuid4
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertIs
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class VersioningTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testVersionManagerValidation() {
|
||||||
|
// Test valid version
|
||||||
|
val validResult = VersionManager.validateClientVersion("1.0")
|
||||||
|
assertIs<VersionValidationResult.Valid>(validResult)
|
||||||
|
assertEquals("1.0", validResult.version)
|
||||||
|
|
||||||
|
// Test unsupported version
|
||||||
|
val unsupportedResult = VersionManager.validateClientVersion("2.0")
|
||||||
|
assertIs<VersionValidationResult.UnsupportedVersion>(unsupportedResult)
|
||||||
|
assertEquals("2.0", unsupportedResult.version)
|
||||||
|
|
||||||
|
// Test missing version
|
||||||
|
val missingResult = VersionManager.validateClientVersion(null)
|
||||||
|
assertIs<VersionValidationResult.MissingVersion>(missingResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testVersionManagerInfo() {
|
||||||
|
val versionInfo = VersionManager.getVersionInfo()
|
||||||
|
assertEquals("1.0", versionInfo.apiVersion)
|
||||||
|
assertTrue(versionInfo.supportedVersions.contains("1.0"))
|
||||||
|
assertEquals("1.0", versionInfo.minimumClientVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testArtikelDtoVersioning() {
|
||||||
|
val artikel = ArtikelDto(
|
||||||
|
id = uuid4(),
|
||||||
|
bezeichnung = "Test Artikel",
|
||||||
|
preis = BigDecimal.fromInt(100),
|
||||||
|
einheit = "Stück",
|
||||||
|
istVerbandsabgabe = false,
|
||||||
|
createdAt = Clock.System.now(),
|
||||||
|
updatedAt = Clock.System.now(),
|
||||||
|
schemaVersion = "1.0",
|
||||||
|
dataVersion = 1L
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals("1.0", artikel.schemaVersion)
|
||||||
|
assertEquals(1L, artikel.dataVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testVereinDtoVersioning() {
|
||||||
|
val verein = VereinDto(
|
||||||
|
id = uuid4(),
|
||||||
|
oepsVereinsNr = "12345",
|
||||||
|
name = "Test Verein",
|
||||||
|
kuerzel = "TV",
|
||||||
|
bundesland = "Wien",
|
||||||
|
adresse = "Teststraße 1",
|
||||||
|
plz = "1010",
|
||||||
|
ort = "Wien",
|
||||||
|
email = "test@verein.at",
|
||||||
|
telefon = "+43123456789",
|
||||||
|
webseite = "www.testverein.at",
|
||||||
|
istAktiv = true,
|
||||||
|
createdAt = Clock.System.now(),
|
||||||
|
updatedAt = Clock.System.now(),
|
||||||
|
schemaVersion = "1.0",
|
||||||
|
dataVersion = 1L
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals("1.0", verein.schemaVersion)
|
||||||
|
assertEquals(1L, verein.dataVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testArtikelDtoMigrator() {
|
||||||
|
val migrator = ArtikelDtoMigrator()
|
||||||
|
|
||||||
|
// Test migration capability
|
||||||
|
assertTrue(migrator.canMigrate("1.0", "1.0"))
|
||||||
|
|
||||||
|
val artikel = ArtikelDto(
|
||||||
|
id = uuid4(),
|
||||||
|
bezeichnung = "Test Artikel",
|
||||||
|
preis = BigDecimal.fromInt(100),
|
||||||
|
einheit = "Stück",
|
||||||
|
istVerbandsabgabe = false,
|
||||||
|
createdAt = Clock.System.now(),
|
||||||
|
updatedAt = Clock.System.now(),
|
||||||
|
schemaVersion = "1.0",
|
||||||
|
dataVersion = 1L
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test migration (same version should return same object)
|
||||||
|
val migratedArtikel = migrator.migrate(artikel, "1.0", "1.0")
|
||||||
|
assertEquals(artikel, migratedArtikel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testVersionSupport() {
|
||||||
|
assertTrue(VersionManager.isVersionSupported("1.0"))
|
||||||
|
assertTrue(!VersionManager.isVersionSupported("2.0"))
|
||||||
|
assertTrue(!VersionManager.isVersionDeprecated("1.0"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.enums.BeginnzeitTypE
|
||||||
|
import at.mocode.model.DotierungsAbstufung
|
||||||
|
import at.mocode.serializers.*
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.LocalTime
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AbteilungDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val bewerbId: Uuid,
|
||||||
|
val abteilungsKennzeichen: String,
|
||||||
|
val bezeichnungIntern: String?,
|
||||||
|
val bezeichnungAufStartliste: String?,
|
||||||
|
val teilungsKriteriumLizenz: String?,
|
||||||
|
val teilungsKriteriumPferdealter: String?,
|
||||||
|
val teilungsKriteriumAltersklasseReiter: String?,
|
||||||
|
val teilungsKriteriumAnzahlMin: Int?,
|
||||||
|
val teilungsKriteriumAnzahlMax: Int?,
|
||||||
|
val teilungsKriteriumFreiText: String?,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeld: BigDecimal?,
|
||||||
|
val dotierungen: List<DotierungsAbstufung>,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val platzId: Uuid?,
|
||||||
|
val datum: LocalDate?,
|
||||||
|
val beginnzeitTypE: BeginnzeitTypE,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val beginnzeitFix: LocalTime?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val beginnNachAbteilungId: Uuid?,
|
||||||
|
val beginnzeitCa: LocalTime?,
|
||||||
|
val dauerProStartGeschaetztSek: Int?,
|
||||||
|
val umbauzeitNachAbteilungMin: Int?,
|
||||||
|
val besichtigungszeitVorAbteilungMin: Int?,
|
||||||
|
val stechzeitZusaetzlichMin: Int?,
|
||||||
|
val anzahlStarter: Int,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateAbteilungDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val bewerbId: Uuid,
|
||||||
|
val abteilungsKennzeichen: String,
|
||||||
|
val bezeichnungIntern: String? = null,
|
||||||
|
val bezeichnungAufStartliste: String? = null,
|
||||||
|
val teilungsKriteriumLizenz: String? = null,
|
||||||
|
val teilungsKriteriumPferdealter: String? = null,
|
||||||
|
val teilungsKriteriumAltersklasseReiter: String? = null,
|
||||||
|
val teilungsKriteriumAnzahlMin: Int? = null,
|
||||||
|
val teilungsKriteriumAnzahlMax: Int? = null,
|
||||||
|
val teilungsKriteriumFreiText: String? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeld: BigDecimal? = null,
|
||||||
|
val dotierungen: List<DotierungsAbstufung> = emptyList(),
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val platzId: Uuid? = null,
|
||||||
|
val datum: LocalDate? = null,
|
||||||
|
val beginnzeitTypE: BeginnzeitTypE = BeginnzeitTypE.ANSCHLIESSEND,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val beginnzeitFix: LocalTime? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val beginnNachAbteilungId: Uuid? = null,
|
||||||
|
val beginnzeitCa: LocalTime? = null,
|
||||||
|
val dauerProStartGeschaetztSek: Int? = null,
|
||||||
|
val umbauzeitNachAbteilungMin: Int? = null,
|
||||||
|
val besichtigungszeitVorAbteilungMin: Int? = null,
|
||||||
|
val stechzeitZusaetzlichMin: Int? = null,
|
||||||
|
val anzahlStarter: Int = 0,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateAbteilungDto(
|
||||||
|
val abteilungsKennzeichen: String,
|
||||||
|
val bezeichnungIntern: String? = null,
|
||||||
|
val bezeichnungAufStartliste: String? = null,
|
||||||
|
val teilungsKriteriumLizenz: String? = null,
|
||||||
|
val teilungsKriteriumPferdealter: String? = null,
|
||||||
|
val teilungsKriteriumAltersklasseReiter: String? = null,
|
||||||
|
val teilungsKriteriumAnzahlMin: Int? = null,
|
||||||
|
val teilungsKriteriumAnzahlMax: Int? = null,
|
||||||
|
val teilungsKriteriumFreiText: String? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeld: BigDecimal? = null,
|
||||||
|
val dotierungen: List<DotierungsAbstufung> = emptyList(),
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val platzId: Uuid? = null,
|
||||||
|
val datum: LocalDate? = null,
|
||||||
|
val beginnzeitTypE: BeginnzeitTypE = BeginnzeitTypE.ANSCHLIESSEND,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val beginnzeitFix: LocalTime? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val beginnNachAbteilungId: Uuid? = null,
|
||||||
|
val beginnzeitCa: LocalTime? = null,
|
||||||
|
val dauerProStartGeschaetztSek: Int? = null,
|
||||||
|
val umbauzeitNachAbteilungMin: Int? = null,
|
||||||
|
val besichtigungszeitVorAbteilungMin: Int? = null,
|
||||||
|
val stechzeitZusaetzlichMin: Int? = null,
|
||||||
|
val anzahlStarter: Int = 0,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.dto.base.VersionedDto
|
||||||
|
import at.mocode.dto.base.Since
|
||||||
|
import at.mocode.serializers.BigDecimalSerializer
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class ArtikelDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val preis: BigDecimal,
|
||||||
|
val einheit: String,
|
||||||
|
val istVerbandsabgabe: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant,
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class CreateArtikelDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val preis: BigDecimal,
|
||||||
|
val einheit: String,
|
||||||
|
val istVerbandsabgabe: Boolean = false,
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class UpdateArtikelDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val preis: BigDecimal,
|
||||||
|
val einheit: String,
|
||||||
|
val istVerbandsabgabe: Boolean = false,
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.enums.BeginnzeitTypE
|
||||||
|
import at.mocode.enums.SparteE
|
||||||
|
import at.mocode.model.DotierungsAbstufung
|
||||||
|
import at.mocode.serializers.*
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.LocalTime
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BewerbDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierId: Uuid,
|
||||||
|
val nummer: String,
|
||||||
|
val bezeichnungOffiziell: String,
|
||||||
|
val internerName: String?,
|
||||||
|
val sparteE: SparteE,
|
||||||
|
val klasse: String?,
|
||||||
|
val kategorieOetoDesBewerbs: String?,
|
||||||
|
val teilnahmebedingungenText: String?,
|
||||||
|
val maxPferdeProReiter: Int?,
|
||||||
|
val pferdealterAnforderung: String?,
|
||||||
|
val zusatzTextZeile1: String?,
|
||||||
|
val zusatzTextZeile2: String?,
|
||||||
|
val zusatzTextZeile3: String?,
|
||||||
|
val logoBewerbUrl: String?,
|
||||||
|
val parcoursskizzeUrl: String?,
|
||||||
|
val pruefungsArtDetailName: String?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val pruefungsaufgabeId: Uuid?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val richtverfahrenId: Uuid?,
|
||||||
|
val anzahlRichterGeplant: Int?,
|
||||||
|
val paraGradeAnforderung: String?,
|
||||||
|
val istManuellKalkuliert: Boolean,
|
||||||
|
val istDotiert: Boolean,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldStandard: BigDecimal?,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldKaderreiter: BigDecimal?,
|
||||||
|
val auszahlungsModusGeldpreis: String?,
|
||||||
|
val hatGeldpreisFuerKaderreiter: Boolean,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val geldpreisVorlageId: Uuid?,
|
||||||
|
val dotierungenManuell: List<DotierungsAbstufung>,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val standardPlatzId: Uuid?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val standardDatum: LocalDate?,
|
||||||
|
val standardBeginnzeitTypE: BeginnzeitTypE,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val standardBeginnzeitFix: LocalTime?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val standardBeginnNachBewerbId: Uuid?,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val standardBeginnzeitCa: LocalTime?,
|
||||||
|
val standardDauerProStartGeschaetztSek: Int?,
|
||||||
|
val standardUmbauzeitNachBewerbMin: Int?,
|
||||||
|
val standardBesichtigungszeitVorBewerbMin: Int?,
|
||||||
|
val standardStechzeitZusaetzlichMin: Int?,
|
||||||
|
val oepsBewerbsartCodeZns: String?,
|
||||||
|
val oepsAltersklasseCodeZns: String?,
|
||||||
|
val oepsPferderassenCodeZns: String?,
|
||||||
|
val notizenIntern: String?,
|
||||||
|
val istStartlisteFinal: Boolean,
|
||||||
|
val istErgebnislisteFinal: Boolean,
|
||||||
|
val erfordertAbteilungsAuswahlFuerNennung: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateBewerbDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierId: Uuid,
|
||||||
|
val nummer: String,
|
||||||
|
val bezeichnungOffiziell: String,
|
||||||
|
val internerName: String? = null,
|
||||||
|
val sparteE: SparteE,
|
||||||
|
val klasse: String? = null,
|
||||||
|
val kategorieOetoDesBewerbs: String? = null,
|
||||||
|
val teilnahmebedingungenText: String? = null,
|
||||||
|
val maxPferdeProReiter: Int? = null,
|
||||||
|
val pferdealterAnforderung: String? = null,
|
||||||
|
val zusatzTextZeile1: String? = null,
|
||||||
|
val zusatzTextZeile2: String? = null,
|
||||||
|
val zusatzTextZeile3: String? = null,
|
||||||
|
val logoBewerbUrl: String? = null,
|
||||||
|
val parcoursskizzeUrl: String? = null,
|
||||||
|
val pruefungsArtDetailName: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val pruefungsaufgabeId: Uuid? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val richtverfahrenId: Uuid? = null,
|
||||||
|
val anzahlRichterGeplant: Int? = 1,
|
||||||
|
val paraGradeAnforderung: String? = null,
|
||||||
|
val istManuellKalkuliert: Boolean = false,
|
||||||
|
val istDotiert: Boolean = false,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldStandard: BigDecimal? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldKaderreiter: BigDecimal? = null,
|
||||||
|
val auszahlungsModusGeldpreis: String? = null,
|
||||||
|
val hatGeldpreisFuerKaderreiter: Boolean = false,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val geldpreisVorlageId: Uuid? = null,
|
||||||
|
val dotierungenManuell: List<DotierungsAbstufung> = emptyList(),
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val standardPlatzId: Uuid? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val standardDatum: LocalDate? = null,
|
||||||
|
val standardBeginnzeitTypE: BeginnzeitTypE = BeginnzeitTypE.ANSCHLIESSEND,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val standardBeginnzeitFix: LocalTime? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val standardBeginnNachBewerbId: Uuid? = null,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val standardBeginnzeitCa: LocalTime? = null,
|
||||||
|
val standardDauerProStartGeschaetztSek: Int? = 120,
|
||||||
|
val standardUmbauzeitNachBewerbMin: Int? = 10,
|
||||||
|
val standardBesichtigungszeitVorBewerbMin: Int? = 10,
|
||||||
|
val standardStechzeitZusaetzlichMin: Int? = 0,
|
||||||
|
val oepsBewerbsartCodeZns: String? = null,
|
||||||
|
val oepsAltersklasseCodeZns: String? = null,
|
||||||
|
val oepsPferderassenCodeZns: String? = null,
|
||||||
|
val notizenIntern: String? = null,
|
||||||
|
val istStartlisteFinal: Boolean = false,
|
||||||
|
val istErgebnislisteFinal: Boolean = false,
|
||||||
|
val erfordertAbteilungsAuswahlFuerNennung: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateBewerbDto(
|
||||||
|
val nummer: String,
|
||||||
|
val bezeichnungOffiziell: String,
|
||||||
|
val internerName: String? = null,
|
||||||
|
val sparteE: SparteE,
|
||||||
|
val klasse: String? = null,
|
||||||
|
val kategorieOetoDesBewerbs: String? = null,
|
||||||
|
val teilnahmebedingungenText: String? = null,
|
||||||
|
val maxPferdeProReiter: Int? = null,
|
||||||
|
val pferdealterAnforderung: String? = null,
|
||||||
|
val zusatzTextZeile1: String? = null,
|
||||||
|
val zusatzTextZeile2: String? = null,
|
||||||
|
val zusatzTextZeile3: String? = null,
|
||||||
|
val logoBewerbUrl: String? = null,
|
||||||
|
val parcoursskizzeUrl: String? = null,
|
||||||
|
val pruefungsArtDetailName: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val pruefungsaufgabeId: Uuid? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val richtverfahrenId: Uuid? = null,
|
||||||
|
val anzahlRichterGeplant: Int? = 1,
|
||||||
|
val paraGradeAnforderung: String? = null,
|
||||||
|
val istManuellKalkuliert: Boolean = false,
|
||||||
|
val istDotiert: Boolean = false,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldStandard: BigDecimal? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldKaderreiter: BigDecimal? = null,
|
||||||
|
val auszahlungsModusGeldpreis: String? = null,
|
||||||
|
val hatGeldpreisFuerKaderreiter: Boolean = false,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val geldpreisVorlageId: Uuid? = null,
|
||||||
|
val dotierungenManuell: List<DotierungsAbstufung> = emptyList(),
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val standardPlatzId: Uuid? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val standardDatum: LocalDate? = null,
|
||||||
|
val standardBeginnzeitTypE: BeginnzeitTypE = BeginnzeitTypE.ANSCHLIESSEND,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val standardBeginnzeitFix: LocalTime? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val standardBeginnNachBewerbId: Uuid? = null,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val standardBeginnzeitCa: LocalTime? = null,
|
||||||
|
val standardDauerProStartGeschaetztSek: Int? = 120,
|
||||||
|
val standardUmbauzeitNachBewerbMin: Int? = 10,
|
||||||
|
val standardBesichtigungszeitVorBewerbMin: Int? = 10,
|
||||||
|
val standardStechzeitZusaetzlichMin: Int? = 0,
|
||||||
|
val oepsBewerbsartCodeZns: String? = null,
|
||||||
|
val oepsAltersklasseCodeZns: String? = null,
|
||||||
|
val oepsPferderassenCodeZns: String? = null,
|
||||||
|
val notizenIntern: String? = null,
|
||||||
|
val istStartlisteFinal: Boolean = false,
|
||||||
|
val istErgebnislisteFinal: Boolean = false,
|
||||||
|
val erfordertAbteilungsAuswahlFuerNennung: Boolean = true
|
||||||
|
)
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.serializers.BigDecimalSerializer
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
// Pruefungsaufgabe DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PruefungsaufgabeDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val schwierigkeitsgrad: String?,
|
||||||
|
val punkteMax: Int?,
|
||||||
|
val zeitlimitSekunden: Int?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
val notizen: String?,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreatePruefungsaufgabeDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val kategorie: String? = null,
|
||||||
|
val schwierigkeitsgrad: String? = null,
|
||||||
|
val punkteMax: Int? = null,
|
||||||
|
val zeitlimitSekunden: Int? = null,
|
||||||
|
val istAktiv: Boolean = true,
|
||||||
|
val notizen: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdatePruefungsaufgabeDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val kategorie: String? = null,
|
||||||
|
val schwierigkeitsgrad: String? = null,
|
||||||
|
val punkteMax: Int? = null,
|
||||||
|
val zeitlimitSekunden: Int? = null,
|
||||||
|
val istAktiv: Boolean = true,
|
||||||
|
val notizen: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
// Richtverfahren DTOs
|
||||||
|
@Serializable
|
||||||
|
data class RichtverfahrenDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val anzahlRichterErforderlich: Int,
|
||||||
|
val bewertungsSchema: String?,
|
||||||
|
val istStandardVerfahren: Boolean,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
val regelwerk: String?,
|
||||||
|
val notizen: String?,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateRichtverfahrenDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val kategorie: String? = null,
|
||||||
|
val anzahlRichterErforderlich: Int = 1,
|
||||||
|
val bewertungsSchema: String? = null,
|
||||||
|
val istStandardVerfahren: Boolean = false,
|
||||||
|
val istAktiv: Boolean = true,
|
||||||
|
val regelwerk: String? = null,
|
||||||
|
val notizen: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateRichtverfahrenDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val kategorie: String? = null,
|
||||||
|
val anzahlRichterErforderlich: Int = 1,
|
||||||
|
val bewertungsSchema: String? = null,
|
||||||
|
val istStandardVerfahren: Boolean = false,
|
||||||
|
val istAktiv: Boolean = true,
|
||||||
|
val regelwerk: String? = null,
|
||||||
|
val notizen: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
// DotierungsAbstufung DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DotierungsAbstufungDto(
|
||||||
|
val platz: Int,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val betrag: BigDecimal
|
||||||
|
)
|
||||||
|
|
||||||
|
// MeisterschaftReferenz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class MeisterschaftReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meisterschaftId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateMeisterschaftReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meisterschaftId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateMeisterschaftReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meisterschaftId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
// CupReferenz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class CupReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val cupId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateCupReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val cupId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateCupReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val cupId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
// SonderpruefungReferenz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class SonderpruefungReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val sonderpruefungId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateSonderpruefungReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val sonderpruefungId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateSonderpruefungReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val sonderpruefungId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val kategorie: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
// Platz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PlatzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val gpsKoordinaten: String?,
|
||||||
|
val kapazitaet: Int?,
|
||||||
|
val ausstattung: List<String>,
|
||||||
|
val istVerfuegbar: Boolean,
|
||||||
|
val kontaktInfo: String?,
|
||||||
|
val notizen: String?,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreatePlatzDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val gpsKoordinaten: String? = null,
|
||||||
|
val kapazitaet: Int? = null,
|
||||||
|
val ausstattung: List<String> = emptyList(),
|
||||||
|
val istVerfuegbar: Boolean = true,
|
||||||
|
val kontaktInfo: String? = null,
|
||||||
|
val notizen: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdatePlatzDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val gpsKoordinaten: String? = null,
|
||||||
|
val kapazitaet: Int? = null,
|
||||||
|
val ausstattung: List<String> = emptyList(),
|
||||||
|
val istVerfuegbar: Boolean = true,
|
||||||
|
val kontaktInfo: String? = null,
|
||||||
|
val notizen: String? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.KotlinLocalDateSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
// DomLizenz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DomLizenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val lizenzTyp: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val sparte: String?,
|
||||||
|
val mindestalter: Int?,
|
||||||
|
val voraussetzungen: String?,
|
||||||
|
val gueltigkeitsdauerJahre: Int?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateDomLizenzDto(
|
||||||
|
val lizenzTyp: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val sparte: String? = null,
|
||||||
|
val mindestalter: Int? = null,
|
||||||
|
val voraussetzungen: String? = null,
|
||||||
|
val gueltigkeitsdauerJahre: Int? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateDomLizenzDto(
|
||||||
|
val lizenzTyp: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val sparte: String? = null,
|
||||||
|
val mindestalter: Int? = null,
|
||||||
|
val voraussetzungen: String? = null,
|
||||||
|
val gueltigkeitsdauerJahre: Int? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// DomPerson DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DomPersonDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate?,
|
||||||
|
val geschlecht: String?,
|
||||||
|
val nationalitaet: String?,
|
||||||
|
val email: String?,
|
||||||
|
val telefon: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val plz: String?,
|
||||||
|
val ort: String?,
|
||||||
|
val land: String?,
|
||||||
|
val feiId: String?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateDomPersonDto(
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geschlecht: String? = null,
|
||||||
|
val nationalitaet: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
val land: String? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateDomPersonDto(
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geschlecht: String? = null,
|
||||||
|
val nationalitaet: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
val land: String? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// DomPferd DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DomPferdDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val name: String,
|
||||||
|
val rasse: String?,
|
||||||
|
val farbe: String?,
|
||||||
|
val geschlecht: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate?,
|
||||||
|
val geburtsland: String?,
|
||||||
|
val vater: String?,
|
||||||
|
val mutter: String?,
|
||||||
|
val zuechter: String?,
|
||||||
|
val eigentuemer: String?,
|
||||||
|
val feiId: String?,
|
||||||
|
val lebensnummer: String?,
|
||||||
|
val chipNummer: String?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateDomPferdDto(
|
||||||
|
val name: String,
|
||||||
|
val rasse: String? = null,
|
||||||
|
val farbe: String? = null,
|
||||||
|
val geschlecht: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geburtsland: String? = null,
|
||||||
|
val vater: String? = null,
|
||||||
|
val mutter: String? = null,
|
||||||
|
val zuechter: String? = null,
|
||||||
|
val eigentuemer: String? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val lebensnummer: String? = null,
|
||||||
|
val chipNummer: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateDomPferdDto(
|
||||||
|
val name: String,
|
||||||
|
val rasse: String? = null,
|
||||||
|
val farbe: String? = null,
|
||||||
|
val geschlecht: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geburtsland: String? = null,
|
||||||
|
val vater: String? = null,
|
||||||
|
val mutter: String? = null,
|
||||||
|
val zuechter: String? = null,
|
||||||
|
val eigentuemer: String? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val lebensnummer: String? = null,
|
||||||
|
val chipNummer: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// DomQualifikation DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DomQualifikationDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val sparte: String?,
|
||||||
|
val voraussetzungen: String?,
|
||||||
|
val gueltigkeitsdauerJahre: Int?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateDomQualifikationDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val kategorie: String? = null,
|
||||||
|
val sparte: String? = null,
|
||||||
|
val voraussetzungen: String? = null,
|
||||||
|
val gueltigkeitsdauerJahre: Int? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateDomQualifikationDto(
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String? = null,
|
||||||
|
val kategorie: String? = null,
|
||||||
|
val sparte: String? = null,
|
||||||
|
val voraussetzungen: String? = null,
|
||||||
|
val gueltigkeitsdauerJahre: Int? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// DomVerein DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DomVereinDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val plz: String?,
|
||||||
|
val ort: String?,
|
||||||
|
val land: String?,
|
||||||
|
val email: String?,
|
||||||
|
val telefon: String?,
|
||||||
|
val webseite: String?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateDomVereinDto(
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
val land: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val webseite: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateDomVereinDto(
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
val land: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val webseite: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.KotlinLocalDateSerializer
|
||||||
|
import at.mocode.serializers.KotlinLocalTimeSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.LocalTime
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
// Veranstaltung subdirectory DTOs
|
||||||
|
|
||||||
|
// Pruefung_OEPS DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PruefungOepsDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val abteilungId: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val schwierigkeitsgrad: String?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val richterIds: List<Uuid>,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datum: LocalDate?,
|
||||||
|
@Serializable(with = KotlinLocalTimeSerializer::class)
|
||||||
|
val startzeit: LocalTime?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val platzId: Uuid?,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pruefung_Abteilung DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PruefungAbteilungDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val pruefungId: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val abteilungId: Uuid,
|
||||||
|
val reihenfolge: Int,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// VeranstaltungsRahmen DTOs
|
||||||
|
@Serializable
|
||||||
|
data class VeranstaltungsRahmenDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val regelwerk: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val gueltigVon: LocalDate?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val gueltigBis: LocalDate?,
|
||||||
|
val istStandard: Boolean,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val veranstalterId: Uuid?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val veranstaltungsortId: Uuid?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
// Turnier_hat_Platz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class TurnierHatPlatzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierId: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val platzId: Uuid,
|
||||||
|
val verwendungszweck: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
// OETO Verwaltung DTOs
|
||||||
|
|
||||||
|
// AltersklasseDefinition DTOs
|
||||||
|
@Serializable
|
||||||
|
data class AltersklasseDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val minAlter: Int?,
|
||||||
|
val maxAlter: Int?,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// LizenzTypGlobal DTOs
|
||||||
|
@Serializable
|
||||||
|
data class LizenzTypGlobalDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val sparte: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// QualifikationsTyp DTOs
|
||||||
|
@Serializable
|
||||||
|
data class QualifikationsTypDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val sparte: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sportfachliche_Stammdaten DTOs
|
||||||
|
@Serializable
|
||||||
|
data class SportfachlicheStammdatenDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val typ: String,
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val sortierung: Int?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
// OETORegelReferenz DTOs
|
||||||
|
@Serializable
|
||||||
|
data class OetoRegelReferenzDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val regelCode: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val kategorie: String?,
|
||||||
|
val version: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val gueltigVon: LocalDate?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val gueltigBis: LocalDate?,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZNS Staging DTOs
|
||||||
|
|
||||||
|
// Person_ZNS_Staging DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PersonZnsStagingDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val znsSatzNr: String,
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate?,
|
||||||
|
val geschlecht: String?,
|
||||||
|
val nationalitaet: String?,
|
||||||
|
val email: String?,
|
||||||
|
val telefon: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val plz: String?,
|
||||||
|
val ort: String?,
|
||||||
|
val vereinsname: String?,
|
||||||
|
val vereinsnummer: String?,
|
||||||
|
val feiId: String?,
|
||||||
|
val importDatum: Instant,
|
||||||
|
val istVerarbeitet: Boolean,
|
||||||
|
val verarbeitungsStatus: String?,
|
||||||
|
val fehlerMeldung: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pferd_ZNS_Staging DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PferdZnsStagingDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val znsPferdNr: String,
|
||||||
|
val name: String,
|
||||||
|
val rasse: String?,
|
||||||
|
val farbe: String?,
|
||||||
|
val geschlecht: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate?,
|
||||||
|
val geburtsland: String?,
|
||||||
|
val vater: String?,
|
||||||
|
val mutter: String?,
|
||||||
|
val zuechter: String?,
|
||||||
|
val eigentuemer: String?,
|
||||||
|
val feiId: String?,
|
||||||
|
val lebensnummer: String?,
|
||||||
|
val importDatum: Instant,
|
||||||
|
val istVerarbeitet: Boolean,
|
||||||
|
val verarbeitungsStatus: String?,
|
||||||
|
val fehlerMeldung: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verein_ZNS_Staging DTOs
|
||||||
|
@Serializable
|
||||||
|
data class VereinZnsStagingDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val znsVereinsNr: String,
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val plz: String?,
|
||||||
|
val ort: String?,
|
||||||
|
val bundesland: String?,
|
||||||
|
val email: String?,
|
||||||
|
val telefon: String?,
|
||||||
|
val webseite: String?,
|
||||||
|
val importDatum: Instant,
|
||||||
|
val istVerarbeitet: Boolean,
|
||||||
|
val verarbeitungsStatus: String?,
|
||||||
|
val fehlerMeldung: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cup DTOs
|
||||||
|
|
||||||
|
// Meisterschaft_Cup_Serie DTOs
|
||||||
|
@Serializable
|
||||||
|
data class MeisterschaftCupSerieDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val beschreibung: String?,
|
||||||
|
val saison: String,
|
||||||
|
val kategorie: String?,
|
||||||
|
val sparte: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val startDatum: LocalDate?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val endDatum: LocalDate?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
// MCS_Wertungspruefung DTOs
|
||||||
|
@Serializable
|
||||||
|
data class McsWertungspruefungDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meisterschaftCupSerieId: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val pruefungId: Uuid,
|
||||||
|
val wertungsfaktor: Double?,
|
||||||
|
val istPflichtpruefung: Boolean,
|
||||||
|
val reihenfolge: Int?
|
||||||
|
)
|
||||||
|
|
||||||
|
// Spezifika DTOs
|
||||||
|
|
||||||
|
// DressurPruefungSpezifika DTOs
|
||||||
|
@Serializable
|
||||||
|
data class DressurPruefungSpezifikaDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val pruefungId: Uuid,
|
||||||
|
val aufgabenbezeichnung: String?,
|
||||||
|
val aufgabenbeschreibung: String?,
|
||||||
|
val bewertungsschema: String?,
|
||||||
|
val maxPunkte: Int?,
|
||||||
|
val zeitlimit: Int?,
|
||||||
|
val besonderheiten: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.enums.FunktionaerRolle
|
||||||
|
import at.mocode.enums.GeschlechtE
|
||||||
|
import at.mocode.stammdaten.LizenzInfo
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.KotlinLocalDateSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
// Person DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PersonDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val oepsSatzNr: String?,
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate?,
|
||||||
|
val geschlechtE: GeschlechtE?,
|
||||||
|
val nationalitaet: String?,
|
||||||
|
val email: String?,
|
||||||
|
val telefon: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val plz: String?,
|
||||||
|
val ort: String?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val stammVereinId: Uuid?,
|
||||||
|
val mitgliedsNummerIntern: String?,
|
||||||
|
val letzteZahlungJahr: Int?,
|
||||||
|
val feiId: String?,
|
||||||
|
val istGesperrt: Boolean,
|
||||||
|
val sperrGrund: String?,
|
||||||
|
val rollen: Set<FunktionaerRolle>,
|
||||||
|
val lizenzen: List<LizenzInfo>,
|
||||||
|
val qualifikationenRichter: List<String>,
|
||||||
|
val qualifikationenParcoursbauer: List<String>,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreatePersonDto(
|
||||||
|
val oepsSatzNr: String? = null,
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geschlechtE: GeschlechtE? = null,
|
||||||
|
val nationalitaet: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val stammVereinId: Uuid? = null,
|
||||||
|
val mitgliedsNummerIntern: String? = null,
|
||||||
|
val letzteZahlungJahr: Int? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val istGesperrt: Boolean = false,
|
||||||
|
val sperrGrund: String? = null,
|
||||||
|
val rollen: Set<FunktionaerRolle> = emptySet(),
|
||||||
|
val lizenzen: List<LizenzInfo> = emptyList(),
|
||||||
|
val qualifikationenRichter: List<String> = emptyList(),
|
||||||
|
val qualifikationenParcoursbauer: List<String> = emptyList(),
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdatePersonDto(
|
||||||
|
val oepsSatzNr: String? = null,
|
||||||
|
val nachname: String,
|
||||||
|
val vorname: String,
|
||||||
|
val titel: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geschlechtE: GeschlechtE? = null,
|
||||||
|
val nationalitaet: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val stammVereinId: Uuid? = null,
|
||||||
|
val mitgliedsNummerIntern: String? = null,
|
||||||
|
val letzteZahlungJahr: Int? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val istGesperrt: Boolean = false,
|
||||||
|
val sperrGrund: String? = null,
|
||||||
|
val rollen: Set<FunktionaerRolle> = emptySet(),
|
||||||
|
val lizenzen: List<LizenzInfo> = emptyList(),
|
||||||
|
val qualifikationenRichter: List<String> = emptyList(),
|
||||||
|
val qualifikationenParcoursbauer: List<String> = emptyList(),
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pferd DTOs
|
||||||
|
@Serializable
|
||||||
|
data class PferdDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val oepsPferdNr: String?,
|
||||||
|
val name: String,
|
||||||
|
val rasse: String?,
|
||||||
|
val farbe: String?,
|
||||||
|
val geschlecht: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate?,
|
||||||
|
val geburtsland: String?,
|
||||||
|
val vater: String?,
|
||||||
|
val mutter: String?,
|
||||||
|
val zuechter: String?,
|
||||||
|
val eigentuemer: String?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val heimatVereinId: Uuid?,
|
||||||
|
val feiId: String?,
|
||||||
|
val lebensnummer: String?,
|
||||||
|
val chipNummer: String?,
|
||||||
|
val istGesperrt: Boolean,
|
||||||
|
val sperrGrund: String?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreatePferdDto(
|
||||||
|
val oepsPferdNr: String? = null,
|
||||||
|
val name: String,
|
||||||
|
val rasse: String? = null,
|
||||||
|
val farbe: String? = null,
|
||||||
|
val geschlecht: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geburtsland: String? = null,
|
||||||
|
val vater: String? = null,
|
||||||
|
val mutter: String? = null,
|
||||||
|
val zuechter: String? = null,
|
||||||
|
val eigentuemer: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val heimatVereinId: Uuid? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val lebensnummer: String? = null,
|
||||||
|
val chipNummer: String? = null,
|
||||||
|
val istGesperrt: Boolean = false,
|
||||||
|
val sperrGrund: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdatePferdDto(
|
||||||
|
val oepsPferdNr: String? = null,
|
||||||
|
val name: String,
|
||||||
|
val rasse: String? = null,
|
||||||
|
val farbe: String? = null,
|
||||||
|
val geschlecht: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val geburtsdatum: LocalDate? = null,
|
||||||
|
val geburtsland: String? = null,
|
||||||
|
val vater: String? = null,
|
||||||
|
val mutter: String? = null,
|
||||||
|
val zuechter: String? = null,
|
||||||
|
val eigentuemer: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val heimatVereinId: Uuid? = null,
|
||||||
|
val feiId: String? = null,
|
||||||
|
val lebensnummer: String? = null,
|
||||||
|
val chipNummer: String? = null,
|
||||||
|
val istGesperrt: Boolean = false,
|
||||||
|
val sperrGrund: String? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// LizenzInfo DTOs
|
||||||
|
@Serializable
|
||||||
|
data class LizenzInfoDto(
|
||||||
|
val lizenzTyp: String,
|
||||||
|
val gueltigVon: LocalDate?,
|
||||||
|
val gueltigBis: LocalDate?,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateLizenzInfoDto(
|
||||||
|
val lizenzTyp: String,
|
||||||
|
val gueltigVon: LocalDate? = null,
|
||||||
|
val gueltigBis: LocalDate? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateLizenzInfoDto(
|
||||||
|
val lizenzTyp: String,
|
||||||
|
val gueltigVon: LocalDate? = null,
|
||||||
|
val gueltigBis: LocalDate? = null,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// BundeslandDefinition DTOs
|
||||||
|
@Serializable
|
||||||
|
data class BundeslandDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val land: String,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateBundeslandDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val land: String,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateBundeslandDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val land: String,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// LandDefinition DTOs
|
||||||
|
@Serializable
|
||||||
|
data class LandDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val istEuMitglied: Boolean,
|
||||||
|
val istAktiv: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateLandDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val istEuMitglied: Boolean = false,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateLandDefinitionDto(
|
||||||
|
val code: String,
|
||||||
|
val bezeichnung: String,
|
||||||
|
val istEuMitglied: Boolean = false,
|
||||||
|
val istAktiv: Boolean = true
|
||||||
|
)
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.enums.NennungsArtE
|
||||||
|
import at.mocode.model.Artikel
|
||||||
|
import at.mocode.model.MeisterschaftReferenz
|
||||||
|
import at.mocode.model.Platz
|
||||||
|
import at.mocode.serializers.*
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import com.ionspin.kotlin.bignum.decimal.BigDecimal
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TurnierDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val veranstaltungId: Uuid,
|
||||||
|
val oepsTurnierNr: String,
|
||||||
|
val titel: String,
|
||||||
|
val untertitel: String?,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumVon: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumBis: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateTimeSerializer::class)
|
||||||
|
val nennungsschluss: LocalDateTime?,
|
||||||
|
val nennungsArt: List<NennungsArtE>,
|
||||||
|
val nennungsHinweis: String?,
|
||||||
|
val eigenesNennsystemUrl: String?,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val nenngeld: BigDecimal?,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldStandard: BigDecimal?,
|
||||||
|
val austragungsplaetze: List<Platz>,
|
||||||
|
val vorbereitungsplaetze: List<Platz>,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierleiterId: Uuid?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierbeauftragterId: Uuid?,
|
||||||
|
val richterIds: List<@Serializable(with = UuidSerializer::class) Uuid>,
|
||||||
|
val parcoursbauerIds: List<@Serializable(with = UuidSerializer::class) Uuid>,
|
||||||
|
val parcoursAssistentIds: List<@Serializable(with = UuidSerializer::class) Uuid>,
|
||||||
|
val tierarztInfos: String?,
|
||||||
|
val hufschmiedInfo: String?,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meldestelleVerantwortlicherId: Uuid?,
|
||||||
|
val meldestelleTelefon: String?,
|
||||||
|
val meldestelleOeffnungszeiten: String?,
|
||||||
|
val ergebnislistenUrl: String?,
|
||||||
|
val verfuegbareArtikel: List<Artikel>,
|
||||||
|
val meisterschaftRefs: List<MeisterschaftReferenz>,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateTurnierDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val veranstaltungId: Uuid,
|
||||||
|
val oepsTurnierNr: String,
|
||||||
|
val titel: String,
|
||||||
|
val untertitel: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumVon: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumBis: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateTimeSerializer::class)
|
||||||
|
val nennungsschluss: LocalDateTime? = null,
|
||||||
|
val nennungsArt: List<NennungsArtE> = emptyList(),
|
||||||
|
val nennungsHinweis: String? = null,
|
||||||
|
val eigenesNennsystemUrl: String? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val nenngeld: BigDecimal? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldStandard: BigDecimal? = null,
|
||||||
|
val austragungsplaetze: List<Platz> = emptyList(),
|
||||||
|
val vorbereitungsplaetze: List<Platz> = emptyList(),
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierleiterId: Uuid? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierbeauftragterId: Uuid? = null,
|
||||||
|
val richterIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||||
|
val parcoursbauerIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||||
|
val parcoursAssistentIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||||
|
val tierarztInfos: String? = null,
|
||||||
|
val hufschmiedInfo: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meldestelleVerantwortlicherId: Uuid? = null,
|
||||||
|
val meldestelleTelefon: String? = null,
|
||||||
|
val meldestelleOeffnungszeiten: String? = null,
|
||||||
|
val ergebnislistenUrl: String? = null,
|
||||||
|
val verfuegbareArtikel: List<Artikel> = emptyList(),
|
||||||
|
val meisterschaftRefs: List<MeisterschaftReferenz> = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateTurnierDto(
|
||||||
|
val oepsTurnierNr: String,
|
||||||
|
val titel: String,
|
||||||
|
val untertitel: String? = null,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumVon: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumBis: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateTimeSerializer::class)
|
||||||
|
val nennungsschluss: LocalDateTime? = null,
|
||||||
|
val nennungsArt: List<NennungsArtE> = emptyList(),
|
||||||
|
val nennungsHinweis: String? = null,
|
||||||
|
val eigenesNennsystemUrl: String? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val nenngeld: BigDecimal? = null,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val startgeldStandard: BigDecimal? = null,
|
||||||
|
val austragungsplaetze: List<Platz> = emptyList(),
|
||||||
|
val vorbereitungsplaetze: List<Platz> = emptyList(),
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierleiterId: Uuid? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val turnierbeauftragterId: Uuid? = null,
|
||||||
|
val richterIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||||
|
val parcoursbauerIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||||
|
val parcoursAssistentIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||||
|
val tierarztInfos: String? = null,
|
||||||
|
val hufschmiedInfo: String? = null,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val meldestelleVerantwortlicherId: Uuid? = null,
|
||||||
|
val meldestelleTelefon: String? = null,
|
||||||
|
val meldestelleOeffnungszeiten: String? = null,
|
||||||
|
val ergebnislistenUrl: String? = null,
|
||||||
|
val verfuegbareArtikel: List<Artikel> = emptyList(),
|
||||||
|
val meisterschaftRefs: List<MeisterschaftReferenz> = emptyList()
|
||||||
|
)
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.enums.VeranstalterTypE
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.KotlinLocalDateSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class VeranstaltungDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val name: String,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumVon: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumBis: LocalDate,
|
||||||
|
val veranstalterName: String,
|
||||||
|
val veranstalterOepsNummer: String?,
|
||||||
|
val veranstalterTypE: VeranstalterTypE,
|
||||||
|
val veranstaltungsortName: String,
|
||||||
|
val veranstaltungsortAdresse: String,
|
||||||
|
val kontaktpersonName: String?,
|
||||||
|
val kontaktTelefon: String?,
|
||||||
|
val kontaktEmail: String?,
|
||||||
|
val webseite: String?,
|
||||||
|
val logoUrl: String?,
|
||||||
|
val anfahrtsplanInfo: String?,
|
||||||
|
val sponsorInfos: List<String>,
|
||||||
|
val dsgvoText: String?,
|
||||||
|
val haftungsText: String?,
|
||||||
|
val sonstigeBesondereBestimmungen: String?,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateVeranstaltungDto(
|
||||||
|
val name: String,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumVon: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumBis: LocalDate,
|
||||||
|
val veranstalterName: String,
|
||||||
|
val veranstalterOepsNummer: String? = null,
|
||||||
|
val veranstalterTypE: VeranstalterTypE = VeranstalterTypE.UNBEKANNT,
|
||||||
|
val veranstaltungsortName: String,
|
||||||
|
val veranstaltungsortAdresse: String,
|
||||||
|
val kontaktpersonName: String? = null,
|
||||||
|
val kontaktTelefon: String? = null,
|
||||||
|
val kontaktEmail: String? = null,
|
||||||
|
val webseite: String? = null,
|
||||||
|
val logoUrl: String? = null,
|
||||||
|
val anfahrtsplanInfo: String? = null,
|
||||||
|
val sponsorInfos: List<String> = emptyList(),
|
||||||
|
val dsgvoText: String? = null,
|
||||||
|
val haftungsText: String? = null,
|
||||||
|
val sonstigeBesondereBestimmungen: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UpdateVeranstaltungDto(
|
||||||
|
val name: String,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumVon: LocalDate,
|
||||||
|
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||||
|
val datumBis: LocalDate,
|
||||||
|
val veranstalterName: String,
|
||||||
|
val veranstalterOepsNummer: String? = null,
|
||||||
|
val veranstalterTypE: VeranstalterTypE = VeranstalterTypE.UNBEKANNT,
|
||||||
|
val veranstaltungsortName: String,
|
||||||
|
val veranstaltungsortAdresse: String,
|
||||||
|
val kontaktpersonName: String? = null,
|
||||||
|
val kontaktTelefon: String? = null,
|
||||||
|
val kontaktEmail: String? = null,
|
||||||
|
val webseite: String? = null,
|
||||||
|
val logoUrl: String? = null,
|
||||||
|
val anfahrtsplanInfo: String? = null,
|
||||||
|
val sponsorInfos: List<String> = emptyList(),
|
||||||
|
val dsgvoText: String? = null,
|
||||||
|
val haftungsText: String? = null,
|
||||||
|
val sonstigeBesondereBestimmungen: String? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package at.mocode.dto
|
||||||
|
|
||||||
|
import at.mocode.dto.base.VersionedDto
|
||||||
|
import at.mocode.dto.base.Since
|
||||||
|
import at.mocode.serializers.KotlinInstantSerializer
|
||||||
|
import at.mocode.serializers.UuidSerializer
|
||||||
|
import com.benasher44.uuid.Uuid
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class VereinDto(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val id: Uuid,
|
||||||
|
val oepsVereinsNr: String,
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String?,
|
||||||
|
val bundesland: String?,
|
||||||
|
val adresse: String?,
|
||||||
|
val plz: String?,
|
||||||
|
val ort: String?,
|
||||||
|
val email: String?,
|
||||||
|
val telefon: String?,
|
||||||
|
val webseite: String?,
|
||||||
|
val istAktiv: Boolean,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val createdAt: Instant,
|
||||||
|
@Serializable(with = KotlinInstantSerializer::class)
|
||||||
|
val updatedAt: Instant,
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class CreateVereinDto(
|
||||||
|
val oepsVereinsNr: String,
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String? = null,
|
||||||
|
val bundesland: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val webseite: String? = null,
|
||||||
|
val istAktiv: Boolean = true,
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Since("1.0")
|
||||||
|
data class UpdateVereinDto(
|
||||||
|
val name: String,
|
||||||
|
val kuerzel: String? = null,
|
||||||
|
val bundesland: String? = null,
|
||||||
|
val adresse: String? = null,
|
||||||
|
val plz: String? = null,
|
||||||
|
val ort: String? = null,
|
||||||
|
val email: String? = null,
|
||||||
|
val telefon: String? = null,
|
||||||
|
val webseite: String? = null,
|
||||||
|
val istAktiv: Boolean = true,
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package at.mocode.dto.base
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages API and DTO versioning across the application.
|
||||||
|
*/
|
||||||
|
object VersionManager {
|
||||||
|
|
||||||
|
// Current API version
|
||||||
|
const val CURRENT_API_VERSION = "1.0"
|
||||||
|
|
||||||
|
// Supported API versions (newest first)
|
||||||
|
val SUPPORTED_VERSIONS = listOf("1.0")
|
||||||
|
|
||||||
|
// Deprecated versions (still supported but discouraged)
|
||||||
|
val DEPRECATED_VERSIONS = emptyList<String>()
|
||||||
|
|
||||||
|
// Minimum client version required
|
||||||
|
const val MINIMUM_CLIENT_VERSION = "1.0"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a version is supported
|
||||||
|
*/
|
||||||
|
fun isVersionSupported(version: String): Boolean {
|
||||||
|
return version in SUPPORTED_VERSIONS
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a version is deprecated
|
||||||
|
*/
|
||||||
|
fun isVersionDeprecated(version: String): Boolean {
|
||||||
|
return version in DEPRECATED_VERSIONS
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get version compatibility info
|
||||||
|
*/
|
||||||
|
fun getVersionInfo(): ApiVersionInfo {
|
||||||
|
return ApiVersionInfo(
|
||||||
|
apiVersion = CURRENT_API_VERSION,
|
||||||
|
supportedVersions = SUPPORTED_VERSIONS,
|
||||||
|
deprecatedVersions = DEPRECATED_VERSIONS,
|
||||||
|
minimumClientVersion = MINIMUM_CLIENT_VERSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate client version compatibility
|
||||||
|
*/
|
||||||
|
fun validateClientVersion(clientVersion: String?): VersionValidationResult {
|
||||||
|
if (clientVersion == null) {
|
||||||
|
return VersionValidationResult.MissingVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVersionSupported(clientVersion)) {
|
||||||
|
return VersionValidationResult.UnsupportedVersion(clientVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVersionDeprecated(clientVersion)) {
|
||||||
|
return VersionValidationResult.DeprecatedVersion(clientVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return VersionValidationResult.Valid(clientVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of version validation
|
||||||
|
*/
|
||||||
|
sealed class VersionValidationResult {
|
||||||
|
data class Valid(val version: String) : VersionValidationResult()
|
||||||
|
data class DeprecatedVersion(val version: String) : VersionValidationResult()
|
||||||
|
data class UnsupportedVersion(val version: String) : VersionValidationResult()
|
||||||
|
object MissingVersion : VersionValidationResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version migration interface for handling DTO evolution
|
||||||
|
*/
|
||||||
|
interface VersionMigrator<T : VersionedDto> {
|
||||||
|
/**
|
||||||
|
* Migrate DTO from one version to another
|
||||||
|
*/
|
||||||
|
fun migrate(dto: T, fromVersion: String, toVersion: String): T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if migration is supported between versions
|
||||||
|
*/
|
||||||
|
fun canMigrate(fromVersion: String, toVersion: String): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry for version migrators
|
||||||
|
*/
|
||||||
|
object MigratorRegistry {
|
||||||
|
private val migrators = mutableMapOf<String, VersionMigrator<*>>()
|
||||||
|
|
||||||
|
fun <T : VersionedDto> register(dtoClass: String, migrator: VersionMigrator<T>) {
|
||||||
|
migrators[dtoClass] = migrator
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : VersionedDto> getMigrator(dtoClass: String): VersionMigrator<T>? {
|
||||||
|
return migrators[dtoClass] as? VersionMigrator<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version compatibility annotations
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Since(val version: String)
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Deprecated(val version: String, val message: String = "")
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Until(val version: String)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package at.mocode.dto.base
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base interface for all versioned DTOs.
|
||||||
|
* Provides version information for API compatibility and evolution.
|
||||||
|
*/
|
||||||
|
interface VersionedDto {
|
||||||
|
/**
|
||||||
|
* The schema version of this DTO.
|
||||||
|
* Used for API versioning and backward compatibility.
|
||||||
|
*/
|
||||||
|
val schemaVersion: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional data version for optimistic locking.
|
||||||
|
* Can be used to detect concurrent modifications.
|
||||||
|
*/
|
||||||
|
val dataVersion: Long?
|
||||||
|
get() = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for versioned DTOs with common versioning fields.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
abstract class BaseVersionedDto(
|
||||||
|
override val schemaVersion: String = "1.0",
|
||||||
|
override val dataVersion: Long? = null
|
||||||
|
) : VersionedDto
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version information for API responses.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class ApiVersionInfo(
|
||||||
|
val apiVersion: String,
|
||||||
|
val supportedVersions: List<String>,
|
||||||
|
val deprecatedVersions: List<String> = emptyList(),
|
||||||
|
val minimumClientVersion: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for versioned API responses.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class VersionedResponse<T>(
|
||||||
|
val data: T,
|
||||||
|
val version: ApiVersionInfo,
|
||||||
|
val timestamp: String
|
||||||
|
) where T : VersionedDto
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package at.mocode.dto.migrations
|
||||||
|
|
||||||
|
import at.mocode.dto.ArtikelDto
|
||||||
|
import at.mocode.dto.base.VersionMigrator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrator for ArtikelDto versions.
|
||||||
|
* Handles migration between different versions of ArtikelDto.
|
||||||
|
*/
|
||||||
|
class ArtikelDtoMigrator : VersionMigrator<ArtikelDto> {
|
||||||
|
|
||||||
|
override fun migrate(dto: ArtikelDto, fromVersion: String, toVersion: String): ArtikelDto {
|
||||||
|
return when {
|
||||||
|
fromVersion == "1.0" && toVersion == "1.0" -> dto
|
||||||
|
// Future migrations would be handled here
|
||||||
|
// fromVersion == "1.0" && toVersion == "1.1" -> migrateFrom1_0To1_1(dto)
|
||||||
|
// fromVersion == "1.1" && toVersion == "1.2" -> migrateFrom1_1To1_2(dto)
|
||||||
|
else -> throw IllegalArgumentException("Unsupported migration from $fromVersion to $toVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canMigrate(fromVersion: String, toVersion: String): Boolean {
|
||||||
|
return when {
|
||||||
|
fromVersion == "1.0" && toVersion == "1.0" -> true
|
||||||
|
// Future migration paths would be defined here
|
||||||
|
// fromVersion == "1.0" && toVersion == "1.1" -> true
|
||||||
|
// fromVersion == "1.1" && toVersion == "1.2" -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example of future migration method
|
||||||
|
// private fun migrateFrom1_0To1_1(dto: ArtikelDto): ArtikelDto {
|
||||||
|
// return dto.copy(
|
||||||
|
// schemaVersion = "1.1",
|
||||||
|
// // Add new fields with default values
|
||||||
|
// // newField = "defaultValue"
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user