feat(billing-feature): introduce billing module with Money class, calculation logic, and DI setup

- Added `Money` value class for precise monetary operations.
- Implemented `BillingCalculator` to handle fee calculations, including ÖTO-compliant contributions and prize distribution rules.
- Created `BillingModule` for dependency injection using Koin.
- Integrated `billing-feature` into the desktop shell and project dependencies.
- Introduced `TurnierWizardV2` and `VeranstalterAuswahlV2` screens with improved UI and billing synchronization support.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-30 16:38:25 +02:00
parent 0503cf8bcc
commit b2e6158328
9 changed files with 462 additions and 9 deletions
@@ -35,6 +35,7 @@ kotlin {
implementation(projects.frontend.features.veranstaltungFeature)
implementation(projects.frontend.features.turnierFeature)
implementation(project(":frontend:features:profile-feature"))
implementation(project(":frontend:features:billing-feature"))
// Compose Desktop
implementation(compose.desktop.currentOs)
@@ -11,6 +11,7 @@ import at.mocode.frontend.core.localdb.DatabaseProvider
import at.mocode.frontend.core.localdb.localDbModule
import at.mocode.frontend.core.network.networkModule
import at.mocode.frontend.core.sync.di.syncModule
import at.mocode.frontend.features.billing.di.billingModule
import at.mocode.frontend.features.profile.di.profileModule
import at.mocode.nennung.feature.di.nennungFeatureModule
import at.mocode.ping.feature.di.pingFeatureModule
@@ -33,6 +34,7 @@ fun main() = application {
nennungFeatureModule,
znsImportModule,
profileModule,
billingModule,
desktopModule,
)
}
@@ -23,8 +23,10 @@ import at.mocode.frontend.features.profile.presentation.ProfileViewModel
import at.mocode.ping.feature.presentation.PingScreen
import at.mocode.ping.feature.presentation.PingViewModel
import at.mocode.turnier.feature.presentation.TurnierDetailScreen
import at.mocode.turnier.feature.presentation.TurnierWizardV2
import at.mocode.veranstalter.feature.presentation.FakeVeranstalterStore
import at.mocode.veranstalter.feature.presentation.FakeVeranstaltungStore
import at.mocode.veranstalter.feature.presentation.VeranstalterAuswahlV2
import at.mocode.veranstalter.feature.presentation.VeranstalterNeuScreen
import at.mocode.veranstaltung.feature.presentation.AdminUebersichtScreen
import at.mocode.veranstaltung.feature.presentation.VeranstaltungDetailScreen
@@ -300,18 +302,18 @@ private fun DesktopContentArea(
// Root-Screen: Leitet in V2-Fluss ab
is AppScreen.Veranstaltungen -> {
// Direkt zur Veranstalter-Auswahl V2
at.mocode.desktop.v2.VeranstalterAuswahlV2(
onBack = { /* bleibt root */ },
VeranstalterAuswahlV2(
onZurueck = { /* bleibt root */ },
onWeiter = { vId -> onNavigate(AppScreen.VeranstalterDetail(vId)) },
onNeu = { onNavigate(AppScreen.VeranstalterNeu) },
onNeuerVeranstalter = { onNavigate(AppScreen.VeranstalterNeu) },
)
}
// Neuer Flow: Veranstalter auswählen → Detail → Veranstaltung-Übersicht
is AppScreen.VeranstalterAuswahl -> at.mocode.desktop.v2.VeranstalterAuswahlV2(
onBack = { onNavigate(AppScreen.Veranstaltungen) },
is AppScreen.VeranstalterAuswahl -> VeranstalterAuswahlV2(
onZurueck = { onNavigate(AppScreen.Veranstaltungen) },
onWeiter = { veranstalterId -> onNavigate(AppScreen.VeranstalterDetail(veranstalterId)) },
onNeu = { onNavigate(AppScreen.VeranstalterNeu) },
onNeuerVeranstalter = { onNavigate(AppScreen.VeranstalterNeu) },
)
is AppScreen.VeranstalterNeu -> VeranstalterNeuScreen(
@@ -414,11 +416,10 @@ private fun DesktopContentArea(
onBack = { onNavigate(AppScreen.Veranstaltungen) }
)
} else {
at.mocode.desktop.v2.TurnierWizardV2(
veranstalterId = parent.id,
TurnierWizardV2(
veranstaltungId = evtId,
onBack = { onNavigate(AppScreen.VeranstaltungUebersicht(parent.id, evtId)) },
onSaved = { _ -> onNavigate(AppScreen.VeranstaltungUebersicht(parent.id, evtId)) },
onSave = { onNavigate(AppScreen.VeranstaltungUebersicht(parent.id, evtId)) },
)
}
}