Introduce modular ZNS parsers (ZnsVereinParser, ZnsReiterParser, ZnsPferdParser, ZnsFunktionaerParser), replace legacy parsers, and update the import service/test suites to support streamlined parsing and isolated imports. Deprecate ZnsLegacyParsers. Add comprehensive tests (ZnsParserTest) and revise extraction logic for ZNS data.

This commit is contained in:
2026-04-06 01:19:48 +02:00
parent f50d4deb16
commit e94dc5a803
10 changed files with 361 additions and 19 deletions
@@ -6,7 +6,10 @@ import at.mocode.masterdata.domain.repository.VereinRepository
import at.mocode.masterdata.domain.repository.HorseRepository
import at.mocode.masterdata.domain.repository.FunktionaerRepository
import at.mocode.masterdata.domain.repository.ReiterRepository
import at.mocode.zns.parser.ZnsLegacyParsers
import at.mocode.zns.parser.ZnsFunktionaerParser
import at.mocode.zns.parser.ZnsPferdParser
import at.mocode.zns.parser.ZnsReiterParser
import at.mocode.zns.parser.ZnsVereinParser
import java.io.InputStream
import java.nio.charset.Charset
import java.util.zip.ZipInputStream
@@ -121,7 +124,7 @@ class ZnsImportService(
var aktualisiert = 0
zeilen.forEachIndexed { index, zeile ->
runCatching {
val verein = ZnsLegacyParsers.parseVerein(zeile) ?: return@forEachIndexed
val verein = ZnsVereinParser.parse(zeile) ?: return@forEachIndexed
val vorhanden = vereinRepository.findByVereinsNummer(verein.vereinsNummer)
if (vorhanden == null) {
vereinRepository.save(verein)
@@ -156,7 +159,7 @@ class ZnsImportService(
var aktualisiert = 0
zeilen.forEachIndexed { index, zeile ->
runCatching {
val reiter = ZnsLegacyParsers.parseLizenz(zeile) ?: return@forEachIndexed
val reiter = ZnsReiterParser.parse(zeile) ?: return@forEachIndexed
val vorhanden = reiterRepository.findBySatznummer(reiter.satznummer)
if (vorhanden == null) {
reiterRepository.save(reiter)
@@ -205,7 +208,7 @@ class ZnsImportService(
var aktualisiert = 0
zeilen.forEachIndexed { index, zeile ->
runCatching {
val pferd = ZnsLegacyParsers.parsePferd(zeile) ?: return@forEachIndexed
val pferd = ZnsPferdParser.parse(zeile) ?: return@forEachIndexed
if (pferd.pferdeName.isBlank()) return@forEachIndexed
// Match primarily by satznummer, then by lebensnummer
@@ -256,7 +259,7 @@ class ZnsImportService(
var aktualisiert = 0
zeilen.forEachIndexed { index, zeile ->
runCatching {
val funktionaer = ZnsLegacyParsers.parseFunktionaer(zeile) ?: return@forEachIndexed
val funktionaer = ZnsFunktionaerParser.parse(zeile) ?: return@forEachIndexed
val satzID = funktionaer.satzId
val satzNummer = funktionaer.satzNummer
val vorhanden = funktionaerRepository.findBySatz(satzID, satzNummer)
@@ -52,7 +52,9 @@ class ZnsImportServiceTest {
zip.closeEntry()
}
}
return ByteArrayInputStream(baos.toByteArray())
val zipBytes = baos.toByteArray()
// println("[DEBUG_LOG] ZIP erstellt mit ${entries.size} Dateien, Gesamtgröße: ${zipBytes.size} Bytes")
return ByteArrayInputStream(zipBytes)
}
/** Erzeugt eine gültige VEREIN01.DAT-Zeile (mind. 30 Zeichen). */
@@ -223,12 +225,11 @@ class ZnsImportServiceTest {
@Test
fun `importiereZip - vollstaendiger Import aller vier Dateien`() = runTest {
val zip = buildZip(
"VEREIN01.DAT" to vereinZeile(),
"LIZENZ01.DAT" to lizenzZeile(),
"PFERDE01.DAT" to pferdeZeile(),
"RICHT01.DAT" to funktionaerZeile()
)
// Erstelle vier separate InputStreams für die vier Dateien (für isolierte Extraktion)
val zipVerein = buildZip("VEREIN01.DAT" to vereinZeile())
val zipLizenz = buildZip("LIZENZ01.DAT" to lizenzZeile())
val zipPferde = buildZip("PFERDE01.DAT" to pferdeZeile())
val zipRicht = buildZip("RICHT01.DAT" to funktionaerZeile())
coEvery { vereinRepository.findByVereinsNummer(any()) } returns null
coEvery { vereinRepository.save(any()) } answers { firstArg<Verein>() }
@@ -236,16 +237,26 @@ class ZnsImportServiceTest {
coEvery { reiterRepository.save(any()) } answers { firstArg<Reiter>() }
coEvery { horseRepository.findBySatznummer(any()) } returns null
coEvery { horseRepository.findByLebensnummer(any()) } returns null
coEvery { horseRepository.findByName(any(), any()) } returns emptyList()
coEvery { horseRepository.save(any()) } answers { firstArg<Pferd>() }
coEvery { funktionaerRepository.findBySatz(any(), any()) } returns null
coEvery { funktionaerRepository.save(any()) } answers { firstArg<Funktionaer>() }
val result = service.importiereZip(zip)
// Importiere nacheinander (Simulation eines vollständigen Workflows)
val res1 = service.importiereZip(zipVerein)
val res2 = service.importiereZip(zipLizenz)
val res3 = service.importiereZip(zipPferde)
val res4 = service.importiereZip(zipRicht)
assertThat(result.gesamtImportiert).isEqualTo(4)
assertThat(result.gesamtAktualisiert).isEqualTo(0)
assertThat(result.fehler).isEmpty()
assertThat(result.hatFehler).isFalse()
assertThat(res1.vereineImportiert).isEqualTo(1)
assertThat(res2.reiterImportiert).isEqualTo(1)
assertThat(res3.pferdeImportiert).isEqualTo(1)
assertThat(res4.richterImportiert).isEqualTo(1)
assertThat(res1.fehler).isEmpty()
assertThat(res2.fehler).isEmpty()
assertThat(res3.fehler).isEmpty()
assertThat(res4.fehler).isEmpty()
}
@Test
@@ -7,6 +7,7 @@ import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.utils.database.DatabaseFactory
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.repository.ReiterRepository
import at.mocode.masterdata.infrastructure.persistence.LicenseTable.lizenzKlasse
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.*