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:
2026-04-10 12:18:00 +02:00
parent bab95d14f4
commit 21f3a57e6e
12 changed files with 1237 additions and 5 deletions
@@ -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
}