feat(entries+time-scheduling): add support for automatic breaks and inspection type configurations
- **Domain Enhancements:** - Introduced `PausenKonfiguration` and `BesichtigungsBlock` entities to handle automatic breaks and inspection scheduling. - Added `BesichtigungsTypE` enum for inspection types (`ZU_FUSS`, `ZU_PFERD`). - Updated `Bewerb` and `Abteilung` models to include pause and inspection type fields. - **Service Updates:** - Enhanced `StartlistenService` to calculate start times, accounting for breaks and inspection buffers. - Extended `BewerbService` to support patchable time scheduling via new `updateZeitplan` API. - **Persistence Changes:** - Updated tables (`BewerbTable`, `AbteilungTable`) to persist break configurations and inspection types. - Implemented repository mappings to include these new fields. - **Testing:** - Introduced `BewerbeZeitplanIntegrationTest` to validate new scheduling behaviors, including automatic pauses and inspection handling. - **Documentation:** - Added rulebook and conceptual documents for inspection and scheduling logic in `docs/01_Architecture/`.
This commit is contained in:
+4
@@ -3,6 +3,7 @@
|
||||
package at.mocode.entries.domain.model
|
||||
|
||||
import at.mocode.core.domain.model.AbteilungsTeilungsTypE
|
||||
import at.mocode.core.domain.model.BesichtigungsTypE
|
||||
import at.mocode.core.domain.serialization.InstantSerializer
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -55,6 +56,9 @@ data class Abteilung(
|
||||
// Zeitplanung
|
||||
var startzeit: String? = null,
|
||||
|
||||
/** Besichtigungstyp für diese Abteilung (optional, wenn abweichend von Standard). */
|
||||
var besichtigungsTyp: BesichtigungsTypE? = null,
|
||||
|
||||
// Verwaltung
|
||||
var bemerkungen: String? = null,
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.entries.domain.model
|
||||
|
||||
import at.mocode.core.domain.model.BesichtigungsTypE
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repräsentiert einen Zeitblock für die Parcoursbesichtigung.
|
||||
* Kann mit mehreren Abteilungen oder Bewerben verknüpft sein.
|
||||
*/
|
||||
@Serializable
|
||||
data class BesichtigungsBlock(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val besichtigungsBlockId: Uuid = Uuid.random(),
|
||||
|
||||
/** Typ der Besichtigung (zu Fuß / zu Pferd). */
|
||||
val typ: BesichtigungsTypE = BesichtigungsTypE.ZU_FUSS,
|
||||
|
||||
/** Geplante Dauer in Minuten. */
|
||||
val dauerMinuten: Int = 15,
|
||||
|
||||
/**
|
||||
* Liste der verknüpften Abteilungs-IDs.
|
||||
* Eine Besichtigung kann für mehrere Abteilungen gleichzeitig stattfinden.
|
||||
*/
|
||||
val abteilungIds: List<@Serializable(with = UuidSerializer::class) Uuid> = emptyList(),
|
||||
|
||||
/** Optionaler Puffer nach der Besichtigung bis zum ersten Start (Standard: 5 Min gemäß ÖTO). */
|
||||
val pufferMinuten: Int = 5
|
||||
)
|
||||
+19
@@ -97,6 +97,10 @@ data class Bewerb(
|
||||
var reitdauerMinuten: Int? = null,
|
||||
var umbauMinuten: Int? = null,
|
||||
var besichtigungMinuten: Int? = null,
|
||||
|
||||
/** Konfiguration für Pausen während der Prüfung. */
|
||||
var pausenKonfiguration: PausenKonfiguration? = null,
|
||||
|
||||
var stechenGeplant: Boolean = false,
|
||||
|
||||
// Finanzen
|
||||
@@ -297,3 +301,18 @@ data class Bewerb(
|
||||
*/
|
||||
fun withUpdatedTimestamp(): Bewerb = this.copy(updatedAt = Clock.System.now())
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für automatische Pausen nach einer bestimmten Anzahl von Startern.
|
||||
*/
|
||||
@Serializable
|
||||
data class PausenKonfiguration(
|
||||
/** Pause alle X Starter (0 = keine automatischen Pausen). */
|
||||
val starterIntervall: Int = 0,
|
||||
|
||||
/** Dauer der Pause in Minuten. */
|
||||
val dauerMinuten: Int = 10,
|
||||
|
||||
/** Optionale Bezeichnung (z.B. "Platzpflege"). */
|
||||
val bezeichnung: String? = null
|
||||
)
|
||||
|
||||
+12
-4
@@ -98,16 +98,24 @@ class StartlistenService {
|
||||
// Besichtigung vor dem ersten Starter
|
||||
aktuelleZeitInMinuten += besichtigung
|
||||
|
||||
startliste.eintraege.forEach { eintrag ->
|
||||
// Puffer nach Besichtigung (ÖTO)
|
||||
if (besichtigung > 0) {
|
||||
aktuelleZeitInMinuten += 5
|
||||
}
|
||||
|
||||
startliste.eintraege.forEachIndexed { index, eintrag ->
|
||||
// Pause nach Intervall berücksichtigen
|
||||
val pausenKonf = bewerb.pausenKonfiguration
|
||||
if (pausenKonf != null && pausenKonf.starterIntervall > 0 && index > 0 && index % pausenKonf.starterIntervall == 0) {
|
||||
aktuelleZeitInMinuten += pausenKonf.dauerMinuten
|
||||
}
|
||||
|
||||
val stunden = aktuelleZeitInMinuten / 60
|
||||
val minuten = aktuelleZeitInMinuten % 60
|
||||
zeiten[eintrag.startnummer] = LocalTime(stunden % 24, minuten)
|
||||
|
||||
// Zeit für den nächsten Starter berechnen
|
||||
aktuelleZeitInMinuten += reitdauer
|
||||
|
||||
// TODO: Umbauzeiten nach bestimmten Intervallen (z.B. alle 10 Starter)
|
||||
// oder bei Abteilungswechsel berücksichtigen.
|
||||
}
|
||||
|
||||
return zeiten
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ data class Abteilung(
|
||||
val nr: Int,
|
||||
val bezeichnung: String,
|
||||
val typ: String,
|
||||
val besichtigungsTyp: String? = null,
|
||||
)
|
||||
|
||||
interface AbteilungRepository {
|
||||
|
||||
+4
-1
@@ -22,7 +22,8 @@ class AbteilungRepositoryImpl : AbteilungRepository {
|
||||
bewerbId = row[AbteilungTable.bewerbId].toKotlinUuid(),
|
||||
nr = row[AbteilungTable.nr],
|
||||
bezeichnung = row[AbteilungTable.bezeichnung],
|
||||
typ = row[AbteilungTable.typ]
|
||||
typ = row[AbteilungTable.typ],
|
||||
besichtigungsTyp = row[AbteilungTable.besichtigungsTyp]
|
||||
)
|
||||
|
||||
override suspend fun create(a: Abteilung): Abteilung = tenantTransaction {
|
||||
@@ -33,6 +34,7 @@ class AbteilungRepositoryImpl : AbteilungRepository {
|
||||
s[AbteilungTable.nr] = a.nr
|
||||
s[AbteilungTable.bezeichnung] = a.bezeichnung
|
||||
s[AbteilungTable.typ] = a.typ
|
||||
s[AbteilungTable.besichtigungsTyp] = a.besichtigungsTyp
|
||||
s[AbteilungTable.createdAt] = now
|
||||
s[AbteilungTable.updatedAt] = now
|
||||
}
|
||||
@@ -53,6 +55,7 @@ class AbteilungRepositoryImpl : AbteilungRepository {
|
||||
s[AbteilungTable.nr] = a.nr
|
||||
s[AbteilungTable.bezeichnung] = a.bezeichnung
|
||||
s[AbteilungTable.typ] = a.typ
|
||||
s[AbteilungTable.besichtigungsTyp] = a.besichtigungsTyp
|
||||
s[AbteilungTable.updatedAt] = now
|
||||
}
|
||||
AbteilungTable.selectAll().where { AbteilungTable.id eq a.id.toJavaUuid() }.map(::rowToAbteilung).single()
|
||||
|
||||
+4
@@ -33,6 +33,10 @@ data class Bewerb(
|
||||
val umbauMinuten: Int? = null,
|
||||
val besichtigungMinuten: Int? = null,
|
||||
val stechenGeplant: Boolean = false,
|
||||
// Pausen
|
||||
val pausenStarterIntervall: Int? = null,
|
||||
val pausenDauerMinuten: Int? = null,
|
||||
val pausenBezeichnung: String? = null,
|
||||
// Finanzen
|
||||
val startgeldCent: Long? = null,
|
||||
val nenngeldCent: Long? = null,
|
||||
|
||||
+12
@@ -70,6 +70,10 @@ class BewerbRepositoryImpl : BewerbRepository {
|
||||
umbauMinuten = row[BewerbTable.umbauMinuten],
|
||||
besichtigungMinuten = row[BewerbTable.besichtigungMinuten],
|
||||
stechenGeplant = row[BewerbTable.stechenGeplant],
|
||||
// Pausen
|
||||
pausenStarterIntervall = row[BewerbTable.pausenStarterIntervall],
|
||||
pausenDauerMinuten = row[BewerbTable.pausenDauerMinuten],
|
||||
pausenBezeichnung = row[BewerbTable.pausenBezeichnung],
|
||||
// Finanzen
|
||||
startgeldCent = row[BewerbTable.startgeldCent],
|
||||
nenngeldCent = row[BewerbTable.nenngeldCent],
|
||||
@@ -106,6 +110,10 @@ class BewerbRepositoryImpl : BewerbRepository {
|
||||
s[BewerbTable.umbauMinuten] = b.umbauMinuten
|
||||
s[BewerbTable.besichtigungMinuten] = b.besichtigungMinuten
|
||||
s[BewerbTable.stechenGeplant] = b.stechenGeplant
|
||||
// Pausen
|
||||
s[BewerbTable.pausenStarterIntervall] = b.pausenStarterIntervall
|
||||
s[BewerbTable.pausenDauerMinuten] = b.pausenDauerMinuten
|
||||
s[BewerbTable.pausenBezeichnung] = b.pausenBezeichnung
|
||||
// Finanzen
|
||||
s[BewerbTable.startgeldCent] = b.startgeldCent
|
||||
s[BewerbTable.nenngeldCent] = b.nenngeldCent
|
||||
@@ -155,6 +163,10 @@ class BewerbRepositoryImpl : BewerbRepository {
|
||||
s[BewerbTable.umbauMinuten] = b.umbauMinuten
|
||||
s[BewerbTable.besichtigungMinuten] = b.besichtigungMinuten
|
||||
s[BewerbTable.stechenGeplant] = b.stechenGeplant
|
||||
// Pausen
|
||||
s[BewerbTable.pausenStarterIntervall] = b.pausenStarterIntervall
|
||||
s[BewerbTable.pausenDauerMinuten] = b.pausenDauerMinuten
|
||||
s[BewerbTable.pausenBezeichnung] = b.pausenBezeichnung
|
||||
// Finanzen
|
||||
s[BewerbTable.startgeldCent] = b.startgeldCent
|
||||
s[BewerbTable.nenngeldCent] = b.nenngeldCent
|
||||
|
||||
+27
@@ -6,6 +6,7 @@ import at.mocode.entries.domain.repository.NennungRepository
|
||||
import at.mocode.entries.service.errors.LockedException
|
||||
import at.mocode.entries.service.persistence.TurnierTable
|
||||
import at.mocode.entries.service.tenant.tenantTransaction
|
||||
import at.mocode.entries.domain.model.PausenKonfiguration
|
||||
import at.mocode.entries.domain.model.RichterEinsatz
|
||||
import at.mocode.entries.domain.service.CompetitionWarningService
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
@@ -13,6 +14,8 @@ import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import kotlin.uuid.Uuid
|
||||
import kotlin.uuid.toJavaUuid
|
||||
|
||||
typealias DomBewerb = at.mocode.entries.domain.model.Bewerb
|
||||
|
||||
class BewerbService(
|
||||
private val repo: BewerbRepository,
|
||||
private val nennungen: NennungRepository,
|
||||
@@ -53,6 +56,10 @@ class BewerbService(
|
||||
umbauMinuten = req.umbauMinuten,
|
||||
besichtigungMinuten = req.besichtigungMinuten,
|
||||
stechenGeplant = req.stechenGeplant,
|
||||
// Pausen
|
||||
pausenStarterIntervall = req.pausenStarterIntervall,
|
||||
pausenDauerMinuten = req.pausenDauerMinuten,
|
||||
pausenBezeichnung = req.pausenBezeichnung,
|
||||
// Finanzen
|
||||
startgeldCent = req.startgeldCent,
|
||||
geldpreisAusbezahlt = req.geldpreisAusbezahlt
|
||||
@@ -93,6 +100,11 @@ class BewerbService(
|
||||
umbauMinuten = req.umbauMinuten,
|
||||
besichtigungMinuten = req.besichtigungMinuten,
|
||||
stechenGeplant = req.stechenGeplant,
|
||||
// Pausen
|
||||
pausenStarterIntervall = req.pausenStarterIntervall,
|
||||
pausenDauerMinuten = req.pausenDauerMinuten,
|
||||
pausenBezeichnung = req.pausenBezeichnung,
|
||||
// Finanzen
|
||||
startgeldCent = req.startgeldCent,
|
||||
geldpreisAusbezahlt = req.geldpreisAusbezahlt,
|
||||
znsNummer = req.znsNummer,
|
||||
@@ -133,6 +145,10 @@ class BewerbService(
|
||||
umbauMinuten = req.umbauMinuten,
|
||||
besichtigungMinuten = req.besichtigungMinuten,
|
||||
stechenGeplant = req.stechenGeplant,
|
||||
// Pausen
|
||||
pausenStarterIntervall = req.pausenStarterIntervall,
|
||||
pausenDauerMinuten = req.pausenDauerMinuten,
|
||||
pausenBezeichnung = req.pausenBezeichnung,
|
||||
// Finanzen
|
||||
startgeldCent = req.startgeldCent,
|
||||
geldpreisAusbezahlt = req.geldpreisAusbezahlt,
|
||||
@@ -140,6 +156,17 @@ class BewerbService(
|
||||
return repo.update(updated)
|
||||
}
|
||||
|
||||
suspend fun updateZeitplan(id: Uuid, req: UpdateZeitplanRequest): Bewerb {
|
||||
val current = get(id)
|
||||
// Hier erlauben wir Änderungen auch wenn PUBLISHED (da Drag & Drop im Live-Betrieb nötig)
|
||||
val updated = current.copy(
|
||||
geplantesDatum = req.geplantesDatum,
|
||||
beginnZeit = req.beginnZeit,
|
||||
austragungsplatzId = req.austragungsplatzId?.let { Uuid.parse(it) }
|
||||
)
|
||||
return repo.update(updated)
|
||||
}
|
||||
|
||||
suspend fun delete(id: Uuid) {
|
||||
val current = get(id)
|
||||
if (isTurnierPublished(current.turnierId)) throw LockedException("Turnier ist PUBLISHED – Bewerbe können nicht gelöscht werden")
|
||||
|
||||
+32
@@ -47,6 +47,11 @@ data class CreateBewerbRequest(
|
||||
val besichtigungMinuten: Int? = null,
|
||||
val stechenGeplant: Boolean = false,
|
||||
|
||||
// Pausen
|
||||
val pausenStarterIntervall: Int? = null,
|
||||
val pausenDauerMinuten: Int? = null,
|
||||
val pausenBezeichnung: String? = null,
|
||||
|
||||
// Finanzen
|
||||
val startgeldCent: Long? = null,
|
||||
val geldpreisAusbezahlt: Boolean = false,
|
||||
@@ -84,6 +89,11 @@ data class UpdateBewerbRequest(
|
||||
val besichtigungMinuten: Int? = null,
|
||||
val stechenGeplant: Boolean = false,
|
||||
|
||||
// Pausen
|
||||
val pausenStarterIntervall: Int? = null,
|
||||
val pausenDauerMinuten: Int? = null,
|
||||
val pausenBezeichnung: String? = null,
|
||||
|
||||
// Finanzen
|
||||
val startgeldCent: Long? = null,
|
||||
val geldpreisAusbezahlt: Boolean = false,
|
||||
@@ -122,6 +132,11 @@ data class BewerbResponse(
|
||||
val besichtigungMinuten: Int?,
|
||||
val stechenGeplant: Boolean,
|
||||
|
||||
// Pausen
|
||||
val pausenStarterIntervall: Int?,
|
||||
val pausenDauerMinuten: Int?,
|
||||
val pausenBezeichnung: String?,
|
||||
|
||||
// Finanzen
|
||||
val startgeldCent: Long?,
|
||||
val geldpreisAusbezahlt: Boolean,
|
||||
@@ -129,9 +144,17 @@ data class BewerbResponse(
|
||||
// ZNS-Integration
|
||||
val znsNummer: Int?,
|
||||
val znsAbteilung: Int?,
|
||||
|
||||
val warnungen: List<AbteilungsWarnungDto> = emptyList(),
|
||||
)
|
||||
|
||||
/** Request für schnelles Zeitplan-Update (Drag & Drop). */
|
||||
data class UpdateZeitplanRequest(
|
||||
val geplantesDatum: LocalDate?,
|
||||
val beginnZeit: LocalTime?,
|
||||
val austragungsplatzId: String?
|
||||
)
|
||||
|
||||
data class AbteilungsWarnungDto(
|
||||
val code: AbteilungsWarnungCodeE,
|
||||
val nachricht: String,
|
||||
@@ -168,6 +191,9 @@ private fun domainToDto(b: Bewerb, warnungen: List<AbteilungsWarnung> = emptyLis
|
||||
geldpreisAusbezahlt = b.geldpreisAusbezahlt,
|
||||
znsNummer = b.znsNummer,
|
||||
znsAbteilung = b.znsAbteilung,
|
||||
pausenStarterIntervall = b.pausenStarterIntervall,
|
||||
pausenDauerMinuten = b.pausenDauerMinuten,
|
||||
pausenBezeichnung = b.pausenBezeichnung,
|
||||
warnungen = warnungen.map { AbteilungsWarnungDto(it.code, it.nachricht, it.oetoParagraph) }
|
||||
)
|
||||
|
||||
@@ -232,4 +258,10 @@ class BewerbeController(
|
||||
suspend fun delete(@PathVariable id: String) {
|
||||
service.delete(Uuid.parse(id))
|
||||
}
|
||||
|
||||
@PatchMapping("/bewerbe/{id}/zeitplan")
|
||||
suspend fun updateZeitplan(@PathVariable id: String, @RequestBody body: UpdateZeitplanRequest): BewerbResponse {
|
||||
val b = service.updateZeitplan(Uuid.parse(id), body)
|
||||
return domainToDto(b)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ object AbteilungTable : Table("abteilungen") {
|
||||
val nr = integer("nr")
|
||||
val bezeichnung = text("bezeichnung")
|
||||
val typ = varchar("typ", 32)
|
||||
val besichtigungsTyp = varchar("besichtigungs_typ", 20).nullable()
|
||||
val createdAt = timestamp("created_at")
|
||||
val updatedAt = timestamp("updated_at")
|
||||
|
||||
|
||||
+5
@@ -34,6 +34,11 @@ object BewerbTable : Table("bewerbe") {
|
||||
val besichtigungMinuten = integer("besichtigung_minuten").nullable()
|
||||
val stechenGeplant = bool("stechen_geplant").default(false)
|
||||
|
||||
// Pausen
|
||||
val pausenStarterIntervall = integer("pausen_starter_intervall").nullable()
|
||||
val pausenDauerMinuten = integer("pausen_dauer_minuten").nullable()
|
||||
val pausenBezeichnung = varchar("pausen_bezeichnung", 100).nullable()
|
||||
|
||||
// Finanzen
|
||||
val startgeldCent = long("startgeld_cent").nullable()
|
||||
val nenngeldCent = long("nenngeld_cent").nullable()
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.entries.service.bewerbe
|
||||
|
||||
import at.mocode.entries.service.persistence.*
|
||||
import at.mocode.entries.service.tenant.Tenant
|
||||
import at.mocode.entries.service.tenant.TenantContextHolder
|
||||
import at.mocode.entries.service.tenant.tenantTransaction
|
||||
import kotlin.time.Clock
|
||||
import kotlinx.datetime.LocalTime
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.deleteAll
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import kotlin.uuid.Uuid
|
||||
import kotlin.uuid.toJavaUuid
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
class BewerbeZeitplanIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var bewerbService: BewerbService
|
||||
|
||||
private val turnierId = Uuid.random()
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
TenantContextHolder.set(Tenant(turnierId.toString(), "PUBLIC", "jdbc:h2:mem:entries-test"))
|
||||
kotlinx.coroutines.runBlocking {
|
||||
tenantTransaction {
|
||||
SchemaUtils.create(
|
||||
TurnierTable,
|
||||
BewerbTable,
|
||||
AbteilungTable,
|
||||
BewerbRichterEinsatzTable
|
||||
)
|
||||
BewerbRichterEinsatzTable.deleteAll()
|
||||
BewerbTable.deleteAll()
|
||||
AbteilungTable.deleteAll()
|
||||
TurnierTable.deleteAll()
|
||||
|
||||
TurnierTable.insert {
|
||||
it[id] = turnierId.toJavaUuid()
|
||||
it[veranstaltungId] = Uuid.random().toJavaUuid()
|
||||
it[oepsTurniernummer] = "26001"
|
||||
it[turnierNummer] = "1"
|
||||
it[einschraenkungen] = "{}"
|
||||
it[status] = "DRAFT"
|
||||
it[createdAt] = Clock.System.now()
|
||||
it[updatedAt] = Clock.System.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun teardown() {
|
||||
TenantContextHolder.clear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bewerb mit pausen erstellen und abrufen`() = kotlinx.coroutines.runBlocking {
|
||||
// GIVEN
|
||||
val request = CreateBewerbRequest(
|
||||
klasse = "A",
|
||||
bezeichnung = "Springpferdeprüfung",
|
||||
pausenStarterIntervall = 20,
|
||||
pausenDauerMinuten = 15,
|
||||
pausenBezeichnung = "Platzpflege",
|
||||
besichtigungMinuten = 20
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val created = bewerbService.create(turnierId, request)
|
||||
|
||||
// THEN
|
||||
val fetched = bewerbService.get(created.id)
|
||||
assertEquals(20, fetched.pausenStarterIntervall)
|
||||
assertEquals(15, fetched.pausenDauerMinuten)
|
||||
assertEquals("Platzpflege", fetched.pausenBezeichnung)
|
||||
assertEquals(20, fetched.besichtigungMinuten)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `zeitplan update via patch`() = kotlinx.coroutines.runBlocking {
|
||||
// GIVEN
|
||||
val bewerb = bewerbService.create(turnierId, CreateBewerbRequest(
|
||||
klasse = "L",
|
||||
bezeichnung = "Standardspringprüfung"
|
||||
))
|
||||
val patchRequest = UpdateZeitplanRequest(
|
||||
geplantesDatum = null,
|
||||
beginnZeit = LocalTime(14, 30),
|
||||
austragungsplatzId = null
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val updated = bewerbService.updateZeitplan(bewerb.id, patchRequest)
|
||||
|
||||
// THEN
|
||||
assertEquals(LocalTime(14, 30), updated.beginnZeit)
|
||||
val fetched = bewerbService.get(bewerb.id)
|
||||
assertEquals(LocalTime(14, 30), fetched.beginnZeit)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user