Integrate billing-service microservice: add API gateway routing, service discovery with Consul, Docker support, and Spring configuration. Update frontend with API integration, BillingRepository, and BillingViewModel.
This commit is contained in:
@@ -16,6 +16,10 @@ Versionierung folgt [Semantic Versioning](https://semver.org/lang/de/).
|
||||
### [Unreleased]
|
||||
|
||||
### Hinzugefügt
|
||||
- **Phase 12 (Abrechnung & Infrastruktur) - 12.04.2026:**
|
||||
- **Infrastruktur:** Docker-Integration für `billing-service` (Port 8087) und API-Gateway Routing vervollständigt.
|
||||
- **Service Discovery:** Alle relevanten Microservices (`masterdata`, `events`, `results`, `series`, `billing`) sind nun bei Consul registriert.
|
||||
- **Frontend Billing:** `BillingRepository` und `BillingViewModel` auf reale API-Anbindung (Ktor) umgestellt; `BillingScreen` funktionalisiert.
|
||||
- **Backend Fixes - 12.04.2026:**
|
||||
- **Infrastruktur:** Behebung von Startfehlern im `events-service` (DataSource) und `masterdata-service` (Consul).
|
||||
- **Build:** Integration von `results-service` und `series-service` in `settings.gradle.kts`.
|
||||
|
||||
+6
-1
@@ -15,7 +15,8 @@ class GatewayConfig(
|
||||
@Value("\${events.service.url:http://localhost:8085}") private val eventsServiceUrl: String,
|
||||
@Value("\${zns.import.service.url:http://localhost:8095}") private val znsImportServiceUrl: String,
|
||||
@Value("\${results.service.url:http://localhost:8088}") private val resultsServiceUrl: String,
|
||||
@Value("\${series.service.url:http://localhost:8089}") private val seriesServiceUrl: String
|
||||
@Value("\${series.service.url:http://localhost:8089}") private val seriesServiceUrl: String,
|
||||
@Value("\${billing.service.url:http://localhost:8087}") private val billingServiceUrl: String
|
||||
) {
|
||||
|
||||
@Bean
|
||||
@@ -52,6 +53,10 @@ class GatewayConfig(
|
||||
path("/api/v1/series/**")
|
||||
uri(seriesServiceUrl)
|
||||
}
|
||||
route(id = "billing-service") {
|
||||
path("/api/v1/billing/**")
|
||||
uri(billingServiceUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
# ===================================================================
|
||||
# Dockerfile for Billing Service
|
||||
# Based on Spring Boot Service Template with Billing-specific configuration
|
||||
# ===================================================================
|
||||
|
||||
# === CENTRALIZED BUILD ARGUMENTS ===
|
||||
ARG GRADLE_VERSION
|
||||
ARG JAVA_VERSION
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
|
||||
# Service-specific arguments
|
||||
ARG SERVICE_PATH=billing/billing-service
|
||||
ARG SERVICE_NAME=billing-service
|
||||
|
||||
# ===================================================================
|
||||
# Build Stage
|
||||
# ===================================================================
|
||||
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS builder
|
||||
|
||||
# Re-declare build arguments for this stage
|
||||
ARG SERVICE_PATH=billing/billing-service
|
||||
ARG SERVICE_NAME=billing-service
|
||||
|
||||
LABEL stage=builder
|
||||
LABEL maintainer="Meldestelle Development Team"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
# Gradle optimizations
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
|
||||
-Dorg.gradle.daemon=false \
|
||||
-Dorg.gradle.parallel=true \
|
||||
-Dorg.gradle.configureondemand=true \
|
||||
-Xmx2g"
|
||||
|
||||
# Copy build files in optimal order for caching
|
||||
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
|
||||
COPY gradle/ gradle/
|
||||
|
||||
# Make gradlew executable
|
||||
RUN chmod +x gradlew
|
||||
|
||||
COPY platform/ platform/
|
||||
COPY core/ core/
|
||||
COPY build.gradle.kts ./
|
||||
|
||||
# Copy billing service modules
|
||||
COPY backend/services/billing/billing-domain/ backend/services/billing/billing-domain/
|
||||
COPY backend/services/billing/billing-service/ backend/services/billing/billing-service/
|
||||
|
||||
# Build billing service
|
||||
RUN echo "Building Billing Service..." && \
|
||||
./gradlew :backend:services:billing:billing-service:dependencies --no-daemon --info && \
|
||||
./gradlew :backend:services:billing:billing-service:bootJar --no-daemon --info
|
||||
|
||||
# Extract JAR layers
|
||||
WORKDIR /builder
|
||||
RUN cp /workspace/backend/services/billing/billing-service/build/libs/*.jar app.jar && \
|
||||
java -Djarmode=layertools -jar app.jar extract
|
||||
|
||||
# ===================================================================
|
||||
# Runtime Stage
|
||||
# ===================================================================
|
||||
FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime
|
||||
|
||||
# Metadata
|
||||
LABEL service="billing-service" \
|
||||
version="1.0.0" \
|
||||
description="Billing and Financial Service for Austrian Equestrian Federation" \
|
||||
maintainer="Meldestelle Development Team" \
|
||||
java.version="${JAVA_VERSION}"
|
||||
|
||||
# Build arguments
|
||||
ARG APP_USER=billinguser
|
||||
ARG APP_GROUP=billinggroup
|
||||
ARG APP_UID=1008
|
||||
ARG APP_GID=1008
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# System setup
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache curl jq tzdata && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Non-root user creation
|
||||
RUN addgroup -g ${APP_GID} -S ${APP_GROUP} && \
|
||||
adduser -u ${APP_UID} -S ${APP_USER} -G ${APP_GROUP} -h /app -s /bin/sh
|
||||
|
||||
# Directory setup
|
||||
RUN mkdir -p /app/logs /app/tmp && \
|
||||
chown -R ${APP_USER}:${APP_GROUP} /app
|
||||
|
||||
# Re-declare build arguments for runtime stage
|
||||
ARG SERVICE_PATH=billing/billing-service
|
||||
ARG SERVICE_NAME=billing-service
|
||||
|
||||
# Copy Spring Boot layers
|
||||
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/dependencies/ ./
|
||||
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/spring-boot-loader/ ./
|
||||
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/snapshot-dependencies/ ./
|
||||
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} /builder/application/ ./
|
||||
|
||||
USER ${APP_USER}
|
||||
|
||||
# Expose application port and debug port
|
||||
EXPOSE 8087 5012
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD curl -fsS --max-time 2 http://localhost:8087/actuator/health/readiness || exit 1
|
||||
|
||||
# JVM configuration
|
||||
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 \
|
||||
-XX:+UseG1GC \
|
||||
-XX:+UseStringDeduplication \
|
||||
-XX:+UseContainerSupport \
|
||||
-Djava.security.egd=file:/dev/./urandom \
|
||||
-Djava.awt.headless=true \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-Duser.timezone=Europe/Vienna"
|
||||
|
||||
# Spring Boot configuration
|
||||
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
|
||||
SERVER_PORT=8087 \
|
||||
LOGGING_LEVEL_ROOT=INFO \
|
||||
LOGGING_LEVEL_AT_MOCODE_BILLING=DEBUG
|
||||
|
||||
# Startup command
|
||||
ENTRYPOINT ["sh", "-c", "\
|
||||
echo 'Starting Billing Service on port 8087...'; \
|
||||
if [ \"${DEBUG:-false}\" = \"true\" ]; then \
|
||||
echo 'Debug mode enabled on port 5012'; \
|
||||
exec java $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5012 org.springframework.boot.loader.launch.JarLauncher; \
|
||||
else \
|
||||
exec java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher; \
|
||||
fi"]
|
||||
@@ -21,6 +21,10 @@ dependencies {
|
||||
implementation(libs.spring.boot.starter.validation)
|
||||
implementation(libs.spring.boot.starter.actuator)
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
implementation(libs.spring.cloud.starter.consul.discovery)
|
||||
implementation(libs.micrometer.tracing.bridge.brave)
|
||||
implementation(libs.zipkin.reporter.brave)
|
||||
implementation(libs.zipkin.sender.okhttp3)
|
||||
|
||||
// Datenbank-Abhängigkeiten
|
||||
implementation(libs.exposed.core)
|
||||
|
||||
+2
@@ -4,8 +4,10 @@ package at.mocode.billing.service
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
class BillingServiceApplication
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ services:
|
||||
EVENTS_SERVICE_URL: "http://events-service:8085"
|
||||
ZNS_IMPORT_SERVICE_URL: "http://zns-import-service:8095"
|
||||
RESULTS_SERVICE_URL: "http://results-service:8088"
|
||||
BILLING_SERVICE_URL: "http://billing-service:8087"
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
@@ -466,6 +467,78 @@ services:
|
||||
volumes:
|
||||
- ./config/app/base-application.yaml:/workspace/config/application.yml:Z
|
||||
|
||||
# --- MICROSERVICE: Billing Service ---
|
||||
billing-service:
|
||||
image: "${DOCKER_REGISTRY:-git.mo-code.at/mo-code}/billing-service:${DOCKER_TAG:-latest}"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: backend/services/billing/billing-service/Dockerfile
|
||||
args:
|
||||
GRADLE_VERSION: "${DOCKER_GRADLE_VERSION:-9.3.1}"
|
||||
JAVA_VERSION: "${DOCKER_JAVA_VERSION:-25}"
|
||||
VERSION: "${DOCKER_VERSION:-1.0.0-SNAPSHOT}"
|
||||
BUILD_DATE: "${DOCKER_BUILD_DATE}"
|
||||
labels:
|
||||
- "org.opencontainers.image.created=${DOCKER_BUILD_DATE}"
|
||||
container_name: "${PROJECT_NAME:-meldestelle}-billing-service"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${BILLING_PORT:-8087:8087}"
|
||||
- "${BILLING_DEBUG_PORT:-5012:5012}"
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: "${BILLING_SPRING_PROFILES_ACTIVE:-docker}"
|
||||
DEBUG: "${BILLING_DEBUG:-true}"
|
||||
SERVER_PORT: "${BILLING_SERVER_PORT:-8087}"
|
||||
|
||||
# --- KEYCLOAK ---
|
||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: "${KC_ISSUER_URI:-http://localhost:8180/realms/meldestelle}"
|
||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: "${KC_JWK_SET_URI:-http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs}"
|
||||
|
||||
# --- CONSUL ---
|
||||
SPRING_CLOUD_CONSUL_HOST: "${CONSUL_HOST:-consul}"
|
||||
SPRING_CLOUD_CONSUL_PORT: "${CONSUL_HTTP_PORT:-8500}"
|
||||
SPRING_CLOUD_CONSUL_DISCOVERY_SERVICE_NAME: "${BILLING_SERVICE_NAME:-billing-service}"
|
||||
SPRING_CLOUD_CONSUL_DISCOVERY_PREFER_IP_ADDRESS: "${BILLING_CONSUL_PREFER_IP:-true}"
|
||||
|
||||
# - DATENBANK VERBINDUNG -
|
||||
SPRING_DATASOURCE_URL: "${POSTGRES_DB_URL:-jdbc:postgresql://postgres:5432/pg-meldestelle-db}"
|
||||
SPRING_DATASOURCE_USERNAME: "${POSTGRES_USER:-pg-user}"
|
||||
SPRING_DATASOURCE_PASSWORD: "${POSTGRES_PASSWORD:-pg-password}"
|
||||
|
||||
# --- VALKEY ---
|
||||
SPRING_DATA_VALKEY_HOST: "${VALKEY_SERVER_HOSTNAME:-valkey}"
|
||||
SPRING_DATA_VALKEY_PORT: "${VALKEY_SERVER_PORT:-6379}"
|
||||
|
||||
# --- ZIPKIN ---
|
||||
MANAGEMENT_ZIPKIN_TRACING_ENDPOINT: "${ZIPKIN_ENDPOINT:-http://zipkin:9411/api/v2/spans}"
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
keycloak:
|
||||
condition: "service_healthy"
|
||||
consul:
|
||||
condition: "service_healthy"
|
||||
valkey:
|
||||
condition: "service_healthy"
|
||||
zipkin:
|
||||
condition: "service_started"
|
||||
|
||||
healthcheck:
|
||||
test: [ "CMD", "wget", "--spider", "-q", "http://localhost:8087/actuator/health/readiness" ]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "billing-service"
|
||||
profiles: [ "backend", "all" ]
|
||||
volumes:
|
||||
- ./config/app/base-application.yaml:/workspace/config/application.yml:Z
|
||||
|
||||
# --- MICROSERVICE: Series Service ---
|
||||
series-service:
|
||||
image: "${DOCKER_REGISTRY:-git.mo-code.at/mo-code}/series-service:${DOCKER_TAG:-latest}"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Curator Log: Phase 12 - Abrechnung (Billing) & Infrastruktur-Fixes
|
||||
**Datum:** 2026-04-12
|
||||
**Status:** In Arbeit / Integration abgeschlossen
|
||||
|
||||
## 🏗️ Infrastruktur-Updates
|
||||
- **Billing Service:**
|
||||
- Dockerfile für `billing-service` erstellt (Multi-Stage Build mit JRE 25).
|
||||
- Service in `dc-backend.yaml` integriert (Port 8087, Debug 5012).
|
||||
- Gateway-Routing in `GatewayConfig.kt` für `/api/v1/billing/**` konfiguriert.
|
||||
- Spring Cloud Consul Discovery im `billing-service` aktiviert und Abhängigkeiten in `build.gradle.kts` ergänzt.
|
||||
|
||||
## 🎨 Frontend-Integration (Billing Context)
|
||||
- **Domain & Data:**
|
||||
- `BillingRepository` Interface definiert für Kontenverwaltung und Buchungshistorie.
|
||||
- `DefaultBillingRepository` implementiert mit Ktor-Client.
|
||||
- `ApiRoutes` um Billing-Konstanten erweitert.
|
||||
- **UI & State:**
|
||||
- `BillingViewModel` auf das reale Repository umgestellt (Mocks entfernt).
|
||||
- `BillingModule` (Koin) um Repository-Injektion erweitert.
|
||||
- `TurnierAbrechnungTab` im Turnier-Feature nutzt nun den funktionalen `BillingScreen`.
|
||||
|
||||
## 🧹 Fixes & Aufräumarbeiten
|
||||
- Behebung von `Unresolved reference` Fehlern in der DI-Konfiguration des `billing-service`.
|
||||
- Konsolidierung der Koin-Module im `billing-feature`.
|
||||
|
||||
## 🛤️ Roadmap-Status
|
||||
- Phase 12 (Billing) von "Geplant" auf "In Arbeit" gesetzt.
|
||||
- Backend-Kommunikation für Konten und Buchungen ist verifiziert.
|
||||
|
||||
---
|
||||
*Dokumentiert durch den Curator am 12.04.2026*
|
||||
+6
@@ -41,4 +41,10 @@ object ApiRoutes {
|
||||
const val ROOT = "/api/v1/series"
|
||||
fun stand(serieId: String) = "$ROOT/$serieId/stand"
|
||||
}
|
||||
|
||||
object Billing {
|
||||
const val ROOT = "/api/v1/billing"
|
||||
const val KONTEN = "$ROOT/konten"
|
||||
fun buchungen(kontoId: String) = "$KONTEN/$kontoId/buchungen"
|
||||
}
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package at.mocode.frontend.features.billing.data
|
||||
|
||||
import at.mocode.frontend.core.network.ApiRoutes
|
||||
import at.mocode.frontend.features.billing.domain.*
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.http.*
|
||||
|
||||
class DefaultBillingRepository(
|
||||
private val client: HttpClient
|
||||
) : BillingRepository {
|
||||
|
||||
override suspend fun getOrCreateKonto(
|
||||
veranstaltungId: String,
|
||||
personId: String,
|
||||
personName: String
|
||||
): Result<TeilnehmerKontoDto> = runCatching {
|
||||
client.get(ApiRoutes.Billing.KONTEN) {
|
||||
parameter("veranstaltungId", veranstaltungId)
|
||||
parameter("personId", personId)
|
||||
parameter("personName", personName)
|
||||
}.body()
|
||||
}
|
||||
|
||||
override suspend fun getBuchungen(kontoId: String): Result<List<BuchungDto>> = runCatching {
|
||||
client.get(ApiRoutes.Billing.buchungen(kontoId)).body()
|
||||
}
|
||||
|
||||
override suspend fun addBuchung(
|
||||
kontoId: String,
|
||||
request: BuchungRequest
|
||||
): Result<TeilnehmerKontoDto> = runCatching {
|
||||
client.post(ApiRoutes.Billing.buchungen(kontoId)) {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(request)
|
||||
}.body()
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -1,10 +1,13 @@
|
||||
package at.mocode.frontend.features.billing.di
|
||||
|
||||
import at.mocode.frontend.features.billing.data.DefaultBillingRepository
|
||||
import at.mocode.frontend.features.billing.domain.BillingCalculator
|
||||
import at.mocode.frontend.features.billing.domain.BillingRepository
|
||||
import at.mocode.frontend.features.billing.presentation.BillingViewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val billingModule = module {
|
||||
single { BillingCalculator() }
|
||||
factory { BillingViewModel() }
|
||||
single<BillingRepository> { DefaultBillingRepository(get()) }
|
||||
factory { BillingViewModel(get()) }
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package at.mocode.frontend.features.billing.domain
|
||||
|
||||
interface BillingRepository {
|
||||
|
||||
/**
|
||||
* Holt das Konto eines Teilnehmers für eine Veranstaltung.
|
||||
* Erstellt das Konto automatisch im Backend, falls es noch nicht existiert.
|
||||
*/
|
||||
suspend fun getOrCreateKonto(
|
||||
veranstaltungId: String,
|
||||
personId: String,
|
||||
personName: String = "Unbekannt"
|
||||
): Result<TeilnehmerKontoDto>
|
||||
|
||||
/**
|
||||
* Holt die Buchungshistorie für ein bestimmtes Konto.
|
||||
*/
|
||||
suspend fun getBuchungen(
|
||||
kontoId: String
|
||||
): Result<List<BuchungDto>>
|
||||
|
||||
/**
|
||||
* Fügt eine neue Buchung zu einem Konto hinzu.
|
||||
*/
|
||||
suspend fun addBuchung(
|
||||
kontoId: String,
|
||||
request: BuchungRequest
|
||||
): Result<TeilnehmerKontoDto>
|
||||
}
|
||||
+37
-57
@@ -18,78 +18,58 @@ data class BillingUiState(
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class BillingViewModel : ViewModel() {
|
||||
class BillingViewModel(
|
||||
private val repository: BillingRepository
|
||||
) : ViewModel() {
|
||||
private val _uiState = MutableStateFlow(BillingUiState())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
fun loadKonten(veranstaltungId: Long) {
|
||||
fun loadKonto(veranstaltungId: String, personId: String, personName: String) {
|
||||
viewModelScope.launch {
|
||||
_uiState.value = _uiState.value.copy(isLoading = true)
|
||||
// TODO: Echter API-Call zum backend:billing-service
|
||||
// Simuliere Daten für MVP (Mock)
|
||||
val mockKonten = listOf(
|
||||
TeilnehmerKontoDto(
|
||||
id = Uuid.random().toString(),
|
||||
veranstaltungId = veranstaltungId.toString(),
|
||||
personId = Uuid.random().toString(),
|
||||
personName = "Max Mustermann",
|
||||
saldoCent = -4500L,
|
||||
bemerkungen = "Stallbox reserviert"
|
||||
),
|
||||
TeilnehmerKontoDto(
|
||||
id = Uuid.random().toString(),
|
||||
veranstaltungId = veranstaltungId.toString(),
|
||||
personId = Uuid.random().toString(),
|
||||
personName = "Erika Musterreiterin",
|
||||
saldoCent = 1250L
|
||||
)
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(konten = mockKonten, isLoading = false)
|
||||
repository.getOrCreateKonto(veranstaltungId, personId, personName)
|
||||
.onSuccess { konto ->
|
||||
_uiState.value = _uiState.value.copy(selectedKonto = konto, error = null)
|
||||
loadBuchungen(konto.id)
|
||||
}
|
||||
.onFailure {
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, error = it.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectKonto(konto: TeilnehmerKontoDto) {
|
||||
private fun loadBuchungen(kontoId: String) {
|
||||
viewModelScope.launch {
|
||||
_uiState.value = _uiState.value.copy(selectedKonto = konto, isLoading = true)
|
||||
// TODO: API-Call für Buchungen
|
||||
val mockBuchungen = listOf(
|
||||
BuchungDto(
|
||||
id = Uuid.random().toString(),
|
||||
kontoId = konto.id,
|
||||
betragCent = -4000L,
|
||||
typ = "NENNUNG",
|
||||
verwendungszweck = "Nenngeld Bewerb 1",
|
||||
gebuchtAm = "2026-04-10T10:00:00Z"
|
||||
),
|
||||
BuchungDto(
|
||||
id = Uuid.random().toString(),
|
||||
kontoId = konto.id,
|
||||
betragCent = -500L,
|
||||
typ = "GEBUEHR",
|
||||
verwendungszweck = "Systemgebühr",
|
||||
gebuchtAm = "2026-04-10T10:05:00Z"
|
||||
)
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(buchungen = mockBuchungen, isLoading = false)
|
||||
_uiState.value = _uiState.value.copy(isLoading = true)
|
||||
repository.getBuchungen(kontoId)
|
||||
.onSuccess { buchungen ->
|
||||
_uiState.value = _uiState.value.copy(buchungen = buchungen, isLoading = false, error = null)
|
||||
}
|
||||
.onFailure {
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, error = it.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buche(betragCent: Long, zweck: String) {
|
||||
val konto = _uiState.value.selectedKonto ?: return
|
||||
viewModelScope.launch {
|
||||
// TODO: API-Call POST /billing/konten/{id}/buchungen
|
||||
val neueBuchung = BuchungDto(
|
||||
id = Uuid.random().toString(),
|
||||
kontoId = konto.id,
|
||||
betragCent = betragCent,
|
||||
typ = "MANUELL",
|
||||
verwendungszweck = zweck,
|
||||
gebuchtAm = "2026-04-10T13:00:00Z"
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(
|
||||
buchungen = listOf(neueBuchung) + _uiState.value.buchungen,
|
||||
selectedKonto = konto.copy(saldoCent = konto.saldoCent + betragCent)
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(isLoading = true)
|
||||
val request = BuchungRequest(betragCent = betragCent, verwendungszweck = zweck)
|
||||
repository.addBuchung(konto.id, request)
|
||||
.onSuccess { aktualisiertesKonto ->
|
||||
_uiState.value = _uiState.value.copy(selectedKonto = aktualisiertesKonto)
|
||||
loadBuchungen(konto.id)
|
||||
}
|
||||
.onFailure {
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, error = it.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Für Abwärtskompatibilität oder Listenansicht (optional)
|
||||
fun selectKonto(konto: TeilnehmerKontoDto) {
|
||||
_uiState.value = _uiState.value.copy(selectedKonto = konto)
|
||||
loadBuchungen(konto.id)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user