feat(frontend+billing): integrate billing UI and navigation into Turnier module

- **Navigation Updates:**
  - Added `AppScreen.Billing` route for participant billing linked to an event and tournament.

- **UI Additions:**
  - Introduced `BillingScreen` and `BillingViewModel` for participant account management and manual transactions.
  - Updated `TurnierAbrechnungTab` to include `BillingScreen` and enable account interaction.

- **Turnier Enhancements:**
  - Enhanced `NennungenTabContent` to support navigation to billing via a new interaction.
  - Added billing feature as a dependency to `turnier-feature`.

- **Billing Domain:**
  - Extended `Money` to include subtraction operation and improved formatting for negative amounts.
  - Added DTOs (`TeilnehmerKontoDto`, `BuchungDto`, `BuchungRequest`) for seamless data exchange with backend.

- **Test Improvements:**
  - Updated `PreviewTurnierAbrechnungTab` to include interactive billing placeholder.

- **Misc Updates:**
  - Enhanced breadcrumb navigation for billing in `DesktopMainLayout` for better user experience.
This commit is contained in:
2026-04-10 14:30:50 +02:00
parent a7e1872d10
commit 1ba4845f6c
13 changed files with 487 additions and 31 deletions
@@ -55,6 +55,9 @@ sealed class AppScreen(val route: String) {
AppScreen("/veranstaltung/$veranstaltungId/turnier/$turnierId")
data class TurnierNeu(val veranstaltungId: Long) : AppScreen("/veranstaltung/$veranstaltungId/turnier/neu")
data class Billing(val veranstaltungId: Long, val turnierId: Long) :
AppScreen("/veranstaltung/$veranstaltungId/turnier/$turnierId/billing")
data object Reiter : AppScreen("/reiter")
data object Pferde : AppScreen("/pferde")
data object Vereine : AppScreen("/vereine")
@@ -67,6 +70,7 @@ sealed class AppScreen(val route: String) {
private val VERANSTALTUNG_DETAIL = Regex("/veranstaltung/(\\d+)$")
private val TURNIER_DETAIL = Regex("/veranstaltung/(\\d+)/turnier/(\\d+)$")
private val TURNIER_NEU = Regex("/veranstaltung/(\\d+)/turnier/neu$")
private val BILLING = Regex("/veranstaltung/(\\d+)/turnier/(\\d+)/billing$")
private val VERANSTALTER_DETAIL = Regex("/veranstalter/(\\d+)$")
private val VERANSTALTUNG_KONFIG = Regex("/veranstalter/(\\d+)/veranstaltung/neu$")
private val VERANSTALTUNG_PROFIL = Regex("/veranstalter/(\\d+)/veranstaltung/(\\d+)$")
@@ -103,6 +107,9 @@ sealed class AppScreen(val route: String) {
"/cups" -> Cups
"/stammdaten/import" -> StammdatenImport
else -> {
BILLING.matchEntire(route)?.destructured?.let { (vId, tId) ->
return Billing(vId.toLong(), tId.toLong())
}
PFERD_PROFIL.matchEntire(route)?.destructured?.let { (id) -> return PferdProfil(id.toLong()) }
REITER_PROFIL.matchEntire(route)?.destructured?.let { (id) -> return ReiterProfil(id.toLong()) }
VEREIN_PROFIL.matchEntire(route)?.destructured?.let { (id) -> return VereinProfil(id.toLong()) }