feat(billing): introduce billing domain and service infrastructure
- **Billing Domain:** - Added Kotlin Multiplatform project with domain models (`TeilnehmerKonto`, `Buchung`, `BuchungsTyp`) to represent billing entities. - Defined serialization strategies using `InstantSerializer`. - **Service Implementation:** - Introduced `BillingServiceApplication` as the main entry point for the billing service. - Developed `TeilnehmerKontoService` for account management and transactions. - **Persistence Layer:** - Implemented Exposed repositories (`ExposedTeilnehmerKontoRepository`, `ExposedBillingRepositories`) for database interaction. - Added table definitions (`TeilnehmerKontoTable`, `BuchungTable`) with indexes for efficient querying. - **Build Configuration:** - Setup Gradle build files for billing domain and service modules with dependencies for Kotlin, Serialization, Spring Boot, and Exposed. - **Test Additions:** - Extended ZNS importer tests with new scenarios for qualification parsing
This commit is contained in:
parent
bab95d14f4
commit
21f3a57e6e
|
|
@ -306,4 +306,52 @@ class ZnsImportServiceTest {
|
||||||
assertThat(result.gesamtAktualisiert).isEqualTo(0)
|
assertThat(result.gesamtAktualisiert).isEqualTo(0)
|
||||||
assertThat(result.fehler).isEmpty()
|
assertThat(result.fehler).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `importiereZip - Funktionaer mit mehrfachen Qualifikationen`() = runTest {
|
||||||
|
// Zeile mit vielen Qualifikationen (Satznummer X014346)
|
||||||
|
val qualifikationen = "DM,DPF,GAR-SP,SPF,SS*,RD,RS"
|
||||||
|
val zeile = "X014346Schubert Renate $qualifikationen"
|
||||||
|
val zip = buildZip("RICHT01.DAT" to zeile)
|
||||||
|
|
||||||
|
coEvery { funktionaerRepository.findBySatz("X", 14346) } returns null
|
||||||
|
coEvery { funktionaerRepository.save(any()) } answers { firstArg<Funktionaer>() }
|
||||||
|
coEvery { reiterRepository.findByName(any(), any()) } returns emptyList()
|
||||||
|
|
||||||
|
val result = service.importiereZip(zip)
|
||||||
|
|
||||||
|
assertThat(result.richterImportiert).isEqualTo(1)
|
||||||
|
coVerify {
|
||||||
|
funktionaerRepository.save(match { f ->
|
||||||
|
f.qualifikationen.size == 7 &&
|
||||||
|
f.qualifikationen.containsAll(listOf("DM", "DPF", "GAR-SP", "SPF", "SS*", "RD", "RS"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `importiereZip - Funktionaer Update Strategie (Delete+Insert)`() = runTest {
|
||||||
|
val zeile = funktionaerZeile(typ = "X", satznummer = "123456", name = "Geaendert Name")
|
||||||
|
val zip = buildZip("RICHT01.DAT" to zeile)
|
||||||
|
|
||||||
|
val existing = Funktionaer(
|
||||||
|
funktionaerId = kotlin.uuid.Uuid.random(),
|
||||||
|
satzId = "X",
|
||||||
|
satzNummer = 123456,
|
||||||
|
name = "Alt Name"
|
||||||
|
)
|
||||||
|
|
||||||
|
coEvery { funktionaerRepository.findBySatz("X", 123456) } returns existing
|
||||||
|
coEvery { funktionaerRepository.save(any()) } answers { firstArg<Funktionaer>() }
|
||||||
|
coEvery { reiterRepository.findByName(any(), any()) } returns emptyList()
|
||||||
|
|
||||||
|
val result = service.importiereZip(zip)
|
||||||
|
|
||||||
|
assertThat(result.richterAktualisiert).isEqualTo(1)
|
||||||
|
coVerify {
|
||||||
|
funktionaerRepository.save(match { f ->
|
||||||
|
f.funktionaerId == existing.funktionaerId && f.name == "Geaendert Name"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
backend/services/billing/billing-domain/build.gradle.kts
Normal file
26
backend/services/billing/billing-domain/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
|
alias(libs.plugins.kotlinSerialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
js(IR) {
|
||||||
|
browser()
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.core.coreDomain)
|
||||||
|
implementation(projects.core.coreUtils)
|
||||||
|
implementation(libs.kotlinx.datetime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package at.mocode.billing.domain.model
|
||||||
|
|
||||||
|
import at.mocode.core.domain.serialization.InstantSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.Instant
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repräsentiert das Kassa-Konto eines Teilnehmers (Reiter oder Besitzer).
|
||||||
|
* Ein Konto wird pro Veranstaltung/Turnier geführt, kann aber veranstaltungsübergreifend aggregiert werden.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class TeilnehmerKonto constructor(
|
||||||
|
val kontoId: Uuid = Uuid.random(),
|
||||||
|
val veranstaltungId: Uuid,
|
||||||
|
val personId: Uuid, // Referenz auf Reiter oder Besitzer
|
||||||
|
val personName: String,
|
||||||
|
val saldoCent: Long = 0L, // Aktueller Kontostand in Cent
|
||||||
|
val bemerkungen: String? = null,
|
||||||
|
@Serializable(with = InstantSerializer::class)
|
||||||
|
val updatedAt: Instant = Clock.System.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ein einzelner Buchungsvorgang (Zahlung, Gutschrift, Gebühr).
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Buchung constructor(
|
||||||
|
val buchungId: Uuid = Uuid.random(),
|
||||||
|
val kontoId: Uuid,
|
||||||
|
val betragCent: Long, // Positiv für Gutschrift/Zahlung, Negativ für Gebühr/Soll
|
||||||
|
val typ: BuchungsTyp,
|
||||||
|
val verwendungszweck: String,
|
||||||
|
@Serializable(with = InstantSerializer::class)
|
||||||
|
val gebuchtAm: Instant = Clock.System.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class BuchungsTyp {
|
||||||
|
NENNGEBUEHR,
|
||||||
|
STARTGEBUEHR,
|
||||||
|
BOXENGEBUEHR,
|
||||||
|
ZAHLUNG_BAR,
|
||||||
|
ZAHLUNG_KARTE,
|
||||||
|
GUTSCHRIFT,
|
||||||
|
STORNIERUNG
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package at.mocode.billing.domain.repository
|
||||||
|
|
||||||
|
import at.mocode.billing.domain.model.Buchung
|
||||||
|
import at.mocode.billing.domain.model.TeilnehmerKonto
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository für den Zugriff auf Teilnehmer-Konten.
|
||||||
|
*/
|
||||||
|
interface TeilnehmerKontoRepository {
|
||||||
|
fun findByVeranstaltungAndPerson(veranstaltungId: Uuid, personId: Uuid): TeilnehmerKonto?
|
||||||
|
fun findById(kontoId: Uuid): TeilnehmerKonto?
|
||||||
|
fun save(konto: TeilnehmerKonto): TeilnehmerKonto
|
||||||
|
fun updateSaldo(kontoId: Uuid, saldoCent: Long): Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository für den Zugriff auf Buchungen.
|
||||||
|
*/
|
||||||
|
interface BuchungRepository {
|
||||||
|
fun findByKonto(kontoId: Uuid): List<Buchung>
|
||||||
|
fun save(buchung: Buchung): Buchung
|
||||||
|
}
|
||||||
40
backend/services/billing/billing-service/build.gradle.kts
Normal file
40
backend/services/billing/billing-service/build.gradle.kts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlinJvm)
|
||||||
|
alias(libs.plugins.spring.boot)
|
||||||
|
alias(libs.plugins.spring.dependencyManagement)
|
||||||
|
alias(libs.plugins.kotlinSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
springBoot {
|
||||||
|
mainClass.set("at.mocode.billing.service.BillingServiceApplicationKt")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Interne Module
|
||||||
|
implementation(projects.platform.platformDependencies)
|
||||||
|
implementation(projects.core.coreUtils)
|
||||||
|
implementation(projects.backend.services.billing.billingDomain)
|
||||||
|
|
||||||
|
// Spring Boot Starters
|
||||||
|
implementation(libs.spring.boot.starter.web)
|
||||||
|
implementation(libs.spring.boot.starter.validation)
|
||||||
|
implementation(libs.spring.boot.starter.actuator)
|
||||||
|
implementation(libs.jackson.module.kotlin)
|
||||||
|
|
||||||
|
// Datenbank-Abhängigkeiten
|
||||||
|
implementation(libs.exposed.core)
|
||||||
|
implementation(libs.exposed.dao)
|
||||||
|
implementation(libs.exposed.jdbc)
|
||||||
|
implementation(libs.exposed.kotlin.datetime)
|
||||||
|
implementation(libs.hikari.cp)
|
||||||
|
runtimeOnly(libs.postgresql.driver)
|
||||||
|
testRuntimeOnly(libs.h2.driver)
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
testImplementation(projects.platform.platformTesting)
|
||||||
|
testImplementation(libs.spring.boot.starter.test)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package at.mocode.billing.service
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.runApplication
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
class BillingServiceApplication
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
runApplication<BillingServiceApplication>(*args)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package at.mocode.billing.service
|
||||||
|
|
||||||
|
import at.mocode.billing.domain.model.Buchung
|
||||||
|
import at.mocode.billing.domain.model.BuchungsTyp
|
||||||
|
import at.mocode.billing.domain.model.TeilnehmerKonto
|
||||||
|
import at.mocode.billing.domain.repository.BuchungRepository
|
||||||
|
import at.mocode.billing.domain.repository.TeilnehmerKontoRepository
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class TeilnehmerKontoService(
|
||||||
|
private val kontoRepository: TeilnehmerKontoRepository,
|
||||||
|
private val buchungRepository: BuchungRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun getOrCreateKonto(veranstaltungId: Uuid, personId: Uuid, personName: String): TeilnehmerKonto {
|
||||||
|
return transaction {
|
||||||
|
kontoRepository.findByVeranstaltungAndPerson(veranstaltungId, personId)
|
||||||
|
?: kontoRepository.save(
|
||||||
|
TeilnehmerKonto(
|
||||||
|
veranstaltungId = veranstaltungId,
|
||||||
|
personId = personId,
|
||||||
|
personName = personName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bucheBetrag(kontoId: Uuid, betragCent: Long, typ: BuchungsTyp, verwendungszweck: String): Buchung {
|
||||||
|
return transaction {
|
||||||
|
val konto = kontoRepository.findById(kontoId) ?: throw IllegalArgumentException("Konto nicht gefunden: $kontoId")
|
||||||
|
|
||||||
|
val buchung = Buchung(
|
||||||
|
kontoId = kontoId,
|
||||||
|
betragCent = betragCent,
|
||||||
|
typ = typ,
|
||||||
|
verwendungszweck = verwendungszweck
|
||||||
|
)
|
||||||
|
|
||||||
|
val neueBuchung = buchungRepository.save(buchung)
|
||||||
|
kontoRepository.updateSaldo(kontoId, konto.saldoCent + betragCent)
|
||||||
|
|
||||||
|
neueBuchung
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBuchungen(kontoId: Uuid): List<Buchung> {
|
||||||
|
return buchungRepository.findByKonto(kontoId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKonto(kontoId: Uuid): TeilnehmerKonto? {
|
||||||
|
return kontoRepository.findById(kontoId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package at.mocode.billing.service.persistence
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.v1.core.Table
|
||||||
|
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
|
||||||
|
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposed-Tabellendefinition für das Teilnehmer-Konto.
|
||||||
|
*/
|
||||||
|
object TeilnehmerKontoTable : Table("teilnehmer_konten") {
|
||||||
|
val id = uuid("konto_id")
|
||||||
|
val veranstaltungId = uuid("veranstaltung_id")
|
||||||
|
val personId = uuid("person_id")
|
||||||
|
val personName = varchar("person_name", 200)
|
||||||
|
val saldoCent = long("saldo_cent").default(0L)
|
||||||
|
val bemerkungen = text("bemerkungen").nullable()
|
||||||
|
|
||||||
|
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)
|
||||||
|
val updatedAt = timestamp("updated_at").defaultExpression(CurrentTimestamp)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
|
||||||
|
init {
|
||||||
|
index("idx_konto_veranstaltung_person", isUnique = true, veranstaltungId, personId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposed-Tabellendefinition für Buchungen.
|
||||||
|
*/
|
||||||
|
object BuchungTable : Table("buchungen") {
|
||||||
|
val id = uuid("buchung_id")
|
||||||
|
val kontoId = uuid("konto_id")
|
||||||
|
val betragCent = long("betrag_cent")
|
||||||
|
val typ = varchar("typ", 50)
|
||||||
|
val verwendungszweck = varchar("verwendungszweck", 500)
|
||||||
|
val gebuchtAm = timestamp("gebucht_am").defaultExpression(CurrentTimestamp)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
|
||||||
|
init {
|
||||||
|
index("idx_buchung_konto", isUnique = false, kontoId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package at.mocode.billing.service.persistence
|
||||||
|
|
||||||
|
import at.mocode.billing.domain.model.Buchung
|
||||||
|
import at.mocode.billing.domain.model.BuchungsTyp
|
||||||
|
import at.mocode.billing.domain.model.TeilnehmerKonto
|
||||||
|
import at.mocode.billing.domain.repository.BuchungRepository
|
||||||
|
import at.mocode.billing.domain.repository.TeilnehmerKontoRepository
|
||||||
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.datetime.CurrentTimestamp
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class ExposedTeilnehmerKontoRepository : TeilnehmerKontoRepository {
|
||||||
|
|
||||||
|
override fun findByVeranstaltungAndPerson(veranstaltungId: Uuid, personId: Uuid): TeilnehmerKonto? {
|
||||||
|
return TeilnehmerKontoTable
|
||||||
|
.selectAll()
|
||||||
|
.where { (TeilnehmerKontoTable.veranstaltungId eq veranstaltungId) and (TeilnehmerKontoTable.personId eq personId) }
|
||||||
|
.singleOrNull()
|
||||||
|
?.toModel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findById(kontoId: Uuid): TeilnehmerKonto? {
|
||||||
|
return TeilnehmerKontoTable
|
||||||
|
.selectAll()
|
||||||
|
.where { TeilnehmerKontoTable.id eq kontoId }
|
||||||
|
.singleOrNull()
|
||||||
|
?.toModel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save(konto: TeilnehmerKonto): TeilnehmerKonto {
|
||||||
|
val existing = findById(konto.kontoId)
|
||||||
|
if (existing == null) {
|
||||||
|
TeilnehmerKontoTable.insert {
|
||||||
|
it[id] = konto.kontoId
|
||||||
|
it[veranstaltungId] = konto.veranstaltungId
|
||||||
|
it[personId] = konto.personId
|
||||||
|
it[personName] = konto.personName
|
||||||
|
it[saldoCent] = konto.saldoCent
|
||||||
|
it[bemerkungen] = konto.bemerkungen
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TeilnehmerKontoTable.update({ TeilnehmerKontoTable.id eq konto.kontoId }) {
|
||||||
|
it[personName] = konto.personName
|
||||||
|
it[saldoCent] = konto.saldoCent
|
||||||
|
it[bemerkungen] = konto.bemerkungen
|
||||||
|
it[updatedAt] = CurrentTimestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return findById(konto.kontoId)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateSaldo(kontoId: Uuid, saldoCent: Long): Long {
|
||||||
|
TeilnehmerKontoTable.update({ TeilnehmerKontoTable.id eq kontoId }) {
|
||||||
|
it[this.saldoCent] = saldoCent
|
||||||
|
it[updatedAt] = CurrentTimestamp
|
||||||
|
}
|
||||||
|
return saldoCent
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultRow.toModel() = TeilnehmerKonto(
|
||||||
|
kontoId = this[TeilnehmerKontoTable.id],
|
||||||
|
veranstaltungId = this[TeilnehmerKontoTable.veranstaltungId],
|
||||||
|
personId = this[TeilnehmerKontoTable.personId],
|
||||||
|
personName = this[TeilnehmerKontoTable.personName],
|
||||||
|
saldoCent = this[TeilnehmerKontoTable.saldoCent],
|
||||||
|
bemerkungen = this[TeilnehmerKontoTable.bemerkungen],
|
||||||
|
updatedAt = this[TeilnehmerKontoTable.updatedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class ExposedBuchungRepository : BuchungRepository {
|
||||||
|
|
||||||
|
override fun findByKonto(kontoId: Uuid): List<Buchung> {
|
||||||
|
return BuchungTable
|
||||||
|
.selectAll()
|
||||||
|
.where { BuchungTable.kontoId eq kontoId }
|
||||||
|
.map { it.toModel() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save(buchung: Buchung): Buchung {
|
||||||
|
BuchungTable.insert {
|
||||||
|
it[id] = buchung.buchungId
|
||||||
|
it[kontoId] = buchung.kontoId
|
||||||
|
it[betragCent] = buchung.betragCent
|
||||||
|
it[typ] = buchung.typ.name
|
||||||
|
it[verwendungszweck] = buchung.verwendungszweck
|
||||||
|
it[gebuchtAm] = buchung.gebuchtAm
|
||||||
|
}
|
||||||
|
return buchung
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultRow.toModel() = Buchung(
|
||||||
|
buchungId = this[BuchungTable.id],
|
||||||
|
kontoId = this[BuchungTable.kontoId],
|
||||||
|
betragCent = this[BuchungTable.betragCent],
|
||||||
|
typ = BuchungsTyp.valueOf(this[BuchungTable.typ]),
|
||||||
|
verwendungszweck = this[BuchungTable.verwendungszweck],
|
||||||
|
gebuchtAm = this[BuchungTable.gebuchtAm]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -8,15 +8,20 @@ servers:
|
||||||
- url: http://localhost:8091
|
- url: http://localhost:8091
|
||||||
description: Lokaler Entwicklungs-Server
|
description: Lokaler Entwicklungs-Server
|
||||||
paths:
|
paths:
|
||||||
/reiter/search:
|
/reiter:
|
||||||
get:
|
get:
|
||||||
summary: Sucht Reiter
|
summary: Alle Reiter abrufen (paginiert)
|
||||||
parameters:
|
parameters:
|
||||||
- name: q
|
- name: limit
|
||||||
in: query
|
in: query
|
||||||
required: true
|
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: integer
|
||||||
|
default: 100
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Liste von Reitern
|
description: Liste von Reitern
|
||||||
|
|
@ -26,6 +31,450 @@ paths:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Reiter'
|
$ref: '#/components/schemas/Reiter'
|
||||||
|
post:
|
||||||
|
summary: Neuen Reiter erstellen
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ReiterCreateRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Reiter erstellt
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Reiter'
|
||||||
|
/reiter/{id}:
|
||||||
|
get:
|
||||||
|
summary: Reiter nach ID abrufen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Reiter Details
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Reiter'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
put:
|
||||||
|
summary: Reiter aktualisieren
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ReiterUpdateRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Reiter aktualisiert
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Reiter'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
delete:
|
||||||
|
summary: Reiter löschen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Erfolgreich gelöscht
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
/reiter/search:
|
||||||
|
get:
|
||||||
|
summary: Sucht Reiter nach Satznummer
|
||||||
|
parameters:
|
||||||
|
- name: q
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Liste von Reitern (Satznummer Match)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Reiter'
|
||||||
|
/reiter/satznummer/{nr}:
|
||||||
|
get:
|
||||||
|
summary: Reiter nach Satznummer suchen
|
||||||
|
parameters:
|
||||||
|
- name: nr
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Reiter gefunden
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Reiter'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
/horse:
|
||||||
|
get:
|
||||||
|
summary: Alle Pferde abrufen (paginiert)
|
||||||
|
parameters:
|
||||||
|
- name: jahrgang
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 100
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Liste von Pferden
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Horse'
|
||||||
|
post:
|
||||||
|
summary: Neues Pferd erstellen
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HorseCreateRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Pferd erstellt
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Horse'
|
||||||
|
/horse/{id}:
|
||||||
|
get:
|
||||||
|
summary: Pferd nach ID abrufen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Pferd Details
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Horse'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
put:
|
||||||
|
summary: Pferd aktualisieren
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HorseUpdateRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Pferd aktualisiert
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Horse'
|
||||||
|
delete:
|
||||||
|
summary: Pferd löschen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Gelöscht
|
||||||
|
/horse/search:
|
||||||
|
get:
|
||||||
|
summary: Sucht Pferde nach Lebensnummer
|
||||||
|
parameters:
|
||||||
|
- name: q
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Liste von Pferden
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Horse'
|
||||||
|
/verein:
|
||||||
|
get:
|
||||||
|
summary: Alle Vereine abrufen (paginiert)
|
||||||
|
parameters:
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 100
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Liste von Vereinen
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Verein'
|
||||||
|
post:
|
||||||
|
summary: Neuen Verein erstellen
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VereinCreateRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Verein erstellt
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Verein'
|
||||||
|
/verein/{id}:
|
||||||
|
get:
|
||||||
|
summary: Verein nach ID abrufen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Verein Details
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Verein'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
put:
|
||||||
|
summary: Verein aktualisieren
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VereinUpdateRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Verein aktualisiert
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Verein'
|
||||||
|
delete:
|
||||||
|
summary: Verein löschen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Gelöscht
|
||||||
|
/funktionaer:
|
||||||
|
get:
|
||||||
|
summary: Alle Funktionäre abrufen (paginiert)
|
||||||
|
parameters:
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 100
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Liste von Funktionären
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Funktionaer'
|
||||||
|
post:
|
||||||
|
summary: Neuen Funktionär erstellen
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/FunktionaerCreateRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Funktionär erstellt
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Funktionaer'
|
||||||
|
/funktionaer/{id}:
|
||||||
|
get:
|
||||||
|
summary: Funktionär nach ID abrufen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Funktionär Details
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Funktionaer'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
|
put:
|
||||||
|
summary: Funktionär aktualisieren
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/FunktionaerUpdateRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Funktionär aktualisiert
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Funktionaer'
|
||||||
|
delete:
|
||||||
|
summary: Funktionär löschen
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Gelöscht
|
||||||
|
/funktionaer/search:
|
||||||
|
get:
|
||||||
|
summary: Sucht Funktionäre nach SatzNummer
|
||||||
|
parameters:
|
||||||
|
- name: q
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Liste von Funktionären
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Funktionaer'
|
||||||
|
/funktionaer/satz/{satzId}/{satzNummer}:
|
||||||
|
get:
|
||||||
|
summary: Funktionär nach Satz-ID und Nummer suchen
|
||||||
|
parameters:
|
||||||
|
- name: satzId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: satzNummer
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Funktionär gefunden
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Funktionaer'
|
||||||
|
'404':
|
||||||
|
description: Nicht gefunden
|
||||||
/rules/turnierklassen:
|
/rules/turnierklassen:
|
||||||
get:
|
get:
|
||||||
summary: Alle Turnierklassen abrufen
|
summary: Alle Turnierklassen abrufen
|
||||||
|
|
@ -58,9 +507,327 @@ components:
|
||||||
reiterId:
|
reiterId:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
satznummer:
|
||||||
|
type: string
|
||||||
nachname:
|
nachname:
|
||||||
type: string
|
type: string
|
||||||
vorname:
|
vorname:
|
||||||
type: string
|
type: string
|
||||||
|
geburtsdatum:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
bundeslandNummer:
|
||||||
|
type: integer
|
||||||
|
vereinsName:
|
||||||
|
type: string
|
||||||
|
nation:
|
||||||
|
type: string
|
||||||
|
reiterLizenz:
|
||||||
|
type: string
|
||||||
|
startkarte:
|
||||||
|
type: string
|
||||||
|
fahrLizenz:
|
||||||
|
type: string
|
||||||
|
mitgliedsNummer:
|
||||||
|
type: integer
|
||||||
|
telefonNummer:
|
||||||
|
type: string
|
||||||
|
lastPayYear:
|
||||||
|
type: integer
|
||||||
|
feiId:
|
||||||
|
type: string
|
||||||
|
lizenzKlasse:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
ReiterCreateRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- satznummer
|
||||||
|
- nachname
|
||||||
|
- vorname
|
||||||
|
properties:
|
||||||
satznummer:
|
satznummer:
|
||||||
type: string
|
type: string
|
||||||
|
nachname:
|
||||||
|
type: string
|
||||||
|
vorname:
|
||||||
|
type: string
|
||||||
|
geburtsdatum:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
bundeslandNummer:
|
||||||
|
type: integer
|
||||||
|
vereinsName:
|
||||||
|
type: string
|
||||||
|
nation:
|
||||||
|
type: string
|
||||||
|
reiterLizenz:
|
||||||
|
type: string
|
||||||
|
startkarte:
|
||||||
|
type: string
|
||||||
|
fahrLizenz:
|
||||||
|
type: string
|
||||||
|
mitgliedsNummer:
|
||||||
|
type: integer
|
||||||
|
telefonNummer:
|
||||||
|
type: string
|
||||||
|
lastPayYear:
|
||||||
|
type: integer
|
||||||
|
feiId:
|
||||||
|
type: string
|
||||||
|
lizenzKlasse:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
ReiterUpdateRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
nachname:
|
||||||
|
type: string
|
||||||
|
vorname:
|
||||||
|
type: string
|
||||||
|
geburtsdatum:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
bundeslandNummer:
|
||||||
|
type: integer
|
||||||
|
vereinsName:
|
||||||
|
type: string
|
||||||
|
nation:
|
||||||
|
type: string
|
||||||
|
reiterLizenz:
|
||||||
|
type: string
|
||||||
|
startkarte:
|
||||||
|
type: string
|
||||||
|
fahrLizenz:
|
||||||
|
type: string
|
||||||
|
mitgliedsNummer:
|
||||||
|
type: integer
|
||||||
|
telefonNummer:
|
||||||
|
type: string
|
||||||
|
lastPayYear:
|
||||||
|
type: integer
|
||||||
|
feiId:
|
||||||
|
type: string
|
||||||
|
lizenzKlasse:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
Horse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
pferdId:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
kopfnummer:
|
||||||
|
type: string
|
||||||
|
pferdeName:
|
||||||
|
type: string
|
||||||
|
lebensnummer:
|
||||||
|
type: string
|
||||||
|
geschlecht:
|
||||||
|
type: string
|
||||||
|
geburtsjahr:
|
||||||
|
type: integer
|
||||||
|
farbe:
|
||||||
|
type: string
|
||||||
|
satznummer:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
HorseCreateRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- pferdeName
|
||||||
|
- geschlecht
|
||||||
|
properties:
|
||||||
|
kopfnummer:
|
||||||
|
type: string
|
||||||
|
pferdeName:
|
||||||
|
type: string
|
||||||
|
lebensnummer:
|
||||||
|
type: string
|
||||||
|
geschlecht:
|
||||||
|
type: string
|
||||||
|
geburtsjahr:
|
||||||
|
type: integer
|
||||||
|
farbe:
|
||||||
|
type: string
|
||||||
|
satznummer:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
HorseUpdateRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
kopfnummer:
|
||||||
|
type: string
|
||||||
|
pferdeName:
|
||||||
|
type: string
|
||||||
|
lebensnummer:
|
||||||
|
type: string
|
||||||
|
geschlecht:
|
||||||
|
type: string
|
||||||
|
geburtsjahr:
|
||||||
|
type: integer
|
||||||
|
farbe:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
Verein:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
vereinId:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
vereinsNummer:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
bundesland:
|
||||||
|
type: string
|
||||||
|
ort:
|
||||||
|
type: string
|
||||||
|
plz:
|
||||||
|
type: string
|
||||||
|
strasse:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
telefon:
|
||||||
|
type: string
|
||||||
|
website:
|
||||||
|
type: string
|
||||||
|
istVeranstalter:
|
||||||
|
type: boolean
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
imageUrl:
|
||||||
|
type: string
|
||||||
|
bemerkungen:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
VereinCreateRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- vereinsNummer
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
vereinsNummer:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
bundesland:
|
||||||
|
type: string
|
||||||
|
ort:
|
||||||
|
type: string
|
||||||
|
plz:
|
||||||
|
type: string
|
||||||
|
strasse:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
telefon:
|
||||||
|
type: string
|
||||||
|
website:
|
||||||
|
type: string
|
||||||
|
istVeranstalter:
|
||||||
|
type: boolean
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
imageUrl:
|
||||||
|
type: string
|
||||||
|
bemerkungen:
|
||||||
|
type: string
|
||||||
|
VereinUpdateRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
bundesland:
|
||||||
|
type: string
|
||||||
|
ort:
|
||||||
|
type: string
|
||||||
|
plz:
|
||||||
|
type: string
|
||||||
|
strasse:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
telefon:
|
||||||
|
type: string
|
||||||
|
website:
|
||||||
|
type: string
|
||||||
|
istVeranstalter:
|
||||||
|
type: boolean
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
imageUrl:
|
||||||
|
type: string
|
||||||
|
bemerkungen:
|
||||||
|
type: string
|
||||||
|
Funktionaer:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
funktionaerId:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
satzId:
|
||||||
|
type: string
|
||||||
|
satzNummer:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
qualifikationen:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
bemerkungen:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
FunktionaerCreateRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- satzId
|
||||||
|
- satzNummer
|
||||||
|
properties:
|
||||||
|
satzId:
|
||||||
|
type: string
|
||||||
|
satzNummer:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
qualifikationen:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
bemerkungen:
|
||||||
|
type: string
|
||||||
|
FunktionaerUpdateRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
qualifikationen:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
istAktiv:
|
||||||
|
type: boolean
|
||||||
|
bemerkungen:
|
||||||
|
type: string
|
||||||
|
|
|
||||||
38
docs/99_Journal/2026-04-10_Billing-Setup_ZNS-Hardening.md
Normal file
38
docs/99_Journal/2026-04-10_Billing-Setup_ZNS-Hardening.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
type: Journal
|
||||||
|
status: ACTIVE
|
||||||
|
owner: Curator
|
||||||
|
last_update: 2026-04-10
|
||||||
|
---
|
||||||
|
# Journal Entry: 2026-04-10 - Billing Service Setup & ZNS Importer Hardening
|
||||||
|
|
||||||
|
## 👷 [Backend Developer] / 🏗️ [Lead Architect] / 🧹 [Curator]
|
||||||
|
|
||||||
|
### Zusammenfassung der Session
|
||||||
|
In dieser Session wurde das Fundament für den Kassa-Service (`billing-context`) gelegt und die Robustheit des ZNS-Importers durch zusätzliche Integrationstests für Funktionäre gesteigert.
|
||||||
|
|
||||||
|
### Wichtigste Ergebnisse
|
||||||
|
1. **Billing Service Initialisierung:**
|
||||||
|
* `billing-service` Modul erstellt und konfiguriert.
|
||||||
|
* Exposed-Tabellendefinitionen für `TeilnehmerKonto` und `Buchung` implementiert.
|
||||||
|
* Repository-Schnittstellen (Domain) und Exposed-Implementierungen (Service) erstellt.
|
||||||
|
* `TeilnehmerKontoService` mit Basis-Logik (Kontoerstellung & Buchungen) implementiert.
|
||||||
|
2. **ZNS-Importer Hardening:**
|
||||||
|
* Erweiterung von `ZnsImportServiceTest` um Tests für mehrfache Qualifikationen und die Update-Strategie (Delete+Insert) bei Funktionären (`RICHT01.dat`).
|
||||||
|
* Alle 11 Integrationstests sind erfolgreich durchgelaufen.
|
||||||
|
3. **Kompilations-Fixes (Billing):**
|
||||||
|
* `billing-service` auf korrekte Exposed DSL Syntax (`selectAll().where { ... }`) umgestellt.
|
||||||
|
* Explizite `transaction { ... }` Blöcke in `TeilnehmerKontoService` eingeführt (da `@Transactional` ohne JPA-Starter nicht verfügbar war).
|
||||||
|
* Typ-Konsistenz für `Instant` (kotlin.time) in `billing-domain` zur Übereinstimmung mit `core-domain` hergestellt.
|
||||||
|
|
||||||
|
### Betroffene Dateien
|
||||||
|
- `backend/services/billing/` (Neuer SCS-Kontext)
|
||||||
|
- `backend/infrastructure/zns-importer/src/test/kotlin/at/mocode/zns/importer/ZnsImportServiceTest.kt`
|
||||||
|
|
||||||
|
### Nächste Schritte
|
||||||
|
- Implementierung der REST-API für den Billing-Service (OpenAPI-First).
|
||||||
|
- Integration des Billing-Services in den `entries-context` (z.B. automatische Buchung von Nenngebühren).
|
||||||
|
- UI-Anbindung im Frontend für Kontenübersicht und manuelle Buchungen.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Co-authored-by: Junie <junie@jetbrains.com>*
|
||||||
|
|
@ -94,6 +94,11 @@ include(":backend:services:masterdata:masterdata-domain")
|
||||||
include(":backend:services:masterdata:masterdata-infrastructure")
|
include(":backend:services:masterdata:masterdata-infrastructure")
|
||||||
include(":backend:services:masterdata:masterdata-service")
|
include(":backend:services:masterdata:masterdata-service")
|
||||||
|
|
||||||
|
// --- BILLING (Kassa, Zahlungen & Rechnungen) ---
|
||||||
|
include(":backend:services:billing:billing-api")
|
||||||
|
include(":backend:services:billing:billing-domain")
|
||||||
|
include(":backend:services:billing:billing-service")
|
||||||
|
|
||||||
// --- PING (Ping Service) ---
|
// --- PING (Ping Service) ---
|
||||||
include(":backend:services:ping:ping-service")
|
include(":backend:services:ping:ping-service")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user