Eigentümersuche (Verfahrensgebiet #56) dokumentiert

Testkonzept docs/tests/verfahrensgebiet-eigentuemersuche.md, Fallstricke
zu personenbezogenen Eigentümerdaten und zur Zahlerkennung beim CSV-Laden
(PLZ ohne führende Null), Modulkarte und Testkatalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 13:04:16 +02:00
co-authored by Claude Opus 5.5
parent cf46d3ad1c
commit 44cce10bda
6 changed files with 225 additions and 24 deletions
+5 -3
View File
@@ -86,21 +86,23 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
| `main.py` | 80 | `Verfahrensgebiet`: registriert Action (Priorität 10), erzeugt `DockWidget` mit `Pruefmanager` und `DataGrabber` |
| `ui/dockwidget.py` | 60 | Tabs `WorkingTab`, `EigentuemerTab`, `PrintTab`, `SettingsTab`; injiziert Services per `set_services()` |
| `ui/tabs/working_tab.py` ⚠ | 820 | Tab „Bearbeitung“: Buttons und Handler für ALKIS/Shape laden, Gemarkungen, Flurstücke, Grenzpunkte, Beteiligung, Statistik; hält viel Ablauflogik |
| `ui/tabs/eigentuemer_tab.py` | 104 | Tab „Eigentümer“ (#56): Flurstückssuche – Eingabe, Hinweise, Treffer auswählen und hinzoomen |
| `ui/tabs/eigentuemer_tab.py` | 211 | Tab „Eigentümer“ (#56): Flurstückssuche (Treffer auswählen und hinzoomen) und Eigentümersuche (Eigentümerdatei wählen, Trefferliste öffnen) |
| `ui/eigentuemer_treffer.py` | 67 | Nicht modale Trefferliste der Eigentümersuche mit Mehrfachauswahl (#56) |
| `functions/verfahrensgebiet_alkis.py` | 359 | WFS-Suche `BauRaumOderBodenordnungsrecht` per Verfahrensnummer/Landkreis, `LoadStatus` |
| `functions/verfahrensgebiet_alkis_komplett.py` ⚠ | 661 | Vollständiger Ladeablauf aus ALKIS: BROR → Gemarkungen → Flurstücke → Flächenvergleich → Detailprüfung |
| `functions/verfahrensgebiet_shape_komplett.py` ⚠ | 978 | Ladeablauf aus Shape/ZIP (Mehrfachauswahl, `FBSchl`-Klassifikation, Abgleich mit BROR) – siehe `docs/aus_shape_laden_technisch.md` im Repo |
| `functions/verfahrensgebiet_dialoge.py` | 300 | Dialoge: BROR-Kombinationen, Landkreis-Objekte, Landkreisauswahl |
| `functions/verfahrensgebiet_dialoge.py` | 344 | Dialoge: BROR-Kombinationen, Landkreis-Objekte, Landkreisauswahl; gemeinsame Auswahltabelle `baue_auswahltabelle` (auch für die Eigentümer-Trefferliste) |
| `functions/verfahrensgebiet_edit_service.py` | 359 | Verfahrensgebiet aus Flurstücksauswahl bilden, ergänzen, entfernen, ersetzen |
| `functions/gemarkung_alkis.py` | 229 | Gemarkungen aus vereinfachtem ALKIS-WFS |
| `functions/flurstueck_alkis.py` | 259 | Flurstücke je Gemarkung aus ALKIS-SF-WFS (BBOX, Batches) |
| `functions/flurstueck_suche.py` | 175 | Flurstückssuche im Layer „Flurstücke“ über das `flurstueckskennzeichen`; Gemarkungsname über Layer „Gemarkungen“ |
| `functions/eigentuemer_suche.py` | 225 | Eigentümersuche im CSV-Layer (Serienbriefexport): Layer erkennen, Name/Vorname suchen, gleiche Personen mit gleicher Anschrift zusammenfassen; Qt-frei |
| `functions/grenzpunkt_alkis.py` ⚠ | 1 674 | Grenzpunkte/`PunktortTA` gemarkungsweise, GML-Parsing, URL-sichere Batches |
| `functions/knickpunkt_pruefung.py` ⚠ | 810 | Knickpunktbasierte BROR-Prüfung, Ergebnislayer mit Kategorien |
| `functions/umringspruefung.py` | 312 | Umringsprüfung (Variante 1) |
| `functions/umringstatistik_grenzpunkte.py` | 374 | Statistik Grenzpunkte am Umring |
| `functions/beteiligung_pruefung.py` | 453 | Abgleich BI-Server-CSV mit ALKIS-Flurstücken |
| `tests/` | 14 Dateien | Mock-Modus, aktuell 157/201 grün (Baseline) |
| `tests/` | 16 Dateien | Mock-Modus, aktuell 157/201 grün (Baseline) |
| `docs/` | 3 Dateien | Ablaufdiagramme (Mermaid) für ALKIS- und Shape-Laden |
| `styles/` | 6 QML | BROR, Gemarkungen, Beteiligung, Verfahrensgebiet |
File diff suppressed because one or more lines are too long
+53 -7
View File
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
Gesamt: 3 Pakete, 62 Testdateien, 762 Testfälle – 719 grün, 35 rot (Baseline), 8 übersprungen.
Mit Beschreibung (Docstring): 617 von 762 (81 %).
Gesamt: 3 Pakete, 64 Testdateien, 782 Testfälle – 739 grün, 35 rot (Baseline), 8 übersprungen.
Mit Beschreibung (Docstring): 637 von 782 (81 %).
## Zusammenfassung
| Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| `sn_basis` | 43 | 490 | 490 | 0 | 0 | 490 von 490 (100 %) |
| `sn_verfahrensgebiet` | 14 | 228 | 186 | 34 | 8 | 126 von 228 (55 %) |
| `sn_verfahrensgebiet` | 16 | 248 | 206 | 34 | 8 | 146 von 248 (59 %) |
| `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
| **Summe** | **62** | **762** | **719** | **35** | **8** | **617 von 762 (81 %)** |
| **Summe** | **64** | **782** | **739** | **35** | **8** | **637 von 782 (81 %)** |
### Legende
@@ -1071,7 +1071,7 @@ Testgruppen:
## Paket `sn_verfahrensgebiet`
Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 14 Testdateien mit 228 Testfällen (186 grün, 34 rot, 8 übersprungen).
Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 16 Testdateien mit 248 Testfällen (206 grün, 34 rot, 8 übersprungen).
### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
@@ -1128,13 +1128,40 @@ Testgruppen:
| `TestDockWidget.test_init_tabs_with_services_injects_available_services` | — (Docstring fehlt) | grün |
| `TestDockWidget.test_init_tabs_with_services_reports_errors` | — (Docstring fehlt) | grün |
### `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_suche.py`
Worum es in der Datei geht: Tests für die Eigentümersuche im Serienbriefexport (#56).
Testgruppen:
- `TestLayererkennung` (2 Testfälle): — (Docstring fehlt)
- `TestSuche` (9 Testfälle): — (Docstring fehlt)
- `TestAnzeigeformen` (1 Testfälle): — (Docstring fehlt)
| Testfall | prüft | Status |
| --- | --- | --- |
| `TestLayererkennung.test_layer_mit_name_und_vorname_ist_eigentuemerlayer` | Prüft, dass ein Layer mit den Spalten Nachname und Vorname als Eigentümerdatei erkannt wird, auch mit BOM und anderer Schreibweise. | grün |
| `TestLayererkennung.test_eigentuemerlayer_im_projekt` | Prüft, dass aus dem Projekt nur passende Layer kommen, nach Namen sortiert. | grün |
| `TestSuche.test_ohne_eingabe_fehler` | Prüft, dass ohne Name und Vorname nicht die ganze Datei gelistet, sondern um eine Eingabe gebeten wird. | grün |
| `TestSuche.test_layer_ohne_pflichtspalten` | Prüft, dass ein Layer ohne Nachname/Vorname mit Hinweis auf den Serienbriefexport abgelehnt wird. | grün |
| `TestSuche.test_teilstring_ohne_gross_kleinschreibung` | Prüft, dass Name und Vorname als Teilstring ohne Rücksicht auf Groß-/Kleinschreibung gesucht werden. | grün |
| `TestSuche.test_umlaute_und_eszett` | Prüft, dass „Müller“ auch „Mueller“ findet und „Strauss“ auch „Strauß“. | grün |
| `TestSuche.test_gleiche_person_gleiche_anschrift_zusammengefasst` | Prüft, dass Zeilen mit gleichem Namen und gleicher Anschrift ein Treffer werden und Konten, Personen-IDs und E-Mails gesammelt werden. | grün |
| `TestSuche.test_andere_anschrift_eigener_treffer` | Prüft, dass dieselbe Person mit anderer Anschrift ein eigener Treffer bleibt, sortiert nach Wohnort. | grün |
| `TestSuche.test_kennzeichen_verstorben_und_rechtsperson` | Prüft, dass „verstorben“ und „Rechtsperson“ aus üblichen Ja-Werten erkannt werden und Nein-Werte, Leeres und NULL nicht zählen. | grün |
| `TestSuche.test_plz_mit_fuehrender_null` | Prüft, dass eine von QGIS als Zahl gelesene PLZ wieder fünfstellig mit führender Null erscheint und Zeilen dadurch zusammenfinden. | grün |
| `TestSuche.test_suche_gibt_nichts_aus` | Prüft, dass die Suche keine Personendaten auf die Konsole schreibt (Datenschutz). | grün |
| `TestAnzeigeformen.test_name_und_anschrift` | Prüft, dass Name mit Titel und einzeilige Anschrift mit Ortsteil lesbar zusammengesetzt werden. | grün |
### `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_tab.py`
Worum es in der Datei geht: Tests für den Tab „Eigentümer“ – Flurstückssuche (#56).
Worum es in der Datei geht: Tests für den Tab „Eigentümer“ – Flurstückssuche und Eigentümersuche (#56).
Testgruppen:
- `TestFlurstueckSuchen` (5 Testfälle): — (Docstring fehlt)
- `TestEigentuemerSuchen` (4 Testfälle): — (Docstring fehlt)
- `TestTrefferliste` (2 Testfälle): — (Docstring fehlt)
- `TestDockWidgetTabs` (1 Testfälle): — (Docstring fehlt)
| Testfall | prüft | Status |
@@ -1144,6 +1171,12 @@ Testgruppen:
| `TestFlurstueckSuchen.test_kein_treffer` | Prüft, dass ohne Treffer ein Hinweis erscheint und keine Auswahl gesetzt wird. | grün |
| `TestFlurstueckSuchen.test_treffer_werden_ausgewaehlt_und_angezoomt` | Prüft, dass Treffer im Flurstückslayer ausgewählt, angezoomt und gemeldet werden. | grün |
| `TestFlurstueckSuchen.test_ohne_kartenansicht_bleibt_auswahl` | Prüft, dass die Auswahl auch ohne Kartenansicht gesetzt und gemeldet wird. | grün |
| `TestEigentuemerSuchen.test_ohne_eigentuemerdatei_warnung` | Prüft, dass ohne gewählte Eigentümerdatei die Liste aufgefrischt und dann auf das Laden der CSV hingewiesen wird. | grün |
| `TestEigentuemerSuchen.test_suchfehler_als_warnung` | Prüft, dass ein Fehler der Suche (z. B. leere Eingabe) als Warnung erscheint. | grün |
| `TestEigentuemerSuchen.test_kein_treffer_ohne_namen_in_meldung` | Prüft, dass ohne Treffer ein Hinweis erscheint, der den gesuchten Namen nicht wiederholt (Datenschutz). | grün |
| `TestEigentuemerSuchen.test_treffer_oeffnen_trefferliste_und_ersetzen_die_alte` | Prüft, dass Treffer in einer neuen Trefferliste erscheinen, eine offene alte Liste geschlossen wird und die Eingaben an die Suche gehen. | grün |
| `TestTrefferliste.test_trefferzeile_mit_hinweis` | Prüft, dass die Trefferliste Name, Anschrift, E-Mail und die Kennzeichen „verstorben“/„Rechtsperson“ zeigt. | grün |
| `TestTrefferliste.test_ausgewaehlte_eigentuemer` | Prüft, dass das Trefferfenster die in der Tabelle gewählten Einträge zurückgibt. | grün |
| `TestDockWidgetTabs.test_tab_ist_im_dockwidget_eingetragen` | Prüft, dass der Tab „Eigentümer“ im DockWidget direkt nach dem Bearbeitungs-Tab steht. | grün |
### `Plugin_SN_Verfahrensgebiet/tests/test_flurstueck_alkis.py`
@@ -1355,6 +1388,19 @@ Testgruppen:
| `TestVerfahrensgebietAlkisKomplett.test_landkreis_modus_wird_bei_fehlender_vkz_verwendet` | Keine Verfahrensnummer, aber Landkreis gesetzt → Landkreis-Suchpfad | grün |
| `TestVerfahrensgebietAlkisKomplett.test_landkreis_modus_nur_laden_ueberspringt_dialog2` | nur_laden=True → Funktion kehrt sofort zurück, Dialog 2 (Gemarkung | grün |
### `Plugin_SN_Verfahrensgebiet/tests/test_verfahrensgebiet_dialoge.py`
Worum es in der Datei geht: Tests für die gemeinsame Auswahltabelle in verfahrensgebiet_dialoge (#56).
Testgruppen:
- `TestAuswahltabelle` (2 Testfälle): — (Docstring fehlt)
| Testfall | prüft | Status |
| --- | --- | --- |
| `TestAuswahltabelle.test_suchfilter_ueber_alle_spalten` | Prüft, dass der Suchfilter Teilstrings in jeder Spalte ohne Groß-/Kleinschreibung findet und leer alles zeigt. | grün |
| `TestAuswahltabelle.test_auswahl_nach_umsortieren` | Prüft, dass die Auswahl über den gespeicherten Index auf die ursprünglichen Zeilen zurückgeführt wird, auch wenn die Tabelle umsortiert ist. | grün |
### `Plugin_SN_Verfahrensgebiet/tests/test_verfahrensgebiet_edit_service.py`
Worum es in der Datei geht: Unit-Tests für das Verfahrensgebiet-Edit-Service-Modul.
@@ -1605,7 +1651,7 @@ dann verschwindet er hier von selbst.
Keine Lücken.
### `sn_verfahrensgebiet` – 102 von 228 Testfällen ohne Beschreibung
### `sn_verfahrensgebiet` – 102 von 248 Testfällen ohne Beschreibung
#### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` (25 von 25)
@@ -0,0 +1,85 @@
# Testkonzept: Eigentümersuche im Tab „Eigentümer“
Plugin: `sn_verfahrensgebiet` · Stand: 06.10.2026 · Pflege: bei jeder Änderung an diesem Ablauf
## Worum es fachlich geht
Im Termin mit Eigentümern gleicht die Sachbearbeitung die gespeicherten Personendaten ab: Name, Anschrift und
E-Mail. Grundlage ist der Serienbriefexport als CSV-Datei, den die Sachbearbeitung von Hand ins Projekt lädt.
Sie sucht nach Name und/oder Vorname. Die Trefferliste fasst Einträge derselben Person mit derselben Anschrift
zusammen, denn im Export steht eine Person für jedes Eigentumskonto einmal. Dieselbe Person mit einer anderen
Anschrift bleibt ein eigener Eintrag. Damit fällt eine abweichende Anschrift sofort auf. Verstorbene und
Rechtspersonen sind in der Liste gekennzeichnet.
Die Trefferliste sieht nur die Sachbearbeitung. Das Anzeigefenster für den Eigentümer auf dem zweiten Bildschirm
und die Korrekturen folgen im nächsten Schritt von Issue Verfahrensgebiet #56.
## Der Ablauf
```mermaid
flowchart TD
A["1 Anwender lädt den Serienbriefexport<br/>als CSV-Layer ins Projekt"] --> B["2 Eigentümerdatei wählen<br/>(nur Layer mit Nachname und Vorname)"]
B --> C{"3 Eigentümerdatei<br/>gewählt?"}
C -->|nein| D["Hinweis: CSV laden und wählen"]
C -->|ja| E{"4 Name oder Vorname<br/>eingegeben?"}
E -->|nein| F["Hinweis: Namen eingeben"]
E -->|ja| G["5 Einträge suchen<br/>(Teilwort, Groß-/Kleinschreibung<br/>und Umlaute egal)"]
G --> H["6 Gleiche Person mit gleicher<br/>Anschrift zusammenfassen"]
H --> I{"7 Treffer?"}
I -->|nein| J["Hinweis: keine Einträge<br/>(ohne den Namen zu wiederholen)"]
I -->|ja| K["8 Trefferliste mit Anschrift, E-Mail,<br/>verstorben / Rechtsperson;<br/>Mehrfachauswahl"]
style D fill:#fff4e6,stroke:#d9822b
style F fill:#fff4e6,stroke:#d9822b
```
## Was geprüft wird
Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_suche.py` (Suchlogik),
`tests/test_eigentuemer_tab.py` (Bedienung, Trefferliste) und `tests/test_verfahrensgebiet_dialoge.py`
(gemeinsame Auswahltabelle).
| # | Schritt | Wird geprüft durch | Ebene |
|---|---|---|---|
| 2 | Nur Layer mit Nachname und Vorname, auch mit BOM oder anderer Schreibweise | `test_layer_mit_name_und_vorname_ist_eigentuemerlayer`, `test_eigentuemerlayer_im_projekt` | Mock |
| 3 | Keine Eigentümerdatei gewählt | `test_ohne_eigentuemerdatei_warnung` | Mock |
| 3 | Layer ohne die nötigen Spalten | `test_layer_ohne_pflichtspalten` | Mock |
| 4 | Ohne Eingabe keine Komplettliste | `test_ohne_eingabe_fehler`, `test_suchfehler_als_warnung` | Mock |
| 5 | Teilwort, Groß-/Kleinschreibung | `test_teilstring_ohne_gross_kleinschreibung` | Mock |
| 5 | Umlaute und ß | `test_umlaute_und_eszett` | Mock |
| 6 | Zusammenfassen, Konten und E-Mails sammeln | `test_gleiche_person_gleiche_anschrift_zusammengefasst` | Mock |
| 6 | Andere Anschrift bleibt eigener Eintrag | `test_andere_anschrift_eigener_treffer` | Mock |
| 7 | Kein Treffer, Meldung ohne Namen | `test_kein_treffer_ohne_namen_in_meldung` | Mock |
| 8 | Trefferliste öffnen, alte ersetzen | `test_treffer_oeffnen_trefferliste_und_ersetzen_die_alte` | Mock |
| 8 | Spalten der Trefferliste, Kennzeichen | `test_trefferzeile_mit_hinweis`, `test_kennzeichen_verstorben_und_rechtsperson` | Mock |
| 8 | Ausgewählte Einträge, auch nach Umsortieren | `test_ausgewaehlte_eigentuemer`, `test_auswahl_nach_umsortieren` | Mock |
| 8 | Suchfeld in der Trefferliste | `test_suchfilter_ueber_alle_spalten` | Mock |
| — | Keine Personendaten auf der Konsole | `test_suche_gibt_nichts_aus` | Mock |
| — | Sieht das in QGIS richtig aus? | manueller Test | manuell |
Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` (Mensch in QGIS).
## Fachliche Sonderfälle
| Sonderfall | Kommt vor bei | Wird geprüft durch |
|---|---|---|
| Eine Person mit mehreren Eigentumskonten | Regelfall im Serienbriefexport | `test_gleiche_person_gleiche_anschrift_zusammengefasst` |
| Schreibweise weicht ab (Groß-/Kleinschreibung, Leerzeichen) | Handeingaben über Jahre | `test_gleiche_person_gleiche_anschrift_zusammengefasst` |
| `Müller` gesucht, `Mueller` gespeichert | ältere Datenbestände | `test_umlaute_und_eszett` |
| PLZ ohne führende Null (`1067`) | QGIS erkennt beim CSV-Laden die Spalte als Zahl | `test_plz_mit_fuehrender_null` |
| Verstorben/Rechtsperson als `J`, `1`, `ja`, `true` | unterschiedliche Exportstände | `test_kennzeichen_verstorben_und_rechtsperson` |
| Leere Felder bzw. QGIS-NULL | unvollständige Datensätze | `test_kennzeichen_verstorben_und_rechtsperson` |
| Rechtsperson ohne Vornamen (z. B. Gemeinde) | Körperschaften als Eigentümer | `test_name_und_anschrift` |
## Bewusst nicht getestet
| Was | Warum | Wie stattdessen abgesichert |
|---|---|---|
| Aufbau von Trefferfenster und Auswahltabelle mit echten Qt-Widgets | im Mock-Modus gibt es keine Widgets; die Filter- und Zuordnungslogik ist als reine Funktion getestet | manueller Test in QGIS |
| Echter Serienbriefexport | personenbezogen, gehört nicht ins Repository | erfundene Testzeilen mit der Kopfzeile der Blanko-Vorlage aus dem Issue |
## Bekannte Lücken
| Lücke | Auswirkung | Issue |
|---|---|---|
| Der manuelle QGIS-Test steht aus, auch für den Landkreis-Dialog, dessen Tabelle jetzt aus der gemeinsamen Funktion kommt | Ein Fehler beim Herauslösen der Tabelle bliebe in beiden Dialogen unbemerkt | Verfahrensgebiet #56 |
@@ -9,7 +9,7 @@ Sie gibt Gemarkung und Flurstücksnummer ein, das Plugin wählt das Flurstück i
und springt dorthin. Gesucht wird nur in den Flurstücken, die vorher mit dem Verfahrensgebiet aus ALKIS geladen
wurden. So muss im Tab keine Gemarkung aus ganz Sachsen ausgewählt werden.
Die Eigentümersuche aus Issue Verfahrensgebiet #56 folgt in weiteren Schritten und wird hier ergänzt.
Die Eigentümersuche im selben Tab beschreibt [`verfahrensgebiet-eigentuemersuche.md`](verfahrensgebiet-eigentuemersuche.md).
## Der Ablauf
+2
View File
@@ -102,6 +102,8 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
- **ArcGIS-REST-Layer nicht nach Nummer deuten.** Gleiche Layer-URLs werden in der Linkliste und im Alternativansatz unterschiedlich benannt; Bedeutung und Geometrie immer gegen die Dienstmetadaten prüfen. (Vergleich Alternativansatz 09/2026)
- **CRS:** Plan41 und VLN-API arbeiten in EPSG:25833. Der Alternativansatz erzeugt EPSG:25832. Für neue Layer 25833 verwenden, bis eine Entscheidung dokumentiert ist.
- **Shape-Import erwartet Feld `FBSchl`** mit `901/10110F` (Neuordnungsgebiet) oder `901/10120F` (Neuvermessungsgebiet). Exporte mit abweichendem Feldnamen (`FBSchl.`) wurden bis 26.8.3 abgewiesen; seit PR Verfahrensgebiet #45 (02.09.2026, 26.8.4-unstable) ist die Feldsuche tolerant. Neue Exportvarianten trotzdem gegen die Erkennung testen.
- **Eigentümerdaten sind personenbezogen (Verfahrensgebiet #56).** Der Serienbriefexport wird vom Anwender von Hand als CSV-Layer geladen; das Plugin liest ihn nur. Namen und Anschriften gehören nicht in `print`, Meldungsleiste, Projektvariablen oder Testdaten (Tests nutzen erfundene Namen, `test_suche_gibt_nichts_aus` prüft die Konsole). Ein gespeichertes Projekt enthält nur den Pfad zur CSV, nicht die Daten. (06.10.2026)
- **QGIS liest Zahlen-Spalten einer CSV als Zahl.** Beim Laden als Textdatei-Layer erkennt QGIS Feldtypen; eine PLZ `01067` wird dabei zur Ganzzahl `1067` – in Sachsen beginnt fast jede PLZ mit 0. `eigentuemer_suche._plz` stellt die Null wieder her. Kennungen wie `EIG_KONTO` oder `PER_ID` deshalb als Text vergleichen, nie als Zahl. (06.10.2026)
- **VLN-Upload ersetzt den Serverbestand der VKZ vollständig** (`PUT`). Vor dem Upload sicherstellen, dass alle Teillayer geladen sind.
- **API-Key liegt in `QSettings`** (Registry/Konfigurationsdatei), kein sicherer Schlüsselspeicher. Niemals in Logs oder Tests ausgeben.