Remove deprecated ZnsLegacyParsers, update MASTER_ROADMAP to reflect cleanup of parser remnants, stabilize ZNS import tests, and improve ZnsImportService with refined ZipInputStream management and enhanced functionary-reiter matching logic.

This commit is contained in:
2026-04-06 14:41:37 +02:00
parent e219116609
commit 1a6f2ea7ad
5 changed files with 16 additions and 213 deletions
@@ -1,208 +0,0 @@
package at.mocode.zns.parser
import at.mocode.core.domain.model.DatenQuelleE
import at.mocode.core.domain.model.ReiterLizenzKlasseE
import at.mocode.core.domain.model.PferdeGeschlechtE
import at.mocode.core.utils.parser.FixedWidthLineReader
import at.mocode.masterdata.domain.model.Funktionaer
import at.mocode.masterdata.domain.model.Pferd
import at.mocode.masterdata.domain.model.Reiter
import at.mocode.masterdata.domain.model.Verein
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
/**
* Parsers for the legacy ZNS .dat files (Fixed-Width format, CP850 encoded).
*
* @deprecated Use specialized parsers instead (ZnsVereinParser, ZnsReiterParser, etc.)
*/
@Deprecated("Use specialized parsers instead")
@OptIn(ExperimentalUuidApi::class)
object ZnsLegacyParsers {
/**
* Parses a line from VEREIN01.DAT.
*/
fun parseVerein(line: String): Verein? {
if (line.isBlank() || line.length < 5) return null
val reader = FixedWidthLineReader(line)
val vereinsNummer = reader.getString(1, 4)
val vereinsName = reader.getString(5, 50)
if (vereinsNummer.isBlank() || vereinsName.isBlank()) return null
return Verein(
vereinsNummer = vereinsNummer,
vereinName = vereinsName,
datenQuelle = DatenQuelleE.IMPORT_ZNS
)
}
/**
* Parses a line from LIZENZ01.DAT.
*/
fun parseLizenz(line: String): Reiter? {
if (line.isBlank() || line.length < 57) return null
val reader = FixedWidthLineReader(line)
val satznummer = reader.getString(1, 6)
if (satznummer.isBlank()) return null
val nachname = reader.getString(7, 50)
val vorname = reader.getString(57, 25)
val bundeslandNummer = reader.getIntOrNull(82, 2)
val vereinsName = reader.getString(84, 50)
val nation = reader.getString(134, 3)
val reiterLizenz = reader.getString(137, 4)
// Ab Stelle 137 weicht die Realität der ZNS.zip von der Spec 2.4 ab
// Die Realität (Aichinger Ewald) zeigt:
// 134-136: AUT
// 137-140: R2
// 147-158: 206607000676 (Mitgliedsnummer 8 Stellen ab 147?)
// 160-166: 4825910 (Telefonnummer?)
// 177-180: 2023 (LastPayYear)
// 181: M (Geschlecht)
// 182-189: 19571010 (Geburtsdatum)
val startkarte = reader.getString(141, 1)
val fahrLizenz = reader.getString(142, 2)
val altersklasseJgJrU25 = reader.getString(144, 2)
val altersklasseY = reader.getString(146, 1)
val mitgliedsNummer = reader.getIntOrNull(147, 8)
val telefonNummer = reader.getString(155, 22).trim()
val kader = reader.getString(177, 1)
val lastPayYear = reader.getIntOrNull(177, 4)
val geschlecht = reader.getString(181, 1)
val geburtsdatum = reader.getLocalDateOrNull(182, 8)
val feiId = reader.getString(190, 8)
val sperrListe = reader.getString(198, 1)
val lizenzInfo = reader.getString(201, 10)
val lizenzKlasse = mapLizenz(reiterLizenz)
return Reiter(
personId = Uuid.random(),
satznummer = satznummer,
nachname = nachname,
vorname = vorname,
bundeslandNummer = bundeslandNummer,
vereinsName = vereinsName.ifBlank { null },
nation = nation.ifBlank { null },
reiterLizenz = reiterLizenz.ifBlank { null },
startkarte = startkarte.ifBlank { null },
fahrLizenz = fahrLizenz.ifBlank { null },
altersklasseJgJrU25 = altersklasseJgJrU25.ifBlank { null },
altersklasseY = altersklasseY.ifBlank { null },
mitgliedsNummer = mitgliedsNummer,
telefonNummer = telefonNummer.ifBlank { null },
kader = kader.ifBlank { null },
lastPayYear = lastPayYear,
geschlecht = geschlecht.ifBlank { null },
geburtsdatum = geburtsdatum,
feiId = feiId.ifBlank { null },
sperrListe = sperrListe.ifBlank { null },
lizenzInfo = lizenzInfo.ifBlank { null },
lizenzKlasse = lizenzKlasse,
datenQuelle = DatenQuelleE.IMPORT_ZNS
)
}
/**
* Parses a line from PFERDE01.DAT.
*/
fun parsePferd(line: String): Pferd? {
if (line.isBlank() || line.trim().length < 4) return null
val reader = FixedWidthLineReader(line)
val kopfnummer = reader.getString(1, 4)
val name = reader.getString(5, 30)
// We need at least a name to identify a horse record
if (name.isBlank()) return null
val lebensnummer = reader.getString(35, 9)
val geschlechtChar = reader.getString(44, 1)
val geschlecht = mapGeschlecht(geschlechtChar)
val geburtsjahr = reader.getIntOrNull(45, 4)
val farbe = reader.getString(49, 15)
val abstammung = reader.getString(64, 15)
val vereinNummer = reader.getIntOrNull(79, 4)
val lastPayYear = reader.getIntOrNull(83, 4)
val verantwortlichePersonId = reader.getString(87, 75)
val vaterName = reader.getString(162, 30)
val feiPass = reader.getString(192, 10)
val satznummer = reader.getString(202, 10)
return Pferd(
personId = Uuid.random(),
pferdeName = name,
geschlecht = geschlecht,
geburtsjahr = geburtsjahr,
lebensnummer = lebensnummer.ifBlank { null },
kopfnummer = kopfnummer.ifBlank { null },
satznummer = satznummer.ifBlank { null },
farbe = farbe.ifBlank { null },
abstammung = abstammung.ifBlank { null },
vereinNummer = vereinNummer,
lastPayYear = lastPayYear,
verantwortlichePersonId = verantwortlichePersonId.ifBlank { null },
vater = vaterName.ifBlank { null },
feiPass = feiPass.ifBlank { null },
datenQuelle = DatenQuelleE.IMPORT_ZNS
)
}
/**
* Parses a line from RICHT01.DAT (Richter oder Parcoursbauer).
*/
fun parseFunktionaer(line: String): Funktionaer? {
if (line.isBlank() || line.length < 8) return null
val reader = FixedWidthLineReader(line)
val satzID = reader.getString(1, 1).uppercase()
if (satzID != "X" && satzID != "Y") return null
val satzNummer = reader.getIntOrNull(2, 6)
if (satzNummer == null) return null
// Name begins directly after the satzNummer (position 8)
val name = reader.getString(8, 75).trim()
// Qualifikation is much later, probably at 83?
// Wait, name is 75 chars, so 8 + 75 = 83.
val qualifikationenRaw = reader.getString(83, 30).trim()
val qualifikationen = qualifikationenRaw.split(",")
.map { it.trim() }
.filter { it.isNotBlank() }
return Funktionaer(
personId = null,
satzId = satzID,
satzNummer = satzNummer,
name = name.ifBlank { null },
qualifikationen = qualifikationen,
datenQuelle = DatenQuelleE.IMPORT_ZNS
)
}
private fun mapLizenz(lizenz: String): ReiterLizenzKlasseE {
return when (lizenz.uppercase()) {
"R1" -> ReiterLizenzKlasseE.R1
"R2" -> ReiterLizenzKlasseE.R2
"R3" -> ReiterLizenzKlasseE.R3
"RD1" -> ReiterLizenzKlasseE.RD1
"RD2" -> ReiterLizenzKlasseE.RD2
"RD3" -> ReiterLizenzKlasseE.RD3
else -> ReiterLizenzKlasseE.LIZENZFREI
}
}
private fun mapGeschlecht(geschlecht: String): PferdeGeschlechtE {
return when (geschlecht.uppercase()) {
"W" -> PferdeGeschlechtE.WALLACH
"S" -> PferdeGeschlechtE.STUTE
"H" -> PferdeGeschlechtE.HENGST
else -> PferdeGeschlechtE.UNBEKANNT
}
}
}