feat(entries+billing): integrate automatic fee booking for entries with billing service
- **Entries-Service Updates:** - Implemented automatic booking of fees (entry fees and late fees) during entry submission using `TeilnehmerKontoService`. - Enhanced `Bewerb` entity with financial fields (`nenngeldCent`, `nachnenngebuehrCent`). - Added Flyway migration to update `bewerbe` table with new financial fields. - Updated `EntriesServiceApplication` to include billing package scanning for integration. - **Billing-Service Enhancements:** - Adjusted `TeilnehmerKontoService` to support fetching accounts by event and person. - Improved database configuration to handle missing JDBC URLs during tests. - **Tests:** - Added integration tests to validate fee booking logic for entries, including late fee scenarios. - Introduced H2 database setup for test isolation. - **Misc:** - Updated tenant-aware transactions to support H2 and PostgreSQL dialects. - Adjusted log and error handling for robust integration between services.
This commit is contained in:
+13
-1
@@ -1,6 +1,10 @@
|
||||
package at.mocode.entries.service.config
|
||||
|
||||
import at.mocode.billing.service.persistence.BuchungTable
|
||||
import at.mocode.billing.service.persistence.TeilnehmerKontoTable
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
@@ -9,6 +13,7 @@ import javax.sql.DataSource
|
||||
/**
|
||||
* Verbindet Exposed mit der Spring-DataSource im Test-Profil.
|
||||
* Ersetzt die EntriesDatabaseConfiguration (die mit @Profile("!test") ausgeschlossen ist).
|
||||
* Initialisiert auch das Billing-Schema, da BillingDatabaseConfiguration im Test ebenfalls ausgeschlossen ist.
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("test")
|
||||
@@ -16,6 +21,13 @@ class TestExposedConfiguration {
|
||||
|
||||
@Bean
|
||||
fun exposedDatabase(dataSource: DataSource): Database {
|
||||
return Database.connect(dataSource)
|
||||
val db = Database.connect(dataSource)
|
||||
transaction(db) {
|
||||
SchemaUtils.create(
|
||||
TeilnehmerKontoTable,
|
||||
BuchungTable
|
||||
)
|
||||
}
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package at.mocode.entries.service.usecase
|
||||
|
||||
import at.mocode.billing.domain.model.BuchungsTyp
|
||||
import at.mocode.billing.service.TeilnehmerKontoService
|
||||
import at.mocode.entries.api.NennungEinreichenRequest
|
||||
import at.mocode.entries.service.bewerbe.Bewerb
|
||||
import at.mocode.entries.service.bewerbe.BewerbRepository
|
||||
import at.mocode.entries.service.persistence.AbteilungTable
|
||||
import at.mocode.entries.service.persistence.BewerbRichterEinsatzTable
|
||||
import at.mocode.entries.service.persistence.BewerbTable
|
||||
import at.mocode.entries.service.persistence.NennungTable
|
||||
import at.mocode.entries.service.tenant.Tenant
|
||||
import at.mocode.entries.service.tenant.TenantContextHolder
|
||||
import at.mocode.entries.service.tenant.tenantTransaction
|
||||
import org.jetbrains.exposed.v1.jdbc.deleteAll
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
class NennungBillingIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var nennungUseCases: NennungUseCases
|
||||
|
||||
@Autowired
|
||||
private lateinit var bewerbRepository: BewerbRepository
|
||||
|
||||
@Autowired
|
||||
private lateinit var kontoService: TeilnehmerKontoService
|
||||
|
||||
private val turnierId = Uuid.random()
|
||||
private val reiterId = Uuid.random()
|
||||
private val pferdId = Uuid.random()
|
||||
private val abteilungId = Uuid.random()
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
// We use PUBLIC schema in H2 for simplicity in this integration test
|
||||
TenantContextHolder.set(Tenant(turnierId.toString(), "PUBLIC", "jdbc:h2:mem:entries-test"))
|
||||
|
||||
// Ensure tables exist in H2 (Flyway might have run on public already, but let's be sure)
|
||||
kotlinx.coroutines.runBlocking {
|
||||
tenantTransaction {
|
||||
org.jetbrains.exposed.v1.jdbc.SchemaUtils.create(
|
||||
NennungTable,
|
||||
BewerbTable,
|
||||
AbteilungTable,
|
||||
BewerbRichterEinsatzTable
|
||||
)
|
||||
NennungTable.deleteAll()
|
||||
BewerbRichterEinsatzTable.deleteAll()
|
||||
BewerbTable.deleteAll()
|
||||
AbteilungTable.deleteAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun teardown() {
|
||||
TenantContextHolder.clear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nennung einreichen bucht automatisch Nenngeld`() = kotlinx.coroutines.runBlocking {
|
||||
// GIVEN: Ein Bewerb mit Nenngeld
|
||||
val bewerb = bewerbRepository.create(Bewerb(
|
||||
id = Uuid.random(),
|
||||
turnierId = turnierId,
|
||||
klasse = "L",
|
||||
bezeichnung = "Standardspringprüfung",
|
||||
nenngeldCent = 2500, // 25,00 EUR
|
||||
hoeheCm = 120
|
||||
))
|
||||
|
||||
val request = NennungEinreichenRequest(
|
||||
turnierId = turnierId,
|
||||
bewerbId = bewerb.id,
|
||||
abteilungId = abteilungId,
|
||||
reiterId = reiterId,
|
||||
pferdId = pferdId,
|
||||
istNachnennung = false
|
||||
)
|
||||
|
||||
// WHEN: Nennung einreichen
|
||||
val result = nennungUseCases.nennungEinreichen(request)
|
||||
|
||||
// THEN: Konto muss existieren und Saldo muss -25,00 EUR sein (Gebühr)
|
||||
val konto = kontoService.getKonto(turnierId, reiterId)
|
||||
assertNotNull(konto, "Konto sollte automatisch erstellt worden sein")
|
||||
assertEquals(-2500L, konto?.saldoCent)
|
||||
|
||||
val buchungen = kontoService.getBuchungsHistorie(konto!!.kontoId)
|
||||
assertEquals(1, buchungen.size)
|
||||
assertEquals(BuchungsTyp.NENNGELD, buchungen[0].typ)
|
||||
assertEquals(-2500L, buchungen[0].betragCent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nachnennung bucht zusätzlich Nachnenngebühr`() = kotlinx.coroutines.runBlocking {
|
||||
// GIVEN: Ein Bewerb mit Nenngeld und Nachnenngebühr
|
||||
val bewerb = bewerbRepository.create(Bewerb(
|
||||
id = Uuid.random(),
|
||||
turnierId = turnierId,
|
||||
klasse = "M",
|
||||
bezeichnung = "Zeitspringprüfung",
|
||||
nenngeldCent = 3000,
|
||||
nachnenngebuehrCent = 1500,
|
||||
hoeheCm = 130
|
||||
))
|
||||
|
||||
val request = NennungEinreichenRequest(
|
||||
turnierId = turnierId,
|
||||
bewerbId = bewerb.id,
|
||||
abteilungId = abteilungId,
|
||||
reiterId = reiterId,
|
||||
pferdId = pferdId,
|
||||
istNachnennung = true
|
||||
)
|
||||
|
||||
// WHEN: Nennung einreichen
|
||||
nennungUseCases.nennungEinreichen(request)
|
||||
|
||||
// THEN: Saldo muss -45,00 EUR sein (-30 - 15)
|
||||
val konto = kontoService.getKonto(turnierId, reiterId)
|
||||
assertEquals(-4500L, konto?.saldoCent)
|
||||
|
||||
val buchungen = kontoService.getBuchungsHistorie(konto!!.kontoId)
|
||||
assertEquals(2, buchungen.size)
|
||||
// Einer muss NACHNENNGEBUEHR sein
|
||||
assertNotNull(buchungen.find { it.typ == BuchungsTyp.NACHNENNGEBUEHR })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user