From bd8b215ddd513bd0c379642745a738b1debf7096 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Oct 2026 11:27:49 +0200 Subject: [PATCH 01/20] Konzept Basemap-Legende und Basemap-Stilvorlagen im Druck (Basis #88) Neue Konzeptseite docs/konzept/10_basemap_legende.md mit den abgestimmten Festlegungen, technischen Grundlagen (selectByGeometry, Style-JSON bm_web_col), Bausteinen, Tests, Umsetzungsschritten und Abnahmekriterien; in docs/README.md verlinkt. Co-Authored-By: Claude Opus 5.5 --- docs/README.md | 1 + docs/konzept/10_basemap_legende.md | 235 +++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 docs/konzept/10_basemap_legende.md diff --git a/docs/README.md b/docs/README.md index e50fd4d..bbf4bb7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,6 +19,7 @@ | `tests/testkatalog.html` | generiert | dieselben Daten als lokal zu öffnendes Dashboard „Prüfstand“ (Baumdiagramm je Testdatei, Filter, Suche) – vom selben Lauf erzeugt | | `tests/*.md` | lebend | Testkonzepte je Fachablauf, mit Mermaid; Vorlage in `vorlagen/` | | `konzept/07_qgis_tests_plan.md` | Vorschlag | Stufenplan für Tests gegen echtes QGIS | +| `konzept/10_basemap_legende.md` | zur Freigabe | Basemap-Legende im Druck und Basemap-Stilvorlagen (Basis #88) | | `konzept/09_alkis7_umstellung.md` | Prüfliste | ALKIS 6 ↔ 7 (GID 6.0/7.1), was in `sn_verfahrensgebiet` anzupassen ist | | `konzept/08_repo_einrichten.md` | Prüfliste | Repo auf Gitea anlegen, `stable` und Branch-Schutz | | `konzept/02_zusammenarbeit_ki_agenten.md` | stabil | Begründung und Aufbau der Zusammenarbeit | diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md new file mode 100644 index 0000000..4da9f65 --- /dev/null +++ b/docs/konzept/10_basemap_legende.md @@ -0,0 +1,235 @@ +# Basemap-Legende und Basemap-Stilvorlagen im Druck + +Stand 02.10.2026. Konzept zur Freigabe, Ticket **Plugin_SN_Basis #88** („Legende für basemap"). +Betroffen ist ausschließlich **`sn_basis`** (Druck); `sn_plan41` und `sn_verfahrensgebiet` nutzen den +Druck-Tab aus `sn_basis` und bekommen die Funktion ohne eigene Änderung. + +## 1. Ziel + +Der Vektorkachel-Dienst basemap.de +(`https://sgx.geodatenzentrum.de/gdz_basemapde_vektor/tiles/v2/bm_web_de_3857/{z}/{x}/{y}.pbf`) hat keine +Legende, die nur die tatsächlich dargestellten Objekte zeigt. Für die Drucklayouts (§ 38, § 41) wird eine solche +Legende gebraucht: + +1. Beim Anlegen einer Druckvorlage wird geprüft, ob ein Basemap-Layer im Projekt ist. +2. Die Objekte der Kacheln **im Auswertebereich** (Standard: Verfahrensgebiet) werden ausgewertet. +3. Ein Auswahldialog zeigt die gefundenen Kategorien; der Nutzer wählt ab, was nicht in die Legende soll. +4. Die gewählten Kategorien erscheinen mit der Signatur **aus dem Projektlayer** (Standard „Basemap.de - Farbe") + in der Legende – nicht mit der Signatur aus dem Signaturenkatalog, weil die Darstellung im Projekt abweichen kann. + +Dazu kommt ein einklappbarer Bereich **„Basemap"** im Druck-Tab (Auswertebereich, Legende aktualisieren, +Stilvorlagen laden/speichern). + +## 2. Festlegungen (mit Daniel abgestimmt, 02.10.2026) + +| # | Frage | Festlegung | +|---|---|---| +| 1 | Für welche Layouts? | Funktion layoutunabhängig bauen; jedes Layout kann sie per Kennzeichen nutzen. **Vorerst aktiv für § 38 und § 41**, nicht für „Anlage zur Baufreigabe". | +| 2 | Auswertebereich | **Standard Verfahrensgebiet.** Umschaltbar auf den Druckbereich (Kartenausschnitt der Vorlage) im Basemap-Bereich des Druck-Tabs. Ohne Verfahrensgebiet: Rückfall auf den Druckbereich mit Hinweis. | +| 3 | Feinheit der Einträge | Brücken-, Tunnel- und Fahrbahnvarianten gehen im Grundeintrag auf („Kreisstraße"). „Im Bau" und „Außer Betrieb" werden eigene Einträge. Beschriftungen (`Name_*`, `Nummer_*`), Hausnummern und `Hintergrund` kommen nie in die Legende. | +| 4 | Basemap-Layer in der Hauptlegende | Der Layer selbst (heute nur Name ohne Symbol) wird ausgeblendet und durch die Gruppe „Basemap" ersetzt. | +| 5 | Nachträglich aktualisieren | Knopf „Basemap-Legende aktualisieren" im Basemap-Bereich, für bestehende Vorlagen. | +| 6 | Stilvorlagen | Darstellung des Basemap-Layers als **lokale Stilvorlage** speichern und wieder laden, **mehrere Varianten** möglich, eine davon als Standard markierbar. | + +## 3. Technische Grundlagen (recherchiert 02.10.2026) + +- **QGIS bietet für Vektorkachel-Layer keine Legende.** `QgsVectorTileLayer` setzt keine `QgsMapLayerLegend` + (Quellcode geprüft für 3.40, 3.44, master). Die Legendeneinträge müssen wir selbst erzeugen. +- **Auswertung über `QgsVectorTileLayer.selectByGeometry()`** (seit QGIS 3.28): holt die Kacheln zur Zoomstufe + des übergebenen Maßstabs (`QgsSelectionContext.setScale`), dekodiert sie, schneidet sie mit der Geometrie (im + Layer-KBS EPSG:3857) und liefert über `selectedFeatures()` Features mit den Zusatzfeldern `tile_layer` und + `tile_zoom`. Mit übergebenem `QgsRenderContext` werden nur Features geliefert, die der Renderer auch zeichnet. + `getRawTile()` ist in Python nicht verfügbar (`SIP_SKIP`). Der Aufruf **verändert die Auswahl des Layers** + – vorher sichern, danach wiederherstellen. +- **Kacheln enden bei Zoomstufe 15** (`bm_web_de_3857.json`). Druckmaßstäbe 1:1.000 bis 1:5.000 werten Stufe 15 + aus. Eine Kachel der Stufe 15 deckt in Sachsen rund 0,6 km² ab → ein Verfahrensgebiet von 50 km² braucht + etwa 100 Kacheln. Bereits von der Kartenansicht geladene Kacheln kommen aus dem Netzwerkcache. +- **Der Signaturenkatalog ist die Style-JSON** `https://sgx.geodatenzentrum.de/gdz_basemapde_vektor/styles/bm_web_col.json` + (569 Style-Layer: 283 Linien, 210 Symbole, 66 Flächen, 7 Kreise, 3 Extrusionen; 29 Quell-Layer). + Style-IDs sind technische Namen (`Kontur_Kreisstr`, `Decker_Kreisstr`, `Bruecke_Kreisstr_getrennt_Decker` …), + **ein Legendeneintrag besteht aus mehreren Style-Layern** (Kreisstraße: 17). Lesbare Bezeichnungen liefern die + `klasse`-Werte der Filter (520 Werte über alle Quell-Layer). +- **Beim Import in QGIS wird jede Style-ID zum `styleName`** eines `QgsVectorTileBasicRendererStyle` (Layername, + Geometrietyp, Filterausdruck, Zoombereich, Symbol, aktiv/inaktiv). Beschriftungen landen in + `QgsVectorTileBasicLabeling`. Darüber verbinden wir Katalog und Projektlayer. +- **Zoomabhängige Strichbreiten** (Mapbox-`stops`) werden in QGIS zu datendefinierten Ausdrücken mit + `@vector_tile_zoom`. In der Legende gibt es diese Variable nicht → Werte für die Druck-Zoomstufe festschreiben + oder die Variable am Legendenelement setzen (Spike, Abschnitt 7). + +Quellen: [Datenmodell](https://basemap.de/data/produkte/web_vektor/meta/bm_web_vektor_datenmodell.html), +[Signaturenkatalog](https://basemap.de/data/produkte/web_vektor/meta/bm_web_vektor_col_signaturenkatalog.html), +[PyQGIS QgsVectorTileLayer](https://qgis.org/pyqgis/3.44/core/QgsVectorTileLayer.html), +QGIS-Quellcode `src/core/vectortile/qgsvectortilelayer.cpp` (`final-3_40_0`, `final-3_44_0`). + +## 4. Ablauf + +```mermaid +flowchart TD + A[Vorlage erstellen
oder Basemap-Legende aktualisieren] --> B{Layout nutzt
Basemap-Legende?} + B -- nein --> Z[Layout wie bisher] + B -- ja --> C{Sichtbarer Basemap-Layer
im Projekt?} + C -- nein --> Z + C -- ja --> D[Auswertebereich bestimmen
Verfahrensgebiet / Druckbereich] + D --> E[Geometrie nach EPSG:3857,
kachelweise selectByGeometry
mit Fortschritt und Abbruch] + E --> F[Feature → aktive Renderer-Styles
bei Druck-Zoomstufe] + F --> G[Styles → Legendeneinträge
über Legendenkatalog] + G --> H[Auswahldialog mit Symbolvorschau
Vorbelegung aus Projektvariable] + H --> I[Gruppe Basemap in der Legende
zusammengesetzte Symbole] + I --> J[Auswahl und Einstellungen
am Layout und im Projekt merken] +``` + +- **Layer-Erkennung:** Layertyp Vektorkachel **und** Quell-URL enthält `gdz_basemapde_vektor`; Name nur als + Rückfall (Layer werden sonst über den Namen gesucht, siehe `fallstricke.md`). Berücksichtigt werden nur Layer, + die in der Karte der Vorlage sichtbar sind (Thema bzw. aktuelle Sichtbarkeit). Mehrere Basemap-Layer + (z. B. Farbe und Grau): jeder bekommt eine eigene Gruppe. +- **Deaktivierte Styles** im Projektlayer erzeugen keine Legendeneinträge. Wer z. B. Hausnummern oder andere + Objekte über eine Stilvorlage ausschaltet, sieht sie automatisch auch nicht in der Legende. +- **Unbekannte Style-IDs** (eigene Ergänzungen im Projektlayer, neue Basemap-Version) erscheinen mit der + Bezeichnung „Quell-Layer: klasse" bzw. dem `styleName` in einer Gruppe „Sonstiges" und sind im Dialog + abgewählt vorbelegt. + +## 5. Bausteine + +### 5.1 `sn_basis` – neue und geänderte Dateien + +| Datei | Inhalt | +|---|---| +| `data/basemap_legende.json` (neu) | **Legendenkatalog:** je Eintrag Schlüssel, Bezeichnung, Gruppe, Reihenfolge, `treffer_stile` (alle Style-IDs, deren Vorkommen den Eintrag auslösen), `symbol_stile` (Style-IDs, die zusammen das Legendensymbol bilden, z. B. `Kontur_Kreisstr` + `Decker_Kreisstr`), dazu eine Liste `nie_in_legende`. Kopf mit Version der Style-JSON. | +| `modules/basemap_legende.py` (neu, < 600 Zeilen) | Layer finden, Auswertebereich bestimmen, Kacheln auswerten, Styles zuordnen (reine Logik, testbar), Symbole zusammensetzen, Gruppe in eine Legende schreiben bzw. ersetzen. Keine direkten QGIS-/Qt-Importe. | +| `modules/basemap_stilvorlagen.py` (neu) | Stilvorlagen auflisten, speichern, laden, löschen, Standard merken. | +| `ui/widgets/basemap_bereich.py` (neu) | Einklappbarer Bereich „Basemap" für den Druck-Tab (Abschnitt 5.2). Eigene Datei, weil `print_tab.py` schon 709 Zeilen hat. | +| `ui/tabs/print_tab.py` | Bereich einbinden (wenige Zeilen), `cleanup()` weiterreichen. | +| `modules/print_logic.py` | Nach der Vorlagenbezeichnung Basemap-Legende ermitteln, wenn das Layout sie nutzt; Ergebnis an das Layout übergeben. Menge der Layouts mit Basemap-Legende als Konstante (`{KARTENNAME_38, KARTENNAME_41}`). | +| `modules/print_layout.py` | Neuer optionaler Parameter für die Basemap-Einträge; Layout-Kennzeichen setzen; Basemap-Layer-Knoten der Hauptlegende ausblenden. | +| `functions/dialog_wrapper.py` | Neuer allgemeiner Dialog `ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)`: Liste mit Häkchen, Symbolvorschau, Gruppen, Suche, „Alle auswählen". Mock: gibt die Vorauswahl zurück. | +| `functions/qgiscore_wrapper.py`, `qgisui_wrapper.py`, `qt_wrapper.py` | Fehlende Symbole mit Mock-Fallback: `QgsVectorTileLayer`, `QgsSelectionContext`, `QgsRenderContext`, `QgsExpression`, `QgsExpressionContext(Scope)`, `QgsCategorizedSymbolRenderer`, `QgsRendererCategory`, `QgsLayerTreeGroup`/`QgsLayerTreeLayer`, `QgsSymbolLayerUtils`, `QgsApplication.qgisSettingsDirPath`, `QgsCollapsibleGroupBox`. | +| `changelog.txt` | Eintrag oben (nächstes Release). | + +**Layoutunabhängigkeit:** Ein Layout nutzt die Basemap-Legende, wenn es die Layout-Eigenschaft +`sn_basemap_legende = 1` trägt und ein Legendenelement mit bekannter ID hat (Standard `Legende`). Am Layout werden +außerdem Auswertebereich, Maßstab und gewählte Einträge gespeichert, damit „Aktualisieren" mit denselben +Einstellungen neu rechnen kann. Ein neues Layout schaltet die Funktion ein, indem es das Kennzeichen setzt bzw. +in die Konstante aufgenommen wird – ohne Änderung am Basemap-Modul. + +### 5.2 Basemap-Bereich im Druck-Tab + +Einklappbar (standardmäßig eingeklappt), unterhalb der bisherigen Einstellungen, oberhalb von „Vorlage erstellen". +Ohne Basemap-Layer im Projekt ist der Bereich deaktiviert und zeigt „Kein Basemap-Layer im Projekt". + +``` +▸ Basemap (Basemap.de - Farbe) + Legende + Auswertebereich [Verfahrensgebiet ▾] (Verfahrensgebiet | Druckbereich) + [x] Basemap-Legende beim Erstellen der Vorlage erzeugen + Vorlage [§41 Wegeplan ▾] [Basemap-Legende aktualisieren] + Stilvorlage + Variante [Ohne Hausnummern ★ ▾] + [Laden] [Speichern unter …] [Als Standard] [Löschen] +``` + +- „Vorlage" listet die Layouts des Projekts mit Kennzeichen `sn_basemap_legende`. +- „Aktualisieren" rechnet mit dem aktuell eingestellten Auswertebereich neu, zeigt den Auswahldialog + (vorbelegt mit der letzten Auswahl dieses Layouts) und ersetzt die Gruppe „Basemap" in der Legende. + +### 5.3 Stilvorlagen (lokal, mehrere Varianten) + +- **Inhalt einer Variante:** Darstellung des Basemap-Layers als QML (Kategorien Symbologie und Beschriftung, + also auch abgeschaltete Styles wie Hausnummern) und eine kleine JSON-Begleitdatei mit Anzeigename, + Erstellungsdatum, Quell-Stil (`bm_web_col` v2) und – optional – den in der Legende standardmäßig abgewählten + Einträgen. +- **Ablage:** lokal im QGIS-Profil, `/sn_basis/basemap_stile/.qml` + `.json`. + Dateiname über `sanitize_bezeichner`. Der Standard wird in `QSettings` gemerkt (`sn_basis/basemap_standardstil`). +- **Laden:** Rückfrage vor dem Überschreiben der aktuellen Darstellung. Danach Prüfung, ob die `styleName`s der + Vorlage zum Layer passen (Farbe vs. Grau, neue Basemap-Version); bei Abweichung Hinweis mit Anzahl unbekannter + Styles. Laden geschieht **nur über den Knopf**, nicht automatisch beim Öffnen eines Projekts. +- **Speichern unter …:** Name abfragen, vorhandene Variante nur nach Rückfrage überschreiben. +- **Später möglich, nicht Teil dieses Tickets:** mitgelieferte Varianten im Plugin (`styles/basemap/`) und ein + gemeinsamer Ordner (Netzlaufwerk) für die ganze Behörde. + +### 5.4 Werkzeug im Projektwissen + +`scripts/basemap_katalog.py` (neu, nur Standardbibliothek): lädt `bm_web_col.json`, gruppiert Style-IDs nach +Quell-Layer und `klasse` (Präfixe/Suffixe wie `Kontur_`, `Decker_`, `Bruecke_`, `Tunnel_`, `_getrennt`, +`_Fahrbahnachse`, `_FSZ1` werden zusammengefasst), schlägt `symbol_stile` vor und schreibt einen Entwurf von +`basemap_legende.json`. Bestehende fachliche Nachbearbeitung (Bezeichnungen, Gruppen, Reihenfolge) bleibt bei +einem erneuten Lauf erhalten; neue oder entfallene Style-IDs werden gemeldet. + +### 5.5 Neue Schnittstellen (in `schnittstellen.md` nachtragen) + +| Art | Schlüssel | Bedeutung | +|---|---|---| +| Projektvariable | `basemap_auswertebereich` | `verfahrensgebiet` (Standard) oder `druckbereich` | +| Projektvariable | `basemap_legende_erzeugen` | `1`/`0`, Standard `1` | +| Projektvariable | `basemap_legende_ausgeblendet` | JSON-Liste der zuletzt abgewählten Eintragsschlüssel (Vorbelegung) | +| Layout-Eigenschaft | `sn_basemap_legende`, `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` | Kennzeichen und letzte Einstellungen je Layout | +| QSettings | `sn_basis/basemap_standardstil` | Name der Standard-Stilvorlage | +| Öffentliche Funktion | `dialog_wrapper.ask_mehrfachauswahl(...)` | neuer allgemeiner Dialog | + +## 6. Tests (Mock-Modus, ohne Netzwerk) + +Jede neue beobachtbare Funktion bekommt mindestens einen Test mit deutschem Docstring („Prüft, dass …"). + +| Bereich | Testfälle | +|---|---| +| Katalog | Laden und Schema; jede Style-ID höchstens einem Eintrag zugeordnet; `symbol_stile` ⊆ `treffer_stile`; Ausschlussliste greift | +| Zuordnung | Mehrere Styles → ein Eintrag; deaktivierte Styles ignoriert; Zoombereich berücksichtigt; Filterprüfung als übergebene Funktion; unbekannte Style-IDs → „Sonstiges", abgewählt vorbelegt; Reihenfolge nach Gruppe/Reihenfolge | +| Layer-Erkennung | Erkennung über Quell-URL; Name nur als Rückfall; unsichtbarer Layer ignoriert; kein Basemap-Layer → Druck unverändert | +| Auswertebereich | Verfahrensgebiet als Standard; Druckbereich bei Einstellung; ohne Verfahrensgebiet Rückfall mit Hinweis; Auswahl des Layers danach wiederhergestellt | +| Dialog | Mock gibt Vorauswahl zurück; Abbruch → keine Basemap-Gruppe, Vorlage trotzdem erzeugt | +| Layout | Kennzeichen nur bei § 38/§ 41; Gruppe „Basemap" wird beim Aktualisieren ersetzt, nicht verdoppelt; Basemap-Layer-Knoten ausgeblendet | +| Projektvariablen | Auswahl und Auswertebereich werden gespeichert und wieder gelesen | +| Stilvorlagen | Speichern, Auflisten, Laden, Löschen in temporärem Profilordner; Name über `sanitize_bezeichner`; Überschreiben nur nach Bestätigung; Standard in QSettings; fehlende Begleitdatei toleriert | +| Katalog-Skript | Gruppierung Kontur/Decker/Brücke zu einem Eintrag an einem Ausschnitt der Style-JSON (Testdaten im Repo, kein Download) | + +**Nur in QGIS prüfbar** (Ergebnis im PR dokumentieren): echte Kachelauswertung, Laufzeit, Aussehen der Symbole +in der Legende, QML-Laden/Speichern am Vektorkachel-Layer, Einklappen des Bereichs unter Qt5 und Qt6. + +## 7. Umsetzungsschritte + +Ein Feature-Branch `feature/88-basemap-legende` in `Plugin_SN_Basis` von `unstable`, Commits je Schritt. + +1. **Spike in QGIS** (Python-Konsole, echtes Verfahrensgebiet, QGIS 3.44 und 4.x). Ergebnis als ADR in + `docs/entscheidungen/`: + - Laufzeit und Kachelzahl von `selectByGeometry()` für ein großes Verfahrensgebiet; kachelweise Aufteilung. + - Auswertung der Style-Filter mit `@vector_tile_zoom`. + - **Darstellung in der Legende:** (a) leere Hilfslayer (Speicherlayer je Geometrietyp, kategorisiert, nicht im + Layerbaum) als Gruppe „Basemap" in der Hauptlegende – offen, ob `setLegendFilterByMapEnabled(True)` sie + wegfiltert; oder (b) eigenes Legendenelement „Basemap" ohne Kartenfilter direkt unter der Hauptlegende. + - Zusammengesetzte Symbole und festgeschriebene Strichbreiten in der Legende. + - `saveNamedStyle`/`loadNamedStyle` am Vektorkachel-Layer inklusive deaktivierter Styles und Beschriftung. + - `QgsCollapsibleGroupBox` unter Qt5/Qt6 verfügbar, sonst Muster des Atlas-Bereichs (Kontrollkästchen + blendet Inhalt ein). +2. **Legendenkatalog:** Skript, erster Entwurf, fachliche Durchsicht der Bezeichnungen und Gruppen. +3. **Auswertung und Zuordnung** (`basemap_legende.py`) mit Tests. +4. **Dialog** `ask_mehrfachauswahl` mit Test. +5. **Layout-Anbindung** in `print_logic`/`print_layout` für § 38/§ 41, Projektvariablen. +6. **Basemap-Bereich im Druck-Tab** mit Aktualisieren-Knopf. +7. **Stilvorlagen** (`basemap_stilvorlagen.py`, Teil des Bereichs). +8. `python scripts/run_tests.py sn_basis --baseline`, `python scripts/testkatalog.py`, + `python scripts/pruefe_umlaute.py Plugin_SN_Basis`, QGIS-Abnahme, `changelog.txt`, PR-Text, Übergabe: + `schnittstellen.md`, `modulkarte.md`, `fallstricke.md` (Katalog hängt an Stil-Version v2), `STATUS.md`. + +## 8. Abnahmekriterien + +- [ ] Beim Erstellen einer § 38- oder § 41-Vorlage mit sichtbarem Basemap-Layer erscheint der Auswahldialog mit + genau den Kategorien, die im Auswertebereich vorkommen und im Projektlayer aktiv sind. +- [ ] Die gewählten Kategorien stehen als Gruppe „Basemap" mit der Signatur des Projektlayers in der Legende; + zusammengesetzte Signaturen (Straßen) sind als ein Symbol dargestellt. +- [ ] Beschriftungen, Hausnummern und Hintergrund erscheinen nie. +- [ ] Ohne Basemap-Layer, bei Abbruch des Dialogs und bei „Anlage zur Baufreigabe" entsteht die Vorlage wie bisher. +- [ ] Auswertebereich ist im Basemap-Bereich auf Druckbereich umstellbar; die Einstellung bleibt im Projekt erhalten. +- [ ] „Basemap-Legende aktualisieren" ersetzt die Gruppe in einer bestehenden Vorlage, ohne sie zu verdoppeln. +- [ ] Stilvorlagen lassen sich unter eigenem Namen speichern, laden, löschen und als Standard markieren; mehrere + Varianten sind möglich; nach dem Laden einer Variante ohne Hausnummern fehlen diese in Karte und Legende. +- [ ] Neue Tests grün, Baseline nicht verschlechtert, Testkatalog aktualisiert, Ablauf in QGIS 3.44 und 4.x geprüft. + +## 9. Risiken + +- **Blockierender Netzwerkzugriff** im Hauptthread bei großen Gebieten → kachelweise mit Fortschrittsanzeige und + Abbruch; Hinweis bei mehr als z. B. 400 Kacheln. +- **Neue Basemap-Stilversion** (heute v2) → Katalog veraltet; Skript erneut laufen lassen, unbekannte IDs fallen + nach „Sonstiges". +- **Kachelränder:** Features sind an Kachelgrenzen beschnitten und können mehrfach vorkommen – für die Frage + „kommt vor" unerheblich. +- **Umfang:** Stilvorlagen sind fachlich eigenständig. Werden sie zu groß für einen PR, als eigenes Ticket + abtrennen (Schritte 1–6 bleiben #88). From a1f399b15025a1c85a6652b5e6c1ca96497271e6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Oct 2026 12:03:14 +0200 Subject: [PATCH 02/20] Basemap-Legende (Basis #88): Umsetzung dokumentiert, ADR 0010, Wissensbasis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Konzept 10_basemap_legende.md: Abschnitt 10 mit Umsetzung, Abweichungen und offenen Punkten - ADR 0010: Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog - schnittstellen.md: neue Projektvariablen, Abschnitt 11 Basemap-Legende - modulkarte.md: neue Module, aktualisierte Zeilenzahlen - fallstricke.md: Legendengröße vor dem ersten Zeichnen, Kartenfilter, Vektorkachel-Zoom, QT_VERSION unter QGIS 3, Mock-Lücken, Offscreen-Spike in QGIS - STATUS.md: Punkt 15; Testkatalog neu erzeugt Co-Authored-By: Claude Opus 5.5 --- docs/architektur/modulkarte.md | 29 +- docs/architektur/schnittstellen.md | 25 + ...basemap-legende-eigenes-legendenelement.md | 42 + docs/entscheidungen/README.md | 1 + docs/konzept/10_basemap_legende.md | 32 +- docs/status/STATUS.md | 8 +- docs/tests/testkatalog.html | 1040 ++++++++++++++++- docs/tests/testkatalog.md | 977 ++++++++++++---- docs/wissen/fallstricke.md | 28 + 9 files changed, 1881 insertions(+), 301 deletions(-) create mode 100644 docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md index 0bab1bb..ac50a37 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -17,17 +17,18 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `ui/dockmanager.py` | 86 | `DockManager.show(dock)`: entfernt alle anderen `sn_dock_*`-Docks, zeigt das neue rechts an | | `ui/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück | | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) | -| `ui/tabs/print_tab.py` ⚠ | 709 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas; nutzt `print_logic` | +| `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` | +| `ui/widgets/basemap_bereich.py` | 271 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Auswertebereich, Legende aktualisieren, Stilvorlagen | ### functions/ – Wrapper-Schicht (Qt/QGIS/OS mit Mock-Fallback) | Datei | Zeilen | Zweck | |---|---:|---| | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) | -| `functions/qt_wrapper.py` ⚠ | 796 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | -| `functions/qgiscore_wrapper.py` ⚠ | 1 122 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | +| `functions/qt_wrapper.py` ⚠ | 832 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | +| `functions/qgiscore_wrapper.py` ⚠ | 1 382 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | | `functions/qgisui_wrapper.py` | 258 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner; Mock-`iface` | -| `functions/dialog_wrapper.py` | 393 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog` | +| `functions/dialog_wrapper.py` | 521 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) | | `functions/message_wrapper.py` | 85 | MessageBar: `info`, `warning`, `error`, `success`, `push_message` | | `functions/variable_wrapper.py` | 120 | `get_variable(key, scope)`, `set_variable(...)` mit Präfix `sn_`; Mock-Speicher | | `functions/settings_logic.py` | 50 | `SettingsLogic.VARIABLEN`: Liste der gespeicherten Projektvariablen, `load()/save()` | @@ -55,9 +56,14 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `modules/Datenabruf.py` ⚠ | 636 | `Datenabruf.datenabruf(...)`: WFS, ArcGIS REST, OGR laden; Raumfilter Verfahrensgebiet/Puffer/ohne | | `modules/LayerLoader.py` | 422 | `create_layer(provider, link, thema)`, `apply_style`, `filter_by_extent`, `filter_by_layer`, `add_to_project` | | `modules/Datenschreiber.py` | 448 | `schreibe_Daten(...)` in GeoPackage, `lade_Layer(...)` ins Projekt, `schreibe_log(...)` HTML-Protokoll | -| `modules/print_logic.py` ⚠ | 1 321 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf | -| `modules/print_layout.py` ⚠ | 1 078 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) | +| `modules/print_logic.py` ⚠ | 1 360 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf | +| `modules/print_layout.py` ⚠ | 1 111 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) | | `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) | +| `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg | +| `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer | +| `modules/basemap_legende_layout.py` | 396 | Legendensymbole aus Projektlayer-Styles zusammensetzen, leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren | +| `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) | +| `modules/basemap_stilvorlagen.py` | 181 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil), Standard in `QSettings` | ### Sonstiges @@ -66,6 +72,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `tests/` | 13 Testdateien, `unittest`, Mock-Modus; `run_tests.py` (farbiger Runner), `test_qgis.bat` (Coverage in OSGeo4W) | | `styles/` | QML-Stile (Verfahrensgebiet, LSG, Denkmalschutz, Flurstücksbeschriftung) | | `data/landkreise.json` | Landkreise mit Kennzahl | +| `data/basemap_legende.json` | Legendenkatalog basemap.de: Style-ID → Legendeneintrag (190 Einträge), erzeugt mit `basemap_katalog.py`, fachlich nachbearbeitet | | `assets/plan-qgis340ExitCrashFix.prompt.md` | KI-Planungsnotiz zum Exit-Crash (historisch) | | `.gitea/workflows/release.yaml`, `.gitea/templates/metadata.template` | Release-Auslöser, Vorlage für `metadata.txt` | @@ -129,12 +136,12 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl | Datei | Zeilen | |---|---:| | `sn_verfahrensgebiet/functions/grenzpunkt_alkis.py` | 1 674 | -| `sn_basis/modules/print_logic.py` | 1 321 | +| `sn_basis/modules/print_logic.py` | 1 360 | | `sn_plan41/ui/tab_a_logic.py` | 1 239 | -| `sn_basis/functions/qgiscore_wrapper.py` | 1 122 | -| `sn_basis/modules/print_layout.py` | 1 078 | +| `sn_basis/functions/qgiscore_wrapper.py` | 1 382 | +| `sn_basis/modules/print_layout.py` | 1 111 | | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 | | `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 | | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 | -| `sn_basis/functions/qt_wrapper.py` | 796 | -| `sn_basis/ui/tabs/print_tab.py` | 709 | +| `sn_basis/functions/qt_wrapper.py` | 832 | +| `sn_basis/ui/tabs/print_tab.py` | 730 | diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md index a3bffa4..dc8eafa 100644 --- a/docs/architektur/schnittstellen.md +++ b/docs/architektur/schnittstellen.md @@ -55,6 +55,9 @@ Der Wrapper setzt das Präfix `sn_` selbst; Schlüssel **ohne** Präfix übergeb | `linkliste`, `tab_a_linkliste` | Plan41 Tab A | Plan41 | Pfad der Linkliste (XLSX) | | `tab_a_layer_id`, `Raumfilter` | Plan41 Tab A | Plan41 | Raumfilter-Einstellung | | `druckrechteck_xmin/xmax/ymin/ymax` | PrintTab/PrintLogic | Druck | gespeicherter Druckbereich | +| `basemap_auswertebereich` | Basemap-Bereich im Druck-Tab | `basemap_legende` | `verfahrensgebiet` (Standard) oder `druckbereich` (Basis #88) | +| `basemap_legende_erzeugen` | Basemap-Bereich | `print_logic` | `1`/`0`, Standard `1`: Basemap-Legende beim Erstellen einer §-38/§-41-Vorlage | +| `basemap_legende_auswahl` | `basemap_legende`, Stilvorlage laden | Auswahldialog | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` mit Eintragsschlüsseln des Legendenkatalogs | | `sn_abdeckungslayer` ⚠ | PrintTab/PrintLogic | Druck (Atlas) | Atlas-Abdeckungslayer. **Schlüssel enthält bereits `sn_` → tatsächliche Variable `sn_sn_abdeckungslayer`**; funktioniert, ist aber inkonsistent | | `verfahrensgebietslayer` ⚠ | `print_logic.py` | `print_logic.py` | **Zweite Schreibweise neben `verfahrensgebiet_layer`** – vermutlich Altlast, beim Refactoring zusammenführen | @@ -126,3 +129,25 @@ flowchart LR ## 10. Linkliste-Repository `linkliste.csv` (Semikolon, Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) → Gitea Action → `linkliste.xml/json/yaml` auf `main`. Die Plan41-Laufzeit nutzt stattdessen die mitgelieferte `assets/Linkliste.xlsx` mit abweichenden Spaltennamen. + +## 11. Basemap-Legende im Druck (Basis #88) + +- **Welche Layouts:** `print_logic.LAYOUTS_MIT_BASEMAP_LEGENDE` (heute §38, §41). Ein Layout nutzt die Funktion, + wenn es die Layout-Eigenschaft `sn_basemap_legende = 1` trägt (`basemap_legende_layout.markiere_layout`); + `PrintLayout.create_single_page_layout(..., basemap_legende=True)` setzt sie und erzeugt die Legende vor dem + Öffnen des Designers. Neue Layouts: in die Konstante aufnehmen bzw. das Kennzeichen setzen. +- **Layout-Elemente nach ID:** `Hauptkarte` (Bezugskarte), `Legende` (Hauptlegende, Spalte und Oberkante), + `Quellenangabe` (Unterkante der Basemap-Legende), `Legende Basemap` (das Basemap-Element selbst). + Umbenennen dieser IDs bricht die Platzierung. +- **Weitere Layout-Eigenschaften:** `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` (JSON), + `sn_basemap_hilfslayer` (IDs der Hilfslayer, werden beim Aktualisieren ersetzt). +- **Hilfslayer:** leere Speicherlayer, nicht im Layerbaum, Eigenschaften `sn_basemap_legende = ` und + `skipMemoryLayersCheck = 1`. Code, der über `QgsProject.mapLayers()` iteriert, sieht sie. +- **Basemap-Layer-Erkennung:** Vektorkachel-Layer, Quelle enthält `gdz_basemapde_vektor`; Name „basemap.de“ nur + als Rückfall. +- **Legendenkatalog:** `sn_basis/data/basemap_legende.json` (Style-ID → Eintrag), erzeugt/aktualisiert mit + `python modules/basemap_katalog.py`; gehört zur Style-Version `bm_web_col` v2. +- **Stilvorlagen:** lokal unter `/sn_basis/basemap_stile/`, Standard in `QSettings` + `sn_basis/basemap_standardstil`. +- **Neue öffentliche Funktion:** `dialog_wrapper.ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)` – + Liste mit Häkchen, Gruppen, Suche; liefert gewählte Schlüssel oder `None` bei Abbruch. diff --git a/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md b/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md new file mode 100644 index 0000000..c1a1860 --- /dev/null +++ b/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md @@ -0,0 +1,42 @@ +# 0010 – Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog + +Status: vorgeschlagen · Datum: 2026-10-02 · Beteiligte: Daniel Helbig, Claude Code · Ticket Plugin_SN_Basis #88 + +## Kontext +QGIS erzeugt für Vektorkachel-Layer keine Legende (`QgsVectorTileLayer` setzt keine `QgsMapLayerLegend`, +geprüft im Quellcode 3.40, 3.44, master). Die §-38-/§-41-Drucklayouts brauchen aber eine Legende der +basemap.de-Objekte, die im Verfahrensgebiet tatsächlich dargestellt werden, mit der Signatur des Projektlayers. +Die Style-JSON von basemap.de hat 569 Style-Layer mit technischen IDs; ein Legendeneintrag wie „Kreisstraße“ +verteilt sich auf bis zu 20 Styles (Kontur, Decker, Brücke, getrennte Fahrbahn …). + +Ein Spike in QGIS 3.44.4 (offscreen, echte Kacheln, Gebiet bei Meißen, 619 ha) am 02.10.2026 ergab: +- `QgsVectorTileLayer.selectByGeometry()` holt und dekodiert die Kacheln zur Zoomstufe des Maßstabs selbst + (5,8 s für 10 250 Objekte, entdoppelt 3 990; Style-Zuordnung 0,5 s). +- Hilfslayer in der **Hauptlegende** verschwinden beim Rendern, weil diese nach Karteninhalt filtert + (`setLegendFilterByMapEnabled(True)`) und die Hilfslayer nicht in der Karte liegen. Außerdem müsste die + Hauptlegende dafür ihre automatische Aktualisierung verlieren. +- Ein **eigenes Legendenelement** ohne Kartenfilter zeigt alle Einträge, übersteht Speichern und Neuladen des + Projekts; leere Speicherlayer lösen beim Schließen keine Rückfrage aus. + +## Entscheidung +Die Basemap-Legende ist ein **eigenes Legendenelement „Legende Basemap“** ohne Kartenfilter und ohne +automatische Aktualisierung, dessen Einträge aus **leeren, kategorisierten Speicherlayern** stammen (je Gruppe +und Geometrietyp, nicht im Layerbaum, mit dem Projekt gespeichert). Welche Styles zu welchem Eintrag gehören +und welche das Symbol bilden, steht in einem **versionierten Legendenkatalog** `sn_basis/data/basemap_legende.json`, +der aus der Style-JSON erzeugt und fachlich nachbearbeitet wird. Signaturen werden zur Laufzeit aus dem +Projektlayer genommen, nicht aus dem Katalog. + +## Folgen +- Hauptlegende bleibt unverändert (Auto-Update, Kartenfilter); der Basemap-Layer selbst erscheint dort nicht, + weil er keine Legendenknoten hat und der Filter ihn ausblendet. +- Lokale Änderungen am Basemap-Stil (Farben, abgeschaltete Styles) wirken automatisch auch in der Legende. +- Die Basemap-Legende ist eine Momentaufnahme; nach Änderungen an Karte oder Stil „Basemap-Legende aktualisieren“. +- Bei einer neuen Basemap-Stilversion muss der Katalog mit `modules/basemap_katalog.py` erneuert werden; + unbekannte Styles erscheinen bis dahin unter „Sonstiges“ (abgewählt). +- Je Layout liegen einige leere Speicherlayer im Projekt (nicht im Layerbaum sichtbar). + +## Verworfene Alternativen +- **Hilfslayer in der Hauptlegende:** vom Kartenfilter ausgeblendet (Spike), und Verlust der Auto-Aktualisierung. +- **Eigene `QgsMapLayerLegend` (Python-Unterklasse) am Vektorkachel-Layer:** wird nicht im Projekt gespeichert, + nach Neuladen wäre die Legende weg. +- **Legende direkt aus dem Signaturenkatalog:** zeigt die Standarddarstellung, nicht die im Projekt angepasste. diff --git a/docs/entscheidungen/README.md b/docs/entscheidungen/README.md index 33a4c75..453a71f 100644 --- a/docs/entscheidungen/README.md +++ b/docs/entscheidungen/README.md @@ -34,3 +34,4 @@ Kurz, mit Grund. | 0007 | Branchmodell: drei Kanäle, `stable` als stabiler Kanal, Rückführungspflicht | vorgeschlagen | | 0008 | Werkzeugunterstützung auf Claude Code fokussiert | vorgeschlagen | | 0009 | Tests sind Teil der Definition of Done, und sie werden für Menschen dokumentiert | vorgeschlagen | +| 0010 | Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog | vorgeschlagen | diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md index 4da9f65..29b1108 100644 --- a/docs/konzept/10_basemap_legende.md +++ b/docs/konzept/10_basemap_legende.md @@ -1,6 +1,6 @@ # Basemap-Legende und Basemap-Stilvorlagen im Druck -Stand 02.10.2026. Konzept zur Freigabe, Ticket **Plugin_SN_Basis #88** („Legende für basemap"). +Stand 02.10.2026. Konzept freigegeben und umgesetzt (Branch `feature/88-basemap-legende`, Abschnitt 10), Ticket **Plugin_SN_Basis #88** („Legende für basemap"). Betroffen ist ausschließlich **`sn_basis`** (Druck); `sn_plan41` und `sn_verfahrensgebiet` nutzen den Druck-Tab aus `sn_basis` und bekommen die Funktion ohne eigene Änderung. @@ -160,7 +160,7 @@ einem erneuten Lauf erhalten; neue oder entfallene Style-IDs werden gemeldet. |---|---|---| | Projektvariable | `basemap_auswertebereich` | `verfahrensgebiet` (Standard) oder `druckbereich` | | Projektvariable | `basemap_legende_erzeugen` | `1`/`0`, Standard `1` | -| Projektvariable | `basemap_legende_ausgeblendet` | JSON-Liste der zuletzt abgewählten Eintragsschlüssel (Vorbelegung) | +| Projektvariable | `basemap_legende_auswahl` | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` – zuletzt ab-/angewählte Einträge (Vorbelegung) | | Layout-Eigenschaft | `sn_basemap_legende`, `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` | Kennzeichen und letzte Einstellungen je Layout | | QSettings | `sn_basis/basemap_standardstil` | Name der Standard-Stilvorlage | | Öffentliche Funktion | `dialog_wrapper.ask_mehrfachauswahl(...)` | neuer allgemeiner Dialog | @@ -233,3 +233,31 @@ Ein Feature-Branch `feature/88-basemap-legende` in `Plugin_SN_Basis` von `unstab „kommt vor" unerheblich. - **Umfang:** Stilvorlagen sind fachlich eigenständig. Werden sie zu groß für einen PR, als eigenes Ticket abtrennen (Schritte 1–6 bleiben #88). + +## 10. Umsetzung und Abweichungen (02.10.2026) + +Umgesetzt in `Plugin_SN_Basis`, Branch `feature/88-basemap-legende` (drei Commits). Spike-Ergebnisse und +Begründung der Legendenvariante: ADR 0010. + +| Thema | Umsetzung | Abweichung vom Konzept | +|---|---|---| +| Legendendarstellung | Variante (b): eigenes Element „Legende Basemap“, Titel „Kartengrundlage (basemap.de)“ | Spike entschieden | +| Platzierung | unten an der Quellenangabe verankert, wächst nach oben, 1–3 Spalten (nie über den Seitenrand), sonst Hinweis | statt „unter der Hauptlegende“: deren Höhe steht erst nach dem Rendern fest | +| Mehrere Basemap-Layer | ausgewertet wird der erste sichtbare, Hinweis an den Nutzer | statt „je Layer eine Gruppe“ (vereinfacht) | +| Katalog-Generator | `sn_basis/modules/basemap_katalog.py` (nur Standardbibliothek, eigenständig aufrufbar) | statt Skript im Projektwissen – so mit der sn_basis-Suite getestet | +| Auswahl im Projekt | `basemap_legende_auswahl` (ab- und angewählte Einträge) | statt nur `…_ausgeblendet`, damit angewählte „Sonstiges“-Einträge gemerkt werden | +| Module | `basemap_katalog`, `basemap_kacheln`, `basemap_legende`, `basemap_legende_layout`, `basemap_stilvorlagen`, `ui/widgets/basemap_bereich` | Kachelauswertung ausgelagert (Dateigröße) | +| Einklappen | `QToolButton`-Muster wie Atlas-Bereich | `QgsCollapsibleGroupBox` nicht nötig | +| Stilvorlagen | QML (rund 3,7 MB je Vorlage) + JSON mit Legendenauswahl, im QGIS-Profil | wie geplant | + +**Katalog:** 434 darstellende Styles in 190 Einträgen; Beschriftungen (`Name_*`, Labeling), Nummernschilder +(`Nummer_*`), Hintergrund, Mittellinien und kleinmaßstäbige Ortspunkte ausgeschlossen. Erster fachlicher +Durchgang der Bezeichnungen ist gemacht; **die fachliche Abnahme steht aus.** + +**Bekannte Punkte für die Durchsicht:** +- „Platz“ (Verkehr, Siedlung) hat im Basemap-Stil eine weiße Füllung ohne Rand und ist in der Legende kaum zu sehen. +- „Durchlass, Düker“ und „Baumreihe“ zeigen in der Legende kein sichtbares Symbol (Ursache noch nicht geklärt). +- Gewässerbreiten sind zu „Fließgewässer (Linie)“ zusammengefasst; Symbol ist die 6-m-Linie. + +**Nicht geprüft:** QGIS 4 (Qt6) – lokal nicht verfügbar. QGIS 3.44.4 (Qt5) offscreen geprüft: Auswertung, +Dialog (echte Widgets), Legende im §-41-Layout, Stilvorlage speichern/laden am echten Layer, Basemap-Bereich. diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md index cc08474..c566d27 100644 --- a/docs/status/STATUS.md +++ b/docs/status/STATUS.md @@ -1,7 +1,7 @@ # Status des Projekts Kurz und aktuell. Wird bei jedem Merge und jeder Übergabe gepflegt (Skill `sn-uebergabe`). Älteres wandert nach unten -in „Erledigt" oder wird gelöscht. **Stand: 04.09.2026.** +in „Erledigt" oder wird gelöscht. **Stand: 02.10.2026.** ## Aktuelle Phase @@ -13,7 +13,7 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek | Person / Behörde | Schwerpunkt | Stand | |---|---|---| -| Daniel Helbig (LK Meißen) | Fachfunktionen Verfahrensgebiet, Plan41, VLN-API | aktiv | +| Daniel Helbig (LK Meißen) | Fachfunktionen Verfahrensgebiet, Plan41, VLN-API; Basemap-Legende im Druck (Basis #88, PR vorbereitet) | aktiv | | Michael Otto (LK Mittelsachsen) | Release-Infrastruktur, Linkliste, zuletzt Verfahrensgebiet (Shape-Laden, Beteiligung) | aktiv | | 22munzke | Druck/Layout-Anforderungen (Issues Basis #37–#44) | Issues | | weitere Behörden | Einstieg über dieses Repository | ab 09/2026 | @@ -63,6 +63,10 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek (fest verdrahteter NAS-Namespace `gid/6.0` in `grenzpunkt_alkis.py`, abgeflachte WFS-Feldnamen in `sql=`-Filtern). Vergleich 6↔7 und Anpassungsliste: `docs/konzept/09_alkis7_umstellung.md`. Nächster Schritt: Gitea-Issue in `Plugin_SN_Verfahrensgebiet`. +15. **Basemap-Legende im Druck (Basis #88).** Umgesetzt auf `Plugin_SN_Basis:feature/88-basemap-legende` + (Konzept `docs/konzept/10_basemap_legende.md`, ADR 0010), PR steht aus. Offen: **fachliche Abnahme des + Legendenkatalogs** `sn_basis/data/basemap_legende.json` (Bezeichnungen, Gruppen, Zusammenfassungen; drei + Einträge ohne gut sichtbares Symbol, siehe Konzept Abschnitt 10) und Test unter QGIS 4. **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45) und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`. diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index d09fbcb..8de74cd 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,20 +119,20 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
346Testfälle
-
301grün
-
37rot (Baseline)
+
648Testfälle
+
605grün
+
35rot (Baseline)
-
33%mit Docstring
-
31Testdateien
+
78%mit Docstring
+
55Testdateien
Verteilung je Paket
-
sn_basis14 Dateien · 94 Testfälle
-
sn_verfahrensgebiet12 Dateien · 208 Testfälle
+
sn_basis38 Dateien · 396 Testfälle
+
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,16 +147,245 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 14 Testdateien · 94 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 38 Testdateien · 396 Testfälle

- -

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 6
flowchart TD
+  

Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).

CloseEventTests 5
flowchart TD
+  root(["test_base_dockwidget.py"])
+  class root rootnode
+  root --> g1["CloseEventTests (5)"]
+  class g1 groupnode
+  g1 --> t1("ruft_cleanup_aller_tabs_auf")
+  g1 --> t2("ignoriert_tabs_ohne_cleanup")
+  g1 --> t3("fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter")
+  g1 --> t4("setzt_toolbar_action_zurueck")
+  g1 --> t5("ohne_action_wird_nichts_zurueckgesetzt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 8
flowchart TD
+  root(["test_basemap_anbindung.py"])
+  class root rootnode
+  root --> g1["TestMehrfachauswahl (1)"]
+  class g1 groupnode
+  g1 --> t1("mock_modus_liefert_vorauswahl_in_listenreihenfolge")
+  root --> g2["TestDruckanbindung (4)"]
+  class g2 groupnode
+  g2 --> t2("basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter")
+  g2 --> t3("layout_wird_markiert_und_legende_erzeugt")
+  g2 --> t4("fehler_der_basemap_legende_verhindert_die_vorlage_nicht")
+  g2 --> t5("druck_tab_aktualisiert_basemap_bereich_falls_vorhanden")
+  root --> g3["TestBasemapBereich (8)"]
+  class g3 groupnode
+  g3 --> t6("auswertebereich_wird_gespeichert")
+  g3 --> t7("aktualisieren_ohne_vorlage_warnt")
+  g3 --> t8("aktualisieren_wertet_gewaehlte_vorlage_aus")
+  g3 --> t9("stil_laden_uebernimmt_legendenauswahl")
+  g3 --> t10("stil_laden_bricht_bei_fremder_vorlage_ab")
+  g3 --> t11("vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt")
+  g3 --> t12("speichern_mit_aktueller_legendenauswahl")
+  g3 --> t13("stil_speichern_ohne_basemap_layer_warnt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grün
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grün
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grün
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grün
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grün
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grün
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grün
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grün
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grün
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grün
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grün

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
+  root(["test_basemap_kacheln.py"])
+  class root rootnode
+  root --> g1["TestSammleObjekte (4)"]
+  class g1 groupnode
+  g1 --> t1("gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt")
+  g1 --> t2("kacheln_ausserhalb_der_geometrie_werden_uebersprungen")
+  g1 --> t3("abbruch_liefert_none_und_hebt_auswahl_auf")
+  g1 --> t4("anzahl_kacheln_aus_kachelbereich")
+  root --> g2["TestZoomUndStile (2)"]
+  class g2 groupnode
+  g2 --> t5("zoomstufen_wie_im_qgis_renderer")
+  g2 --> t6("stile_aus_layer_liest_renderer_styles")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSammleObjekte
test_gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt
Prüft, dass Objekte mit gleichem Quell-Layer, Geometrietyp und Attributen über Kachelgrenzen hinweg nur einmal gezählt werden.grün
TestSammleObjekte
test_kacheln_ausserhalb_der_geometrie_werden_uebersprungen
Prüft, dass Kacheln ohne Schnitt mit dem Auswertebereich nicht abgefragt werden.grün
TestSammleObjekte
test_abbruch_liefert_none_und_hebt_auswahl_auf
Prüft, dass ein Abbruch über den Fortschritt None liefert und die Auswahl auf dem Basemap-Layer trotzdem aufgehoben wird.grün
TestSammleObjekte
test_anzahl_kacheln_aus_kachelbereich
Prüft, dass die Kachelzahl aus dem Kachelbereich der Geometrie berechnet wird.grün
TestZoomUndStile
test_zoomstufen_wie_im_qgis_renderer
Prüft, dass Kachelzoom begrenzt, Darstellungszoom unbegrenzt und der gebrochene Zoom aus dem Maßstab bestimmt werden.grün
TestZoomUndStile
test_stile_aus_layer_liest_renderer_styles
Prüft, dass Name, Quell-Layer, Geometrietyp, Zoombereich, Schalter und Filter der Renderer-Styles übernommen werden.grün

Tests für modules/basemap_katalog.py – Legendenkatalog aus der basemap.de-Style-JSON (Basis #88).

TestRendererStil 3TestErzeugeKatalog 6TestSymbolwahlUndPruefung 3
flowchart TD
+  root(["test_basemap_katalog.py"])
+  class root rootnode
+  root --> g1["TestRendererStil (3)"]
+  class g1 groupnode
+  g1 --> t1("beschriftung_und_3d_sind_keine_renderer_stile")
+  g1 --> t2("ausschluss_ueber_liste_und_praefix")
+  g1 --> t3("normalisierung_entfernt_variantenkennungen")
+  root --> g2["TestErzeugeKatalog (6)"]
+  class g2 groupnode
+  g2 --> t4("kontur_decker_und_bruecke_bilden_einen_eintrag")
+  g2 --> t5("zustand_im_bau_wird_eigener_eintrag")
+  g2 --> t6("punkt_und_flaeche_derselben_objektart_werden_zusammengefasst")
+  g2 --> t7("ausgeschlossene_stile_fehlen_im_katalog")
+  g2 --> t8("fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten")
+  g2 --> t9("entfallene_style_ids_werden_gemeldet")
+  root --> g3["TestSymbolwahlUndPruefung (3)"]
+  class g3 groupnode
+  g3 --> t10("symbol_stile_je_rolle_nur_grundvariante")
+  g3 --> t11("pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole")
+  g3 --> t12("ausgelieferter_katalog_ist_konsistent")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestRendererStil
test_beschriftung_und_3d_sind_keine_renderer_stile
Prüft, dass reine Beschriftungen und 3D-Gebäude nicht als Renderer-Style gelten, Icon-Symbole und Flächen schon.grün
TestRendererStil
test_ausschluss_ueber_liste_und_praefix
Prüft, dass Style-IDs über die Ausschlussliste und über Präfixe (Nummernschilder) ausgeschlossen werden.grün
TestRendererStil
test_normalisierung_entfernt_variantenkennungen
Prüft, dass Variantenkennungen wie Kontur, Brücke oder Breitenangaben beim Normalisieren einer Style-ID entfallen.grün
TestErzeugeKatalog
test_kontur_decker_und_bruecke_bilden_einen_eintrag
Prüft, dass Kontur-, Decker-, Fahrbahn- und Brückenvarianten derselben Straßenklasse genau einen Legendeneintrag bilden.grün
TestErzeugeKatalog
test_zustand_im_bau_wird_eigener_eintrag
Prüft, dass „im Bau“ einen eigenen Legendeneintrag mit Zustandsangabe in der Bezeichnung bekommt.grün
TestErzeugeKatalog
test_punkt_und_flaeche_derselben_objektart_werden_zusammengefasst
Prüft, dass das Kran-Symbol aus Bauwerkspunkt und Bauwerksfläche in einem Eintrag landet.grün
TestErzeugeKatalog
test_ausgeschlossene_stile_fehlen_im_katalog
Prüft, dass Hintergrund, Beschriftungen, Nummernschilder und 3D-Gebäude keinem Eintrag zugeordnet werden.grün
TestErzeugeKatalog
test_fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten
Prüft, dass Bezeichnung, Gruppe, Zusammenführung und Symbolwahl eines bestehenden Katalogs bei einem erneuten Lauf erhalten bleiben.grün
TestErzeugeKatalog
test_entfallene_style_ids_werden_gemeldet
Prüft, dass eine Style-ID, die in der neuen Style-JSON fehlt, als entfallen gemeldet und aus dem Katalog entfernt wird.grün
TestSymbolwahlUndPruefung
test_symbol_stile_je_rolle_nur_grundvariante
Prüft, dass für das Legendensymbol je Rolle (Kontur, Decker) nur die erste Grundvariante gewählt wird.grün
TestSymbolwahlUndPruefung
test_pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole
Prüft, dass die Katalogprüfung doppelt zugeordnete Style-IDs und Symbol-Styles außerhalb des Katalogs meldet.grün
TestSymbolwahlUndPruefung
test_ausgelieferter_katalog_ist_konsistent
Prüft, dass der ausgelieferte Katalog data/basemap_legende.json ohne Fehler geprüft wird und Kreisstraße mit Kontur und Decker enthält.grün

Tests für modules/basemap_legende.py – Zuordnung, Auswahl und Ablauf der Basemap-Legende (Basis #88).

TestStileUndEintraege 4TestAuswahlUndEinstellungen 6TestLayerErkennung 2TestAblauf 6
flowchart TD
+  root(["test_basemap_legende.py"])
+  class root rootnode
+  root --> g1["TestStileUndEintraege (4)"]
+  class g1 groupnode
+  g1 --> t1("stil_aktiv_nur_eingeschaltet_und_im_zoombereich")
+  g1 --> t2("getroffene_stile_pruefen_layer_geometrie_zoom_und_filter")
+  g1 --> t3("eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe")
+  g1 --> t4("ausgeschlossene_und_unbekannte_styles")
+  root --> g2["TestAuswahlUndEinstellungen (6)"]
+  class g2 groupnode
+  g2 --> t5("vorauswahl_bekannte_an_unbekannte_aus")
+  g2 --> t6("auswahl_wird_gemerkt_und_vorbelegt")
+  g2 --> t7("merken_ergaenzt_die_bisherige_auswahl")
+  g2 --> t8("kaputte_projektvariable_ergibt_leere_auswahl")
+  g2 --> t9("auswertebereich_standard_und_umstellung")
+  g2 --> t10("legende_erzeugen_standard_an")
+  root --> g3["TestLayerErkennung (2)"]
+  class g3 groupnode
+  g3 --> t11("erkennung_ueber_quelle_und_namen")
+  g3 --> t12("sichtbare_layer_aus_thema_feste_liste_oder_layerbaum")
+  root --> g4["TestAblauf (6)"]
+  class g4 groupnode
+  g4 --> t13("ohne_basemap_layer_passiert_nichts")
+  g4 --> t14("gewaehlte_eintraege_werden_geschrieben_und_gemerkt")
+  g4 --> t15("abbruch_im_dialog_aendert_nichts")
+  g4 --> t16("rueckfall_auf_druckbereich_wird_gemeldet")
+  g4 --> t17("ohne_objekte_keine_legende")
+  g4 --> t18("viele_kacheln_werden_vorher_bestaetigt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 3
flowchart TD
+  root(["test_basemap_legende_layout.py"])
+  class root rootnode
+  root --> g1["TestKennzeichenUndKarte (2)"]
+  class g1 groupnode
+  g1 --> t1("layout_kennzeichen_setzen_und_erkennen")
+  g1 --> t2("hauptkarte_ueber_id")
+  root --> g2["TestSymbolwahl (2)"]
+  class g2 groupnode
+  g2 --> t3("symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge")
+  g2 --> t4("ohne_aktive_symbol_stile_erster_getroffener_style")
+  root --> g3["TestHilfslayer (3)"]
+  class g3 groupnode
+  g3 --> t5("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
+  g3 --> t6("alte_hilfslayer_werden_entfernt")
+  g3 --> t7("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
+  root --> g4["TestPlatzierung (3)"]
+  class g4 groupnode
+  g4 --> t8("einspaltig_ueber_der_quellenangabe")
+  g4 --> t9("mehrspaltig_wenn_hoehe_nicht_reicht")
+  g4 --> t10("nie_breiter_als_der_seitenrand_mit_hinweis")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).

TestStilvorlagen 8
flowchart TD
+  root(["test_basemap_stilvorlagen.py"])
+  class root rootnode
+  root --> g1["TestStilvorlagen (8)"]
+  class g1 groupnode
+  g1 --> t1("speichern_und_auflisten")
+  g1 --> t2("mehrere_varianten_alphabetisch")
+  g1 --> t3("leerer_name_oder_fehler_beim_schreiben")
+  g1 --> t4("laden_setzt_stil_und_zeichnet_neu")
+  g1 --> t5("passung_meldet_fremde_styles")
+  g1 --> t6("standard_setzen_und_beim_loeschen_entfernen")
+  g1 --> t7("fehlende_begleitdatei_wird_toleriert")
+  g1 --> t8("ohne_ordner_leere_liste")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grün
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grün
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grün
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grün
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grün
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grün
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grün
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grün

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 8
flowchart TD
   root(["test_datagrabber.py"])
   class root rootnode
-  root --> g1["TestDataGrabber (6)"]
+  root --> g1["TestDataGrabber (8)"]
   class g1 groupnode
   g1 --> t1("analyze_source_type_detects_excel")
   g1 --> t2("analyze_source_type_returns_unknown_filetype")
@@ -164,20 +393,24 @@
   g1 --> t4("analyze_source_type_returns_unbekannte_quelle_on_full_failure")
   g1 --> t5("run_returns_success_summary_with_valid_rows")
   g1 --> t6("run_returns_failure_summary_without_rows")
+  g1 --> t7("aggregate_results_mit_zeilen_liefert_erfolgsergebnis")
+  g1 --> t8("aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
   class t6 ok
+  class t7 ok
+  class t8 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDataGrabber
test_analyze_source_type_detects_excel
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_returns_unknown_filetype
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_detects_service_when_file_check_fails
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_returns_unbekannte_quelle_on_full_failure
— (Docstring fehlt)grün
TestDataGrabber
test_run_returns_success_summary_with_valid_rows
— (Docstring fehlt)grün
TestDataGrabber
test_run_returns_failure_summary_without_rows
— (Docstring fehlt)grün

— (Docstring fehlt)

TestDateipruefer 6
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDataGrabber
test_analyze_source_type_detects_excel
Prüft, dass eine erreichbare .xlsx-Datei als Quelltyp 'excel' erkannt wird.grün
TestDataGrabber
test_analyze_source_type_returns_unknown_filetype
Prüft, dass eine erreichbare Datei mit unbekannter Endung als 'unbekannter_dateityp' eingestuft wird.grün
TestDataGrabber
test_analyze_source_type_detects_service_when_file_check_fails
Prüft, dass eine Quelle als 'dienst' erkannt wird, wenn die Dateiprüfung fehlschlägt, die URL aber erreichbar ist.grün
TestDataGrabber
test_analyze_source_type_returns_unbekannte_quelle_on_full_failure
Prüft, dass eine Quelle als 'unbekannte_quelle' eingestuft wird, wenn weder Datei- noch Linkprüfung erfolgreich sind.grün
TestDataGrabber
test_run_returns_success_summary_with_valid_rows
Prüft, dass run() bei erfolgreichem Excel-Import eine Erfolgsmeldung mit den geladenen Zeilen liefert.grün
TestDataGrabber
test_run_returns_failure_summary_without_rows
Prüft, dass run() bei nicht erkennbarer Quelle eine Fehlermeldung ohne Zeilen liefert.grün
TestDataGrabber
test_aggregate_results_mit_zeilen_liefert_erfolgsergebnis
Prüft, dass _aggregate_results bei vorhandenen Zeilen ein Erfolgsergebnis mit Zeilenzahl liefert.grün
TestDataGrabber
test_aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege
Prüft, dass _aggregate_results ohne Zeilen ok=False mit Aktion 'keine_validen_eintraege' liefert.grün

— (Docstring fehlt)

TestDateipruefer 17
flowchart TD
   root(["test_dateipruefer.py"])
   class root rootnode
-  root --> g1["TestDateipruefer (6)"]
+  root --> g1["TestDateipruefer (17)"]
   class g1 groupnode
   g1 --> t1("leereingabe_erlaubt")
   g1 --> t2("leereingabe_nicht_erlaubt")
@@ -185,6 +418,167 @@
   g1 --> t4("temporaer_erlaubt")
   g1 --> t5("datei_nicht_gefunden")
   g1 --> t6("datei_ok")
+  g1 --> t7("erkenne_dateityp_unterscheidet_excel_datenbank_und_unbekannt")
+  g1 --> t8("pfad_verbindet_relativen_pfad_mit_basispfad")
+  g1 --> t9("pfad_laesst_absoluten_pfad_unveraendert")
+  g1 --> t10("ist_gueltiger_gpkg_pfad_akzeptiert_vorhandenes_verzeichnis")
+  g1 --> t11("ist_gueltiger_gpkg_pfad_lehnt_falsche_endung_ab")
+  g1 --> t12("ist_gueltiger_gpkg_pfad_lehnt_fehlendes_verzeichnis_ab")
+  g1 --> t13("ist_leer_erkennt_none_und_nicht_string_als_leer")
+  g1 --> t14("ist_leer_erkennt_reinen_whitespace_als_leer")
+  g1 --> t15("ist_leer_meldet_pfad_mit_sichtbaren_zeichen_nicht_als_leer")
+  g1 --> t16("leere_eingabe_bevorzugt_leereingabe_erlaubt_vor_standarddatei")
+  g1 --> t17("leere_eingabe_bevorzugt_standarddatei_vor_temporaer_erlaubt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDateipruefer
test_leereingabe_erlaubt
Prüft, dass eine leere Pfadangabe bei leereingabe_erlaubt=True als 'leereingabe_erlaubt' ohne Kontext gemeldet wird.grün
TestDateipruefer
test_leereingabe_nicht_erlaubt
Prüft, dass eine leere Pfadangabe ohne leereingabe_erlaubt als 'leereingabe_nicht_erlaubt' abgelehnt wird.grün
TestDateipruefer
test_standarddatei_vorschlagen
Prüft, dass bei leerer Pfadangabe mit gesetzter Standarddatei 'standarddatei_vorschlagen' mit dem Standardpfad als Kontext geliefert wird.grün
TestDateipruefer
test_temporaer_erlaubt
Prüft, dass eine leere Pfadangabe bei temporaer_erlaubt=True als 'temporaer_erlaubt' ohne Kontext gemeldet wird.grün
TestDateipruefer
test_datei_nicht_gefunden
Prüft, dass ein nicht existierender Dateipfad als 'datei_nicht_gefunden' mit dem Pfad als Kontext abgelehnt wird.grün
TestDateipruefer
test_datei_ok
Prüft, dass ein existierender Dateipfad als 'ok' mit dem Pfad als Kontext bestätigt wird.grün
TestDateipruefer
test_erkenne_dateityp_unterscheidet_excel_datenbank_und_unbekannt
Prüft, dass erkenne_dateityp .xlsx als 'excel', .gpkg/.sqlite als 'datenbank' und Rest als 'unbekannt' erkennt.grün
TestDateipruefer
test_pfad_verbindet_relativen_pfad_mit_basispfad
Prüft, dass _pfad einen relativen Pfad mit dem Basisverzeichnis verbindet.grün
TestDateipruefer
test_pfad_laesst_absoluten_pfad_unveraendert
Prüft, dass _pfad einen bereits absoluten Pfad nicht mit dem Basisverzeichnis verbindet.grün
TestDateipruefer
test_ist_gueltiger_gpkg_pfad_akzeptiert_vorhandenes_verzeichnis
Prüft, dass ein .gpkg-Pfad mit vorhandenem Zielverzeichnis als gültig gilt.grün
TestDateipruefer
test_ist_gueltiger_gpkg_pfad_lehnt_falsche_endung_ab
Prüft, dass ein Pfad ohne .gpkg-Endung als ungültig gilt, auch bei vorhandenem Verzeichnis.grün
TestDateipruefer
test_ist_gueltiger_gpkg_pfad_lehnt_fehlendes_verzeichnis_ab
Prüft, dass ein .gpkg-Pfad in einem nicht vorhandenen Verzeichnis als ungültig gilt.grün
TestDateipruefer
test_ist_leer_erkennt_none_und_nicht_string_als_leer
Prüft, dass _ist_leer sowohl None als auch einen Nicht-String-Wert als leer meldet.grün
TestDateipruefer
test_ist_leer_erkennt_reinen_whitespace_als_leer
Prüft, dass _ist_leer eine Eingabe aus reinen Leerzeichen als leer meldet.grün
TestDateipruefer
test_ist_leer_meldet_pfad_mit_sichtbaren_zeichen_nicht_als_leer
Prüft, dass _ist_leer einen Pfad mit sichtbaren Zeichen nicht als leer meldet.grün
TestDateipruefer
test_leere_eingabe_bevorzugt_leereingabe_erlaubt_vor_standarddatei
Prüft, dass bei leerer Eingabe leereingabe_erlaubt Vorrang vor einer gesetzten Standarddatei hat.grün
TestDateipruefer
test_leere_eingabe_bevorzugt_standarddatei_vor_temporaer_erlaubt
Prüft, dass bei leerer Eingabe eine gesetzte Standarddatei Vorrang vor temporaer_erlaubt hat.grün

Tests für sn_basis/modules/Datenabruf.py (Datenabruf-Kernpipeline, Issue #63).

DetermineSpatialFilterTests 7BuildProviderUrlTests 9FetchFeaturesTests 5DatenabrufOrchestrierungTests 3
flowchart TD
+  root(["test_datenabruf.py"])
+  class root rootnode
+  root --> g1["DetermineSpatialFilterTests (7)"]
+  class g1 groupnode
+  g1 --> t1("raumfilter_ohne_liefert_none")
+  g1 --> t2("fehlender_layer_liefert_none")
+  g1 --> t3("unbekannter_raumfilter_liefert_none")
+  g1 --> t4("verfahrensgebiet_liefert_extent_und_crs")
+  g1 --> t5("verfahrensgebiet_ohne_extent_liefert_none")
+  g1 --> t6("pufferlayer_liefert_extent_des_pufferlayers")
+  g1 --> t7("pufferlayer_ohne_puffer_liefert_none")
+  root --> g2["BuildProviderUrlTests (9)"]
+  class g2 groupnode
+  g2 --> t8("wms_liefert_link_unveraendert")
+  g2 --> t9("url_praefix_wird_entfernt")
+  g2 --> t10("wfs_baut_standardparameter_und_bbox_mit_crs")
+  g2 --> t11("wfs_repariert_doppeltes_fragezeichen")
+  g2 --> t12("wfs_ohne_raumfilter_setzt_keine_bbox")
+  g2 --> t13("rest_provider_haengt_query_endpunkt_an")
+  g2 --> t14("rest_provider_mit_bbox_baut_geometry_envelope_und_srid")
+  g2 --> t15("default_provider_haengt_generischen_bbox_parameter_an")
+  g2 --> t16("default_provider_ohne_raumfilter_laesst_query_unveraendert")
+  root --> g3["FetchFeaturesTests (5)"]
+  class g3 groupnode
+  g3 --> t17("wms_liefert_keine_features_ohne_fehler")
+  g3 --> t18("abbruch_vor_abruf_liefert_fehlermeldung")
+  g3 --> t19("json_featurecollection_wird_geparst")
+  g3 --> t20("netzwerkfehler_wird_als_fehlermeldung_gemeldet")
+  g3 --> t21("wfs_xml_liefert_anzahl_aus_numberofmatched")
+  root --> g4["DatenabrufOrchestrierungTests (3)"]
+  class g4 groupnode
+  g4 --> t22("fehlende_pflichtfelder_werden_als_ungueltig_gemeldet")
+  g4 --> t23("wms_zeile_ohne_features_landet_in_ausserhalb")
+  g4 --> t24("abbruch_ueber_progress_stoppt_weitere_zeilen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
DetermineSpatialFilterTests
test_raumfilter_ohne_liefert_none
Prüft, dass Raumfilter 'ohne' unabhängig vom Layer immer None liefert.grün
DetermineSpatialFilterTests
test_fehlender_layer_liefert_none
Prüft, dass ohne Verfahrensgebiet-Layer kein Raumfilter gebaut wird.grün
DetermineSpatialFilterTests
test_unbekannter_raumfilter_liefert_none
Prüft, dass ein unbekannter Raumfilter-Wert None liefert, statt einen Fehler zu werfen.grün
DetermineSpatialFilterTests
test_verfahrensgebiet_liefert_extent_und_crs
Prüft, dass Raumfilter 'Verfahrensgebiet' Extent und CRS des Verfahrensgebiet-Layers liefert.grün
DetermineSpatialFilterTests
test_verfahrensgebiet_ohne_extent_liefert_none
Prüft, dass ein Verfahrensgebiet-Layer ohne ermittelbaren Extent None liefert.grün
DetermineSpatialFilterTests
test_pufferlayer_liefert_extent_des_pufferlayers
Prüft, dass Raumfilter 'Pufferlayer' Extent und CRS des erzeugten Pufferlayers liefert.grün
DetermineSpatialFilterTests
test_pufferlayer_ohne_puffer_liefert_none
Prüft, dass 'Pufferlayer' None liefert, wenn kein Pufferlayer erzeugt werden konnte.grün
BuildProviderUrlTests
test_wms_liefert_link_unveraendert
Prüft, dass bei Provider WMS der Link unverändert durchgereicht wird, auch mit Raumfilter.grün
BuildProviderUrlTests
test_url_praefix_wird_entfernt
Prüft, dass ein vorangestelltes 'url=' aus dem Link entfernt wird.grün
BuildProviderUrlTests
test_wfs_baut_standardparameter_und_bbox_mit_crs
Prüft, dass WFS-Abrufe Standardparameter, umbenanntes TYPENAMES und BBOX mit CRS-URN erhalten.grün
BuildProviderUrlTests
test_wfs_repariert_doppeltes_fragezeichen
Prüft, dass ein zweites '?' im WFS-Link zu '&' korrigiert wird, statt eine ungültige URL zu erzeugen.grün
BuildProviderUrlTests
test_wfs_ohne_raumfilter_setzt_keine_bbox
Prüft, dass WFS ohne Raumfilter (bbox_geom=None) keinen BBOX-Parameter setzt.grün
BuildProviderUrlTests
test_rest_provider_haengt_query_endpunkt_an
Prüft, dass REST/ArcGIS-Links ohne '/query'-Endpunkt automatisch ergänzt werden.grün
BuildProviderUrlTests
test_rest_provider_mit_bbox_baut_geometry_envelope_und_srid
Prüft, dass REST-Abrufe mit Raumfilter eine Geometry-Envelope und inSR/outSR aus dem CRS setzen.grün
BuildProviderUrlTests
test_default_provider_haengt_generischen_bbox_parameter_an
Prüft, dass ein unbekannter Provider mit Raumfilter einen generischen bbox-Parameter erhält.grün
BuildProviderUrlTests
test_default_provider_ohne_raumfilter_laesst_query_unveraendert
Prüft, dass ein unbekannter Provider ohne Raumfilter keinen bbox-Parameter ergänzt.grün
FetchFeaturesTests
test_wms_liefert_keine_features_ohne_fehler
Prüft, dass bei Provider WMS kein Featureabruf stattfindet und kein Fehler gemeldet wird.grün
FetchFeaturesTests
test_abbruch_vor_abruf_liefert_fehlermeldung
Prüft, dass ein bereits gesetzter Abbruch vor dem eigentlichen Abruf erkannt wird.grün
FetchFeaturesTests
test_json_featurecollection_wird_geparst
Prüft, dass eine JSON-FeatureCollection-Antwort in eine Feature-Liste umgewandelt wird.grün
FetchFeaturesTests
test_netzwerkfehler_wird_als_fehlermeldung_gemeldet
Prüft, dass ein fehlgeschlagener HTTP-Abruf eine Fehlermeldung statt einer Ausnahme liefert.grün
FetchFeaturesTests
test_wfs_xml_liefert_anzahl_aus_numberofmatched
Prüft, dass eine WFS-XML-Antwort ohne JSON die Trefferzahl aus numberOfMatched übernimmt.grün
DatenabrufOrchestrierungTests
test_fehlende_pflichtfelder_werden_als_ungueltig_gemeldet
Prüft, dass eine Zeile ohne Link/Provider mit Aktion 'pflichtfelder_fehlen' gemeldet und übersprungen wird.grün
DatenabrufOrchestrierungTests
test_wms_zeile_ohne_features_landet_in_ausserhalb
Prüft, dass eine erfolgreich abgerufene WMS-Zeile mit 0 Features als 'ausserhalb' gezählt wird.grün
DatenabrufOrchestrierungTests
test_abbruch_ueber_progress_stoppt_weitere_zeilen
Prüft, dass ein Abbruch über das Progress-Objekt die Verarbeitung weiterer Zeilen stoppt.grün

Tests für sn_basis/modules/Datenschreiber.py (Schreib-/Ladepipeline, Issue #63).

WriteLayerToGpkgTests 2ResolveStylePathTests 3SchreibeDatenTests 4LadeLayerTests 3SchreibeLogTests 2StoreStyleInGpkgTests 4
flowchart TD
+  root(["test_datenschreiber.py"])
+  class root rootnode
+  root --> g1["WriteLayerToGpkgTests (2)"]
+  class g1 groupnode
+  g1 --> t1("ungueltiger_layer_liefert_fehlermeldung")
+  g1 --> t2("gueltiger_layer_wird_ohne_fehler_geschrieben")
+  root --> g2["ResolveStylePathTests (3)"]
+  class g2 groupnode
+  g2 --> t3("leerer_stilpfad_liefert_none")
+  g2 --> t4("absoluter_vorhandener_stilpfad_wird_aufgeloest")
+  g2 --> t5("nicht_vorhandener_stilpfad_liefert_none")
+  root --> g3["SchreibeDatenTests (4)"]
+  class g3 groupnode
+  g3 --> t6("ohne_speicherort_wirft_valueerror")
+  g3 --> t7("eintrag_ohne_gueltigen_layer_wird_uebersprungen")
+  g3 --> t8("erfolgreicher_eintrag_liefert_layer_path_und_thema")
+  g3 --> t9("ohne_thema_wird_ident_als_layername_verwendet")
+  root --> g4["LadeLayerTests (3)"]
+  class g4 groupnode
+  g4 --> t10("ohne_layer_path_wird_uebersprungen")
+  g4 --> t11("gueltiger_layer_wird_dem_projekt_hinzugefuegt")
+  g4 --> t12("ungueltiger_layer_meldet_layer_nicht_gefunden")
+  root --> g5["SchreibeLogTests (2)"]
+  class g5 groupnode
+  g5 --> t13("ohne_speicherort_wirft_valueerror")
+  g5 --> t14("schreibt_json_datei_mit_ergebnissen_und_gibt_pfad_zurueck")
+  root --> g6["StoreStyleInGpkgTests (4)"]
+  class g6 groupnode
+  g6 --> t15("legt_tabelle_an_und_speichert_stil_als_default")
+  g6 --> t16("setzt_bisherigen_default_stil_desselben_layers_zurueck")
+  g6 --> t17("liest_geometriespalte_ueber_geometrycolumn_aus")
+  g6 --> t18("fehlender_stilpfad_meldet_fehler_ueber_pruefmanager_statt_absturz")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
WriteLayerToGpkgTests
test_ungueltiger_layer_liefert_fehlermeldung
Prüft, dass ein ungültiger Layer eine Fehlermeldung statt eines Absturzes liefert.grün
WriteLayerToGpkgTests
test_gueltiger_layer_wird_ohne_fehler_geschrieben
Prüft, dass ein gültiger Layer im Mock-Modus ohne Fehlermeldung geschrieben wird.grün
ResolveStylePathTests
test_leerer_stilpfad_liefert_none
Prüft, dass ein leerer oder fehlender Stilpfad None liefert.grün
ResolveStylePathTests
test_absoluter_vorhandener_stilpfad_wird_aufgeloest
Prüft, dass ein absoluter, vorhandener Stilpfad normalisiert und unverändert erkannt wird.grün
ResolveStylePathTests
test_nicht_vorhandener_stilpfad_liefert_none
Prüft, dass ein nicht vorhandener Stilpfad None liefert, statt einen ungültigen Pfad zurückzugeben.grün
SchreibeDatenTests
test_ohne_speicherort_wirft_valueerror
Prüft, dass schreibe_Daten ohne Speicherort einen ValueError wirft.grün
SchreibeDatenTests
test_eintrag_ohne_gueltigen_layer_wird_uebersprungen
Prüft, dass ein Eintrag ohne gültigen Layer übersprungen wird und ein Fehlerergebnis meldet.grün
SchreibeDatenTests
test_erfolgreicher_eintrag_liefert_layer_path_und_thema
Prüft, dass ein gültiger Layer einen Layer-Path mit GPKG-Pfad und Layername liefert.grün
SchreibeDatenTests
test_ohne_thema_wird_ident_als_layername_verwendet
Prüft, dass ohne passendes Thema in processed_results der ident als Layername verwendet wird.grün
LadeLayerTests
test_ohne_layer_path_wird_uebersprungen
Prüft, dass ein Eintrag ohne layer_path übersprungen wird, ohne einen Fehler zu melden.grün
LadeLayerTests
test_gueltiger_layer_wird_dem_projekt_hinzugefuegt
Prüft, dass ein gültig ladbarer Layer ohne Fehlermeldung verarbeitet wird.grün
LadeLayerTests
test_ungueltiger_layer_meldet_layer_nicht_gefunden
Prüft, dass ein ungültig ladbarer Layer als 'layer_nicht_gefunden' gemeldet wird.grün
SchreibeLogTests
test_ohne_speicherort_wirft_valueerror
Prüft, dass schreibe_log ohne Speicherort einen ValueError wirft.grün
SchreibeLogTests
test_schreibt_json_datei_mit_ergebnissen_und_gibt_pfad_zurueck
Prüft, dass schreibe_log eine lesbare JSON-Log-Datei erzeugt und deren Pfad zurückgibt.grün
StoreStyleInGpkgTests
test_legt_tabelle_an_und_speichert_stil_als_default
Prüft, dass _store_style_in_gpkg die layer_styles-Tabelle anlegt und den Stil als Default speichert.grün
StoreStyleInGpkgTests
test_setzt_bisherigen_default_stil_desselben_layers_zurueck
Prüft, dass _store_style_in_gpkg den useAsDefault-Wert vorhandener Stile desselben Layers zurücksetzt.grün
StoreStyleInGpkgTests
test_liest_geometriespalte_ueber_geometrycolumn_aus
Prüft, dass _store_style_in_gpkg die Geometriespalte eines Layers mit geometryColumn() übernimmt.grün
StoreStyleInGpkgTests
test_fehlender_stilpfad_meldet_fehler_ueber_pruefmanager_statt_absturz
Prüft, dass ein nicht lesbarer Stilpfad über den Pruefmanager gemeldet wird statt eine Exception zu werfen.grün

Tests für sn_basis/ui/dockmanager.py (nur ein Dock gleichzeitig sichtbar, Issue #65).

DockManagerShowTests 6
flowchart TD
+  root(["test_dockmanager.py"])
+  class root rootnode
+  root --> g1["DockManagerShowTests (6)"]
+  class g1 groupnode
+  g1 --> t1("none_dock_meldet_fehler_und_zeigt_nichts_an")
+  g1 --> t2("setzt_objectname_wenn_leer")
+  g1 --> t3("entfernt_andere_sn_docks_aber_nicht_sich_selbst")
+  g1 --> t4("zeigt_neues_dock_mit_default_area_an")
+  g1 --> t5("fehler_beim_entfernen_wird_als_warnung_gemeldet")
+  g1 --> t6("fehler_beim_anzeigen_wird_gemeldet")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -195,7 +589,32 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDateipruefer
test_leereingabe_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_leereingabe_nicht_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_standarddatei_vorschlagen
— (Docstring fehlt)grün
TestDateipruefer
test_temporaer_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_datei_nicht_gefunden
— (Docstring fehlt)grün
TestDateipruefer
test_datei_ok
— (Docstring fehlt)grün

Unit-Tests für `sn_basis.functions.laenderkennung`.

TestGetLaenderkennung 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
DockManagerShowTests
test_none_dock_meldet_fehler_und_zeigt_nichts_an
Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt.grün
DockManagerShowTests
test_setzt_objectname_wenn_leer
Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält.grün
DockManagerShowTests
test_entfernt_andere_sn_docks_aber_nicht_sich_selbst
Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben.grün
DockManagerShowTests
test_zeigt_neues_dock_mit_default_area_an
Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_entfernen_wird_als_warnung_gemeldet
Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_anzeigen_wird_gemeldet
Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird.grün

Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).

ImportXlsxTests 5ExcelImporterKonstruktorTests 2
flowchart TD
+  root(["test_excel_importer.py"])
+  class root rootnode
+  root --> g1["ImportXlsxTests (5)"]
+  class g1 groupnode
+  g1 --> t1("erfolgreicher_import_liefert_zeilen_als_dicts")
+  g1 --> t2("fehlende_datei_liefert_leere_liste")
+  g1 --> t3("arbeitsblatt_ohne_header_liefert_leere_liste_und_meldet_kein_header")
+  g1 --> t4("header_zeile_nur_leere_werte_liefert_kein_header")
+  g1 --> t5("nicht_lesbare_datei_liefert_leere_liste_und_meldet_read_error")
+  root --> g2["ExcelImporterKonstruktorTests (2)"]
+  class g2 groupnode
+  g2 --> t6("ohne_dateipfad_wirft_valueerror")
+  g2 --> t7("ohne_pruefmanager_wirft_valueerror")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
ImportXlsxTests
test_erfolgreicher_import_liefert_zeilen_als_dicts
Prüft, dass eine gültige Excel-Datei als Liste von Dicts mit den Header-Spalten geliefert wird.grün
ImportXlsxTests
test_fehlende_datei_liefert_leere_liste
Prüft, dass eine nicht vorhandene Datei eine leere Liste liefert, statt eine Ausnahme zu werfen.grün
ImportXlsxTests
test_arbeitsblatt_ohne_header_liefert_leere_liste_und_meldet_kein_header
Prüft, dass ein leeres Arbeitsblatt ohne Header-Zeile 'kein_header' meldet und eine leere Liste liefert.grün
ImportXlsxTests
test_header_zeile_nur_leere_werte_liefert_kein_header
Prüft, dass eine Header-Zeile aus ausschließlich leeren Zellen ebenfalls als 'kein_header' gilt.grün
ImportXlsxTests
test_nicht_lesbare_datei_liefert_leere_liste_und_meldet_read_error
Prüft, dass eine Datei mit ungültigem Excel-Inhalt 'read_error' meldet und eine leere Liste liefert.grün
ExcelImporterKonstruktorTests
test_ohne_dateipfad_wirft_valueerror
Prüft, dass ExcelImporter ohne Dateipfad einen ValueError wirft.grün
ExcelImporterKonstruktorTests
test_ohne_pruefmanager_wirft_valueerror
Prüft, dass ExcelImporter ohne Pruefmanager einen ValueError wirft.grün

Unit-Tests für `sn_basis.functions.laenderkennung`.

TestGetLaenderkennung 5
flowchart TD
   root(["test_laenderkennung.py"])
   class root rootnode
   root --> g1["TestGetLaenderkennung (5)"]
@@ -214,7 +633,89 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetLaenderkennung
test_bekannter_praefix_gibt_kennung_zurueck
— (Docstring fehlt)grün
TestGetLaenderkennung
test_praefix_gross_klein_unabhaengig
— (Docstring fehlt)grün
TestGetLaenderkennung
test_unbekannter_praefix_wirft_keyerror
— (Docstring fehlt)grün
TestGetLaenderkennung
test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet
— (Docstring fehlt)grün
TestGetLaenderkennung
test_mapping_enthaelt_sachsen
— (Docstring fehlt)grün

— (Docstring fehlt)

TestLayerpruefer 10
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetLaenderkennung
test_bekannter_praefix_gibt_kennung_zurueck
Prüft, dass der Präfix 'sn' die ALKIS-Länderkennung '14' für Sachsen liefert.grün
TestGetLaenderkennung
test_praefix_gross_klein_unabhaengig
Prüft, dass die Präfixsuche groß-/kleinschreibungsunabhängig ist.grün
TestGetLaenderkennung
test_unbekannter_praefix_wirft_keyerror
Prüft, dass ein unbekannter Präfix einen KeyError auslöst.grün
TestGetLaenderkennung
test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet
Prüft, dass ohne Argument der Präfix aus dem Modulnamen des Aufrufers abgeleitet wird.grün
TestGetLaenderkennung
test_mapping_enthaelt_sachsen
Prüft, dass die Mapping-Tabelle LAENDERKENNUNGEN einen Eintrag für Sachsen ('sn' -> '14') enthält.grün

Tests für sn_basis/functions/landkreise.py::_lade_landkreise (Issue #65).

LadeLandkreiseTests 2
flowchart TD
+  root(["test_landkreise.py"])
+  class root rootnode
+  root --> g1["LadeLandkreiseTests (2)"]
+  class g1 groupnode
+  g1 --> t1("laedt_die_echte_landkreisliste_nicht_leer")
+  g1 --> t2("fehlende_datei_liefert_leere_liste")
+  class t1 ok
+  class t2 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
LadeLandkreiseTests
test_laedt_die_echte_landkreisliste_nicht_leer
Prüft, dass _lade_landkreise die echte landkreise.json als nicht-leere Liste von (Kennzahl, Name)-Tupeln liefert.grün
LadeLandkreiseTests
test_fehlende_datei_liefert_leere_liste
Prüft, dass eine nicht lesbare Datenquelle eine leere Liste liefert, statt eine Ausnahme zu werfen.grün

Tests für sn_basis/modules/LayerLoader.py (Layer erzeugen/filtern/laden, Issue #63).

CreateLayerTests 7TransformGeometryTests 3TransformExtentTests 2FilterByExtentTests 6FilterByLayerTests 4AddToProjectTests 3
flowchart TD
+  root(["test_layerloader.py"])
+  class root rootnode
+  root --> g1["CreateLayerTests (7)"]
+  class g1 groupnode
+  g1 --> t1("wfs_baut_uri_ohne_getfeature_parameter")
+  g1 --> t2("wfs_mit_url_praefix_wird_nicht_doppelt_gesetzt")
+  g1 --> t3("wms_baut_url_praefix_und_nutzt_rasterlayer")
+  g1 --> t4("rest_featureserver_ergaenzt_layer_index")
+  g1 --> t5("ogr_provider_nutzt_link_direkt")
+  g1 --> t6("ungueltiger_layer_meldet_layer_nicht_verfuegbar")
+  g1 --> t7("ausnahme_beim_erstellen_wird_abgefangen")
+  root --> g2["TransformGeometryTests (3)"]
+  class g2 groupnode
+  g2 --> t8("fehlender_layer_liefert_geometrie_unveraendert")
+  g2 --> t9("gleiche_crs_liefert_geometrie_unveraendert")
+  g2 --> t10("unterschiedliche_crs_klont_und_transformiert_geometrie")
+  root --> g3["TransformExtentTests (2)"]
+  class g3 groupnode
+  g3 --> t11("gleiche_crs_liefert_extent_unveraendert")
+  g3 --> t12("unterschiedliche_crs_nutzt_coordinate_transform")
+  root --> g4["FilterByExtentTests (6)"]
+  class g4 groupnode
+  g4 --> t13("ungueltiger_layer_wird_unveraendert_zurueckgegeben")
+  g4 --> t14("ohne_extent_wird_layer_unveraendert_zurueckgegeben")
+  g4 --> t15("falscher_layertyp_wird_unveraendert_zurueckgegeben")
+  g4 --> t16("keine_treffer_liefert_none")
+  g4 --> t17("abbruch_liefert_none_und_meldet_needs_user_action")
+  g4 --> t18("erfolgreicher_filter_liefert_neuen_layer_mit_features")
+  root --> g5["FilterByLayerTests (4)"]
+  class g5 groupnode
+  g5 --> t19("ungueltiger_layer_oder_filterlayer_wird_unveraendert_zurueckgegeben")
+  g5 --> t20("filterlayer_ohne_features_liefert_none")
+  g5 --> t21("erfolgreicher_geometriefilter_liefert_neuen_layer")
+  g5 --> t22("keine_ueberschneidung_liefert_none")
+  root --> g6["AddToProjectTests (3)"]
+  class g6 groupnode
+  g6 --> t23("gueltiger_layer_wird_dem_projekt_hinzugefuegt")
+  g6 --> t24("ungueltiger_layer_wird_nicht_hinzugefuegt")
+  g6 --> t25("none_layer_wird_nicht_hinzugefuegt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CreateLayerTests
test_wfs_baut_uri_ohne_getfeature_parameter
Prüft, dass für WFS die GetFeature-spezifischen Parameter aus der URI entfernt werden.grün
CreateLayerTests
test_wfs_mit_url_praefix_wird_nicht_doppelt_gesetzt
Prüft, dass ein bereits vorhandenes 'url='-Präfix im WFS-Link nicht verdoppelt wird.grün
CreateLayerTests
test_wms_baut_url_praefix_und_nutzt_rasterlayer
Prüft, dass WMS-Layer über QgsRasterLayer mit 'url='-Präfix erzeugt werden.grün
CreateLayerTests
test_rest_featureserver_ergaenzt_layer_index
Prüft, dass ein REST-Link auf '.../FeatureServer' automatisch um '/0' ergänzt wird.grün
CreateLayerTests
test_ogr_provider_nutzt_link_direkt
Prüft, dass ein OGR-Provider den Link unverändert an QgsVectorLayer übergibt.grün
CreateLayerTests
test_ungueltiger_layer_meldet_layer_nicht_verfuegbar
Prüft, dass ein ungültig erzeugter Layer als 'layer_nicht_verfuegbar' gemeldet wird und None liefert.grün
CreateLayerTests
test_ausnahme_beim_erstellen_wird_abgefangen
Prüft, dass eine Ausnahme beim Layer-Erstellen abgefangen und als Fehlerergebnis gemeldet wird.grün
TransformGeometryTests
test_fehlender_layer_liefert_geometrie_unveraendert
Prüft, dass ohne Quell- oder Ziel-Layer die Geometrie unverändert zurückgegeben wird.grün
TransformGeometryTests
test_gleiche_crs_liefert_geometrie_unveraendert
Prüft, dass bei identischem CRS von Quelle und Ziel keine Transformation stattfindet.grün
TransformGeometryTests
test_unterschiedliche_crs_klont_und_transformiert_geometrie
Prüft, dass bei unterschiedlichem CRS eine geklonte, transformierte Geometrie geliefert wird.grün
TransformExtentTests
test_gleiche_crs_liefert_extent_unveraendert
Prüft, dass bei identischem CRS von Quelle und Ziel der Extent unverändert bleibt.grün
TransformExtentTests
test_unterschiedliche_crs_nutzt_coordinate_transform
Prüft, dass bei unterschiedlichem CRS der Extent über QgsCoordinateTransform umgerechnet wird.grün
FilterByExtentTests
test_ungueltiger_layer_wird_unveraendert_zurueckgegeben
Prüft, dass ein ungültiger Layer von filter_by_extent unverändert zurückgegeben wird.grün
FilterByExtentTests
test_ohne_extent_wird_layer_unveraendert_zurueckgegeben
Prüft, dass ohne Extent (None) der Layer unverändert zurückgegeben wird.grün
FilterByExtentTests
test_falscher_layertyp_wird_unveraendert_zurueckgegeben
Prüft, dass ein Nicht-Vektor-Layer (z. B. Rasterlayer) unverändert zurückgegeben wird.grün
FilterByExtentTests
test_keine_treffer_liefert_none
Prüft, dass ein Raumfilter ohne Treffer None statt eines leeren Layers liefert.grün
FilterByExtentTests
test_abbruch_liefert_none_und_meldet_needs_user_action
Prüft, dass ein Abbruch während des Raumfilters None liefert und gemeldet wird.grün
FilterByExtentTests
test_erfolgreicher_filter_liefert_neuen_layer_mit_features
Prüft, dass ein erfolgreicher BBOX-Filter einen neuen Speicherlayer mit den gefundenen Features liefert.grün
FilterByLayerTests
test_ungueltiger_layer_oder_filterlayer_wird_unveraendert_zurueckgegeben
Prüft, dass ein ungültiger Haupt- oder Filter-Layer unverändert zurückgegeben wird.grün
FilterByLayerTests
test_filterlayer_ohne_features_liefert_none
Prüft, dass ein Filter-Layer ohne Features (leere Vereinigungsgeometrie) None liefert.grün
FilterByLayerTests
test_erfolgreicher_geometriefilter_liefert_neuen_layer
Prüft, dass ein erfolgreicher Geometriefilter einen neuen Speicherlayer mit den passenden Features liefert.grün
FilterByLayerTests
test_keine_ueberschneidung_liefert_none
Prüft, dass ohne überschneidende Features None statt eines leeren Layers geliefert wird.grün
AddToProjectTests
test_gueltiger_layer_wird_dem_projekt_hinzugefuegt
Prüft, dass ein gültiger Layer über QgsProject.instance().addMapLayer hinzugefügt wird.grün
AddToProjectTests
test_ungueltiger_layer_wird_nicht_hinzugefuegt
Prüft, dass ein ungültiger Layer nicht ans Projekt übergeben wird.grün
AddToProjectTests
test_none_layer_wird_nicht_hinzugefuegt
Prüft, dass ein fehlender Layer (None) nicht ans Projekt übergeben wird.grün

— (Docstring fehlt)

TestLayerpruefer 10
flowchart TD
   root(["test_layerpruefer.py"])
   class root rootnode
   root --> g1["TestLayerpruefer (10)"]
@@ -243,26 +744,147 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLayerpruefer
test_layer_exists
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_unsichtbar
— (Docstring fehlt)grün
TestLayerpruefer
test_falscher_layertyp
— (Docstring fehlt)grün
TestLayerpruefer
test_falscher_geotyp
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_leer
— (Docstring fehlt)grün
TestLayerpruefer
test_falsches_crs
— (Docstring fehlt)grün
TestLayerpruefer
test_felder_fehlen
— (Docstring fehlt)grün
TestLayerpruefer
test_datenquelle_unerwartet
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_nicht_editierbar
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_ok
— (Docstring fehlt)grün

— (Docstring fehlt)

TestLinkpruefer 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLayerpruefer
test_layer_exists
Prüft, dass ein nicht vorhandener Layer als 'layer_nicht_gefunden' abgelehnt wird.grün
TestLayerpruefer
test_layer_unsichtbar
Prüft, dass ein unsichtbarer Layer bei geforderter Sichtbarkeit als 'layer_unsichtbar' abgelehnt wird.grün
TestLayerpruefer
test_falscher_layertyp
Prüft, dass ein Layer mit abweichendem Layertyp (Raster statt Vektor) als 'falscher_layertyp' abgelehnt wird.grün
TestLayerpruefer
test_falscher_geotyp
Prüft, dass ein Layer mit abweichendem Geometrietyp (Punkt statt Polygon) als 'falscher_geotyp' abgelehnt wird.grün
TestLayerpruefer
test_layer_leer
Prüft, dass ein Layer ohne Features als 'layer_leer' abgelehnt wird.grün
TestLayerpruefer
test_falsches_crs
Prüft, dass ein Layer mit abweichendem Koordinatenbezugssystem als 'falsches_crs' abgelehnt wird.grün
TestLayerpruefer
test_felder_fehlen
Prüft, dass ein Layer mit fehlenden Pflichtfeldern als 'felder_fehlen' abgelehnt wird.grün
TestLayerpruefer
test_datenquelle_unerwartet
Prüft, dass eine Datenquelle außerhalb der erlaubten Liste als 'datenquelle_unerwartet' abgelehnt wird.grün
TestLayerpruefer
test_layer_nicht_editierbar
Prüft, dass ein nicht editierbarer Layer bei geforderter Editierbarkeit als 'layer_nicht_editierbar' abgelehnt wird.grün
TestLayerpruefer
test_layer_ok
Prüft, dass ein Layer, der alle Anforderungen erfüllt, als 'ok' bestätigt wird.grün

— (Docstring fehlt)

TestLinkpruefer 6
flowchart TD
   root(["test_linkpruefer.py"])
   class root rootnode
-  root --> g1["TestLinkpruefer (5)"]
+  root --> g1["TestLinkpruefer (6)"]
   class g1 groupnode
-  g1 --> t1("remote_link_ok")
-  g1 --> t2("remote_link_error")
-  g1 --> t3("remote_link_network_error")
-  g1 --> t4("local_link_not_found")
-  g1 --> t5("local_link_ok")
+  g1 --> t1("leere_eingabe")
+  g1 --> t2("remote_link_ok")
+  g1 --> t3("remote_link_error")
+  g1 --> t4("remote_link_network_error")
+  g1 --> t5("local_link_not_found")
+  g1 --> t6("local_link_ok")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLinkpruefer
test_remote_link_ok
— (Docstring fehlt)grün
TestLinkpruefer
test_remote_link_error
— (Docstring fehlt)grün
TestLinkpruefer
test_remote_link_network_error
— (Docstring fehlt)grün
TestLinkpruefer
test_local_link_not_found
— (Docstring fehlt)grün
TestLinkpruefer
test_local_link_ok
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLinkpruefer
test_leere_eingabe
Prüft, dass eine leere Eingabe ohne Netzwerk-/Dateizugriff als 'leer' abgelehnt wird.grün
TestLinkpruefer
test_remote_link_ok
Prüft, dass eine erreichbare Remote-URL (HTTP HEAD ohne Fehler) als 'ok' bestätigt wird.grün
TestLinkpruefer
test_remote_link_error
Prüft, dass eine Remote-URL mit Fehlerantwort als 'url_nicht_erreichbar' abgelehnt wird.grün
TestLinkpruefer
test_remote_link_network_error
Prüft, dass eine ausbleibende Netzwerkantwort (None) als 'netzwerkfehler' gemeldet wird.grün
TestLinkpruefer
test_local_link_not_found
Prüft, dass ein nicht existierender lokaler Pfad als 'pfad_nicht_gefunden' abgelehnt wird.grün
TestLinkpruefer
test_local_link_ok
Prüft, dass ein existierender lokaler Pfad als 'ok' bestätigt wird.grün

Tests für sn_basis/functions/ly_existence_wrapper.py::layer_is_valid/layer_is_usable (Issue #65).

LayerIsValidTests 5LayerIsUsableTests 3
flowchart TD
+  root(["test_ly_existence_wrapper.py"])
+  class root rootnode
+  root --> g1["LayerIsValidTests (5)"]
+  class g1 groupnode
+  g1 --> t1("none_ist_nicht_gueltig")
+  g1 --> t2("objekt_ohne_isvalid_ist_nicht_gueltig")
+  g1 --> t3("gueltiger_layer_liefert_true")
+  g1 --> t4("ungueltiger_layer_liefert_false")
+  g1 --> t5("ausnahme_bei_isvalid_liefert_false")
+  root --> g2["LayerIsUsableTests (3)"]
+  class g2 groupnode
+  g2 --> t6("none_ist_nicht_nutzbar")
+  g2 --> t7("gueltiger_layer_ist_nutzbar")
+  g2 --> t8("ungueltiger_layer_ist_nicht_nutzbar")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
LayerIsValidTests
test_none_ist_nicht_gueltig
Prüft, dass layer_is_valid für None False liefert.grün
LayerIsValidTests
test_objekt_ohne_isvalid_ist_nicht_gueltig
Prüft, dass ein Objekt ohne isValid()-Methode als ungültig gilt.grün
LayerIsValidTests
test_gueltiger_layer_liefert_true
Prüft, dass ein Layer mit isValid()==True als gültig erkannt wird.grün
LayerIsValidTests
test_ungueltiger_layer_liefert_false
Prüft, dass ein Layer mit isValid()==False als ungültig erkannt wird.grün
LayerIsValidTests
test_ausnahme_bei_isvalid_liefert_false
Prüft, dass eine Ausnahme in isValid() abgefangen wird und False liefert.grün
LayerIsUsableTests
test_none_ist_nicht_nutzbar
Prüft, dass layer_is_usable für None False liefert.grün
LayerIsUsableTests
test_gueltiger_layer_ist_nutzbar
Prüft, dass ein vorhandener, gültiger Layer als nutzbar gilt.grün
LayerIsUsableTests
test_ungueltiger_layer_ist_nicht_nutzbar
Prüft, dass ein vorhandener, aber ungültiger Layer als nicht nutzbar gilt.grün

Tests für sn_basis/functions/ly_style_wrapper.py::apply_style_from_path (Issue #65).

ApplyStyleFromPathTests 4
flowchart TD
+  root(["test_ly_style_wrapper.py"])
+  class root rootnode
+  root --> g1["ApplyStyleFromPathTests (4)"]
+  class g1 groupnode
+  g1 --> t1("fehlender_layer_liefert_false")
+  g1 --> t2("erfolgreiches_laden_liefert_true_und_triggert_repaint")
+  g1 --> t3("fehlgeschlagenes_laden_liefert_false_ohne_repaint")
+  g1 --> t4("ausnahme_beim_laden_wird_abgefangen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
ApplyStyleFromPathTests
test_fehlender_layer_liefert_false
Prüft, dass ein fehlender Layer (None) False liefert, ohne einen Stil zu laden.grün
ApplyStyleFromPathTests
test_erfolgreiches_laden_liefert_true_und_triggert_repaint
Prüft, dass ein erfolgreich geladener Stil True liefert und den Layer neu zeichnet.grün
ApplyStyleFromPathTests
test_fehlgeschlagenes_laden_liefert_false_ohne_repaint
Prüft, dass ein fehlgeschlagenes Laden False liefert, ohne den Layer neu zu zeichnen.grün
ApplyStyleFromPathTests
test_ausnahme_beim_laden_wird_abgefangen
Prüft, dass eine Ausnahme beim Laden des Stils abgefangen wird und False liefert.grün

Tests für sn_basis/functions/message_wrapper.py (Issue #65).

GetMessageBarTests 2PushMessageTests 2
flowchart TD
+  root(["test_message_wrapper.py"])
+  class root rootnode
+  root --> g1["GetMessageBarTests (2)"]
+  class g1 groupnode
+  g1 --> t1("liefert_objekt_mit_pushmessage_methode")
+  g1 --> t2("faellt_bei_fehlerhaftem_iface_auf_mock_bar_zurueck")
+  root --> g2["PushMessageTests (2)"]
+  class g2 groupnode
+  g2 --> t3("liefert_dict_mit_titel_text_level_und_dauer")
+  g2 --> t4("fehler_in_der_message_bar_wird_abgefangen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
GetMessageBarTests
test_liefert_objekt_mit_pushmessage_methode
Prüft, dass _get_message_bar ein Objekt mit aufrufbarer pushMessage()-Methode liefert.grün
GetMessageBarTests
test_faellt_bei_fehlerhaftem_iface_auf_mock_bar_zurueck
Prüft, dass ein Fehler beim Zugriff auf iface.messageBar() auf eine funktionierende Mock-MessageBar zurückfällt.grün
PushMessageTests
test_liefert_dict_mit_titel_text_level_und_dauer
Prüft, dass push_message im Mock-Modus ein Dict mit den übergebenen Werten liefert.grün
PushMessageTests
test_fehler_in_der_message_bar_wird_abgefangen
Prüft, dass eine fehlschlagende MessageBar push_message nicht zum Absturz bringt, sondern None liefert.grün

Tests für sn_basis/ui/navigation.py::Navigation (Issue #67, ohne init_ui – s. Docstring dort).

AddActionTests 2FinalizeMenuAndToolbarTests 2SetActivePluginTests 2RemoveActionTests 3RemoveAllTests 3
flowchart TD
+  root(["test_navigation.py"])
+  class root rootnode
+  root --> g1["AddActionTests (2)"]
+  class g1 groupnode
+  g1 --> t1("ohne_plugin_group_liefert_none")
+  g1 --> t2("erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu")
+  root --> g2["FinalizeMenuAndToolbarTests (2)"]
+  class g2 groupnode
+  g2 --> t3("ohne_menu_oder_toolbar_tut_nichts")
+  g2 --> t4("sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu")
+  root --> g3["SetActivePluginTests (2)"]
+  class g3 groupnode
+  g3 --> t5("setzt_nur_aktive_action_auf_checked")
+  g3 --> t6("ohne_aktive_action_setzt_alle_auf_false")
+  root --> g4["RemoveActionTests (3)"]
+  class g4 groupnode
+  g4 --> t7("none_action_tut_nichts")
+  g4 --> t8("entfernt_action_aus_menu_toolbar_und_liste")
+  g4 --> t9("ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste")
+  root --> g5["RemoveAllTests (3)"]
+  class g5 groupnode
+  g5 --> t10("entfernt_menu_toolbar_und_setzt_zustand_zurueck")
+  g5 --> t11("loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt")
+  g5 --> t12("ohne_menu_und_toolbar_ruft_wrapper_nicht_auf")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
AddActionTests
test_ohne_plugin_group_liefert_none
Prüft, dass add_action ohne initialisierte Aktionsgruppe None liefert und nichts anlegt.grün
AddActionTests
test_erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu
Prüft, dass add_action eine Action erzeugt, ihr die Gruppe zuweist und sie priorisiert in der Liste ablegt.grün
FinalizeMenuAndToolbarTests
test_ohne_menu_oder_toolbar_tut_nichts
Prüft, dass finalize_menu_and_toolbar ohne Menü oder Toolbar keine Ausnahme wirft.grün
FinalizeMenuAndToolbarTests
test_sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu
Prüft, dass finalize_menu_and_toolbar Aktionen nach Priorität sortiert in Menü und Toolbar einträgt.grün
SetActivePluginTests
test_setzt_nur_aktive_action_auf_checked
Prüft, dass set_active_plugin nur die übergebene Action markiert, alle anderen abwählt.grün
SetActivePluginTests
test_ohne_aktive_action_setzt_alle_auf_false
Prüft, dass set_active_plugin ohne aktive Action alle bestehenden Actions abwählt.grün
RemoveActionTests
test_none_action_tut_nichts
Prüft, dass remove_action mit None keine Ausnahme wirft und die Liste unverändert lässt.grün
RemoveActionTests
test_entfernt_action_aus_menu_toolbar_und_liste
Prüft, dass remove_action die Action aus Menü, Toolbar und der internen Liste entfernt.grün
RemoveActionTests
test_ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste
Prüft, dass remove_action ohne Menü/Toolbar trotzdem aus der internen Liste entfernt.grün
RemoveAllTests
test_entfernt_menu_toolbar_und_setzt_zustand_zurueck
Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt.grün
RemoveAllTests
test_loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt
Prüft, dass remove_all deleteLater() auf Menü und Toolbar aufruft, damit sie den Qt-Objektbaum tatsächlich verlassen (Issue #72).grün
RemoveAllTests
test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf
Prüft, dass remove_all ohne vorhandenes Menü/Toolbar die Wrapper nicht aufruft, aber die Liste leert.grün

Tests für sn_basis/functions/os_wrapper.py::is_case_sensitive_fs (Issue #67).

IsCaseSensitiveFsTests 3
flowchart TD
+  root(["test_os_wrapper.py"])
+  class root rootnode
+  root --> g1["IsCaseSensitiveFsTests (3)"]
+  class g1 groupnode
+  g1 --> t1("windows_ist_nicht_case_sensitiv")
+  g1 --> t2("macos_ist_nicht_case_sensitiv")
+  g1 --> t3("linux_ist_case_sensitiv")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
   root(["test_print_layout.py"])
   class root rootnode
   root --> g1["TestPrintLayout (5)"]
@@ -281,7 +903,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLayout
test_create_legend_item_links_map_and_enables_map_filtering
— (Docstring fehlt)grün
TestPrintLayout
test_create_legend_item_does_not_copy_project_layers_into_legend_model
— (Docstring fehlt)grün
TestPrintLayout
test_configure_legend_size_uses_content_adaptive_size
— (Docstring fehlt)grün
TestPrintLayout
test_configure_hauptkarte_grids_uses_wrapper_constants
— (Docstring fehlt)grün
TestPrintLayout
test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPrintLogic 17
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLayout
test_create_legend_item_links_map_and_enables_map_filtering
Prüft, dass eine erzeugte Legende mit der übergebenen Karte verknüpft ist und automatisch nach deren Sichtbarkeit gefiltert wird.grün
TestPrintLayout
test_create_legend_item_does_not_copy_project_layers_into_legend_model
Prüft, dass eine neu erzeugte Legende keine Projekt-Layer in ihr Legendenmodell übernimmt (leere Root-Gruppe).grün
TestPrintLayout
test_configure_legend_size_uses_content_adaptive_size
Prüft, dass die Legendengröße inhaltsadaptiv bestimmt wird (kein manuelles Resize) und die ermittelte Höhe zurückgegeben wird.grün
TestPrintLayout
test_configure_hauptkarte_grids_uses_wrapper_constants
Prüft, dass das Gitternetz der Hauptkarte mit den erwarteten Stil-, Rahmen- und Beschriftungskonstanten (nur rechts/unten beschriftet, Schriftgröße und Hintergrund) konfiguriert wird.grün
TestPrintLayout
test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme
Prüft, dass ein einseitiges Layout die Hauptkarte auf das übergebene Kartenthema festlegt und die Legende mit dieser Karte verknüpft bleibt.grün

— (Docstring fehlt)

TestPrintLogic 17
flowchart TD
   root(["test_print_logic.py"])
   class root rootnode
   root --> g1["TestPrintLogic (17)"]
@@ -324,10 +946,148 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLogic
test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_escalates_to_a3
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_uses_a4_as_minimum_sheet
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_limits_hauptkarte_to_a3
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_returns_none_if_a3_is_too_small
— (Docstring fehlt)grün
TestPrintLogic
test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent
— (Docstring fehlt)grün
TestPrintLogic
test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm
— (Docstring fehlt)grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_for_small_page
— (Docstring fehlt)grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_keeps_large_page
— (Docstring fehlt)grün
TestPrintLogic
test_does_plot_fit_page_true_for_standard_a4
— (Docstring fehlt)grün
TestPrintLogic
test_does_plot_fit_page_false_if_objects_exceed_page
— (Docstring fehlt)grün
TestPrintLogic
test_set_druckbereich_accepts_wie_kartenfenster
— (Docstring fehlt)grün
TestPrintLogic
test_get_canvas_extent_returns_valid_extent
— (Docstring fehlt)grün
TestPrintLogic
test_resolve_extent_uses_canvas_extent_without_buffer
— (Docstring fehlt)grün
TestPrintLogic
test_resolve_extent_uses_rectangle_extent_without_buffer
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPruefmanager 14
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLogic
test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent
Prüft, dass für einen kleinen Kartenausschnitt (Baufreigabe) das Format DIN A4 ohne Fallback gewählt wird.grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_escalates_to_a3
Prüft, dass ein größerer Kartenausschnitt (Baufreigabe) auf DIN A3 eskaliert wird.grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback
Prüft, dass bei auf DIN A4 begrenztem Höchstformat ein eigentlich A3-großer Ausschnitt mit Fallback-Kennzeichnung auf A4 reduziert wird.grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent
Prüft, dass ein Ausschnitt, der auch DIN A3 übersteigt, mit Fallback-Kennzeichnung auf DIN A3 begrenzt wird.grün
TestPrintLogic
test_select_standard_page_layout_uses_a4_as_minimum_sheet
Prüft, dass für einen kleinen Ausschnitt trotz Höchstgrenze DIN A3 mindestens DIN A4 als Blattgröße gewählt wird.grün
TestPrintLogic
test_select_standard_page_layout_limits_hauptkarte_to_a3
Prüft, dass ein die Fläche voll ausnutzender Ausschnitt auf DIN A3 als größtes zulässiges Format begrenzt wird.grün
TestPrintLogic
test_select_standard_page_layout_returns_none_if_a3_is_too_small
Prüft, dass kein Layout gewählt wird (None), wenn der Ausschnitt selbst DIN A3 als Höchstformat übersteigt.grün
TestPrintLogic
test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent
Prüft, dass für einen kleinen Ausschnitt DIN A4 gewählt und das Einpassen (force_fit) erzwungen wird.grün
TestPrintLogic
test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm
Prüft, dass bei aktiviertem Formfaktor (Endlosrolle) die Seitenbreite auf die maximale Rollenbreite (2000 mm) begrenzt wird.grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_for_small_page
Prüft, dass eine zu kleine Seite auf die A4-Mindestgröße angehoben und als erzwungen (forced) markiert wird.grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_keeps_large_page
Prüft, dass eine bereits ausreichend große Seite (A3) unverändert bleibt und nicht als erzwungen markiert wird.grün
TestPrintLogic
test_does_plot_fit_page_true_for_standard_a4
Prüft, dass ein Kartenobjekt, das in eine DIN A4-Seite passt, als passend erkannt wird.grün
TestPrintLogic
test_does_plot_fit_page_false_if_objects_exceed_page
Prüft, dass ein Kartenobjekt, das die Seitenbreite überschreitet, als nicht passend erkannt wird.grün
TestPrintLogic
test_set_druckbereich_accepts_wie_kartenfenster
Prüft, dass die Auswahl 'wie Kartenfenster' als Projektvariable 'druckbereich' gespeichert wird.grün
TestPrintLogic
test_get_canvas_extent_returns_valid_extent
Prüft, dass der aktuelle Kartenausschnitt des Kartenfensters unverändert zurückgegeben wird.grün
TestPrintLogic
test_resolve_extent_uses_canvas_extent_without_buffer
Prüft, dass bei Druckbereich 'wie Kartenfenster' der Kartenausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird.grün
TestPrintLogic
test_resolve_extent_uses_rectangle_extent_without_buffer
Prüft, dass bei Druckbereich 'Rechteck' der gezeichnete Ausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird.grün

Tests für sn_basis/ui/tabs/print_tab.py::PrintTab (Issue #67).

SetServicesTests 2RestoreStateTests 2OnDruckpufferClickedTests 3OnVorlageErstellenTests 2ProjectSignalsTests 3CleanupTests 1ToggleAtlasTests 2RestoreAtlasLayerSelectionTests 2ThemeCollectionSignalsTests 3RefreshThemaComboLiveTests 2GetGespeicherteThemenTests 2GetCurrentCanvasScaleTests 3OnAtlasLayerChangedTests 3OnAuswahlAsAtlasClickedTests 2PopulateAtlasLayerComboTests 2OnAtlasErzeugenChangedTests 3
flowchart TD
+  root(["test_print_tab.py"])
+  class root rootnode
+  root --> g1["SetServicesTests (2)"]
+  class g1 groupnode
+  g1 --> t1("ruft_alle_set_methoden_mit_combo_werten_auf")
+  g1 --> t2("ohne_combos_ruft_keine_set_methode_auf")
+  root --> g2["RestoreStateTests (2)"]
+  class g2 groupnode
+  g2 --> t3("gueltige_gespeicherte_werte_werden_uebernommen")
+  g2 --> t4("ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck")
+  root --> g3["OnDruckpufferClickedTests (3)"]
+  class g3 groupnode
+  g3 --> t5("gueltige_zahl_setzt_variable_und_button_text")
+  g3 --> t6("abgebrochen_aendert_nichts")
+  g3 --> t7("ungueltige_zahl_zeigt_hinweis_und_setzt_nichts")
+  root --> g4["OnVorlageErstellenTests (2)"]
+  class g4 groupnode
+  g4 --> t8("ruft_druckvorlage_anlegen_mit_combo_werten_auf")
+  g4 --> t9("ohne_logic_tut_nichts")
+  root --> g5["ProjectSignalsTests (3)"]
+  class g5 groupnode
+  g5 --> t10("connect_verbindet_alle_drei_signale")
+  g5 --> t11("disconnect_trennt_alle_drei_signale")
+  g5 --> t12("instance_wirft_ausnahme_wird_abgefangen")
+  root --> g6["CleanupTests (1)"]
+  class g6 groupnode
+  g6 --> t13("ruft_beide_disconnect_methoden_auf")
+  root --> g7["ToggleAtlasTests (2)"]
+  class g7 groupnode
+  g7 --> t14("checked_true_setzt_arrow_down_und_zeigt_inhalt")
+  g7 --> t15("checked_false_setzt_arrow_right_und_versteckt_inhalt")
+  root --> g8["RestoreAtlasLayerSelectionTests (2)"]
+  class g8 groupnode
+  g8 --> t16("vorhandene_layer_id_setzt_layer_in_combo")
+  g8 --> t17("leere_gespeicherte_layer_id_tut_nichts")
+  root --> g9["ThemeCollectionSignalsTests (3)"]
+  class g9 groupnode
+  g9 --> t18("connect_verbindet_alle_signale")
+  g9 --> t19("connect_ist_idempotent_wenn_schon_verbunden")
+  g9 --> t20("disconnect_trennt_alle_signale")
+  root --> g10["RefreshThemaComboLiveTests (2)"]
+  class g10 groupnode
+  g10 --> t21("erhaelt_vorherige_auswahl_wenn_noch_vorhanden")
+  g10 --> t22("faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden")
+  root --> g11["GetGespeicherteThemenTests (2)"]
+  class g11 groupnode
+  g11 --> t23("dedupliziert_und_filtert_leere_eintraege")
+  g11 --> t24("fehler_liefert_leere_liste")
+  root --> g12["GetCurrentCanvasScaleTests (3)"]
+  class g12 groupnode
+  g12 --> t25("liefert_skalierung_als_float")
+  g12 --> t26("ohne_canvas_liefert_none")
+  g12 --> t27("skalierung_null_liefert_none")
+  root --> g13["OnAtlasLayerChangedTests (3)"]
+  class g13 groupnode
+  g13 --> t28("none_oder_leer_setzt_variable_leer")
+  g13 --> t29("layer_objekt_mit_id_methode_setzt_dessen_id")
+  g13 --> t30("string_fallback_sucht_layer_ueber_namen")
+  root --> g14["OnAuswahlAsAtlasClickedTests (2)"]
+  class g14 groupnode
+  g14 --> t31("erfolgreicher_layer_aktualisiert_atlas_ui")
+  g14 --> t32("ohne_erzeugten_layer_tut_nichts_weiter")
+  root --> g15["PopulateAtlasLayerComboTests (2)"]
+  class g15 groupnode
+  g15 --> t33("befuellt_combo_ohne_druckrechteck_und_leere_namen")
+  g15 --> t34("ohne_atlas_layer_combo_tut_nichts")
+  root --> g16["OnAtlasErzeugenChangedTests (3)"]
+  class g16 groupnode
+  g16 --> t35("aktiviert_speichert_variable_und_aktiviert_widgets")
+  g16 --> t36("deaktiviert_speichert_variable_und_deaktiviert_widgets")
+  g16 --> t37("ohne_widgets_wirft_keine_ausnahme")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 ok
+  class t28 ok
+  class t29 ok
+  class t30 ok
+  class t31 ok
+  class t32 ok
+  class t33 ok
+  class t34 ok
+  class t35 ok
+  class t36 ok
+  class t37 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
SetServicesTests
test_ruft_alle_set_methoden_mit_combo_werten_auf
Prüft, dass set_services alle set_*-Methoden von PrintLogic mit den aktuellen Combo-Werten aufruft.grün
SetServicesTests
test_ohne_combos_ruft_keine_set_methode_auf
Prüft, dass set_services ohne vorhandene Combos keine set_*-Methode von PrintLogic aufruft.grün
RestoreStateTests
test_gueltige_gespeicherte_werte_werden_uebernommen
Prüft, dass _restore_state gültige gespeicherte Werte in die jeweiligen Widgets übernimmt.grün
RestoreStateTests
test_ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck
Prüft, dass _restore_state ohne (gültige) gespeicherte Werte auf die Default-Konstanten zurückfällt.grün
OnDruckpufferClickedTests
test_gueltige_zahl_setzt_variable_und_button_text
Prüft, dass eine bestätigte, gültige Zahl den Druckpuffer speichert und den Button-Text aktualisiert.grün
OnDruckpufferClickedTests
test_abgebrochen_aendert_nichts
Prüft, dass ein abgebrochener Dialog den Druckpuffer nicht verändert.grün
OnDruckpufferClickedTests
test_ungueltige_zahl_zeigt_hinweis_und_setzt_nichts
Prüft, dass eine nicht-numerische Eingabe einen Hinweis zeigt und den Druckpuffer nicht setzt.grün
OnVorlageErstellenTests
test_ruft_druckvorlage_anlegen_mit_combo_werten_auf
Prüft, dass _on_vorlage_erstellen druckvorlage_anlegen mit den aktuellen Combo-Werten aufruft.grün
OnVorlageErstellenTests
test_ohne_logic_tut_nichts
Prüft, dass _on_vorlage_erstellen ohne gesetzte PrintLogic keine Ausnahme wirft.grün
ProjectSignalsTests
test_connect_verbindet_alle_drei_signale
Prüft, dass _connect_project_signals alle drei Projektsignale mit _on_project_changed verbindet.grün
ProjectSignalsTests
test_disconnect_trennt_alle_drei_signale
Prüft, dass _disconnect_project_signals alle drei zuvor verbundenen Projektsignale trennt.grün
ProjectSignalsTests
test_instance_wirft_ausnahme_wird_abgefangen
Prüft, dass eine Ausnahme bei QgsProject.instance() in beiden Signalmethoden abgefangen wird.grün
CleanupTests
test_ruft_beide_disconnect_methoden_auf
Prüft, dass cleanup sowohl die Theme- als auch die Projektsignale trennt.grün
ToggleAtlasTests
test_checked_true_setzt_arrow_down_und_zeigt_inhalt
Prüft, dass _toggle_atlas bei checked=True den Pfeil nach unten dreht und den Inhalt einblendet.grün
ToggleAtlasTests
test_checked_false_setzt_arrow_right_und_versteckt_inhalt
Prüft, dass _toggle_atlas bei checked=False den Pfeil nach rechts dreht und den Inhalt ausblendet.grün
RestoreAtlasLayerSelectionTests
test_vorhandene_layer_id_setzt_layer_in_combo
Prüft, dass eine gespeicherte Layer-ID den zugehörigen Layer im Atlas-Dropdown auswählt.grün
RestoreAtlasLayerSelectionTests
test_leere_gespeicherte_layer_id_tut_nichts
Prüft, dass eine leere gespeicherte Layer-ID das Projekt gar nicht erst anfragt.grün
ThemeCollectionSignalsTests
test_connect_verbindet_alle_signale
Prüft, dass _connect_theme_collection_signals alle Signale der Theme-Collection verbindet.grün
ThemeCollectionSignalsTests
test_connect_ist_idempotent_wenn_schon_verbunden
Prüft, dass _connect_theme_collection_signals bei bereits verbundenem Signal nichts erneut tut.grün
ThemeCollectionSignalsTests
test_disconnect_trennt_alle_signale
Prüft, dass _disconnect_theme_collection_signals alle verbundenen Theme-Signale trennt und zurücksetzt.grün
RefreshThemaComboLiveTests
test_erhaelt_vorherige_auswahl_wenn_noch_vorhanden
Prüft, dass _refresh_thema_combo_live die vorherige Auswahl beibehält, wenn sie noch existiert.grün
RefreshThemaComboLiveTests
test_faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden
Prüft, dass _refresh_thema_combo_live auf 'wie Kartenfenster' zurückfällt, wenn die Auswahl verschwunden ist.grün
GetGespeicherteThemenTests
test_dedupliziert_und_filtert_leere_eintraege
Prüft, dass _get_gespeicherte_themen Duplikate und leere/None-Einträge entfernt.grün
GetGespeicherteThemenTests
test_fehler_liefert_leere_liste
Prüft, dass ein Fehler beim Zugriff auf die Theme-Collection eine leere Liste liefert.grün
GetCurrentCanvasScaleTests
test_liefert_skalierung_als_float
Prüft, dass _get_current_canvas_scale die Canvas-Skalierung als float liefert.grün
GetCurrentCanvasScaleTests
test_ohne_canvas_liefert_none
Prüft, dass _get_current_canvas_scale ohne verfügbares Kartenfenster None liefert.grün
GetCurrentCanvasScaleTests
test_skalierung_null_liefert_none
Prüft, dass eine Skalierung von 0 als None statt als 0.0 gilt.grün
OnAtlasLayerChangedTests
test_none_oder_leer_setzt_variable_leer
Prüft, dass None oder ein leerer Wert die Abdeckungslayer-Variable leeren.grün
OnAtlasLayerChangedTests
test_layer_objekt_mit_id_methode_setzt_dessen_id
Prüft, dass ein echtes Layer-Objekt (mit id()-Methode) dessen ID speichert.grün
OnAtlasLayerChangedTests
test_string_fallback_sucht_layer_ueber_namen
Prüft, dass ohne id()-Methode über den Layernamen im Projekt gesucht und dessen ID gespeichert wird.grün
OnAuswahlAsAtlasClickedTests
test_erfolgreicher_layer_aktualisiert_atlas_ui
Prüft, dass ein erfolgreich erzeugter Atlas-Layer den Atlas-Bereich öffnet und ins Dropdown übernimmt.grün
OnAuswahlAsAtlasClickedTests
test_ohne_erzeugten_layer_tut_nichts_weiter
Prüft, dass ohne erzeugten Atlas-Layer keine UI-Aktualisierung stattfindet.grün
PopulateAtlasLayerComboTests
test_befuellt_combo_ohne_druckrechteck_und_leere_namen
Prüft, dass _populate_atlas_layer_combo alle Layernamen außer 'Druckrechteck' und leeren Namen einträgt.grün
PopulateAtlasLayerComboTests
test_ohne_atlas_layer_combo_tut_nichts
Prüft, dass _populate_atlas_layer_combo ohne Atlas-Dropdown keine Ausnahme wirft.grün
OnAtlasErzeugenChangedTests
test_aktiviert_speichert_variable_und_aktiviert_widgets
Prüft, dass ein aktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button aktiviert.grün
OnAtlasErzeugenChangedTests
test_deaktiviert_speichert_variable_und_deaktiviert_widgets
Prüft, dass ein deaktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button deaktiviert.grün
OnAtlasErzeugenChangedTests
test_ohne_widgets_wirft_keine_ausnahme
Prüft, dass _on_atlas_erzeugen_changed ohne Atlas-Combo/-Button keine Ausnahme wirft.grün

Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung/dateipfad (Issue #65/#67).

IstVerfahrensDbAktionTests 2ErlaubtePersistierungTests 2DateipfadTests 2
flowchart TD
+  root(["test_pruef_ergebnis.py"])
+  class root rootnode
+  root --> g1["IstVerfahrensDbAktionTests (2)"]
+  class g1 groupnode
+  g1 --> t1("liefert_true_fuer_alle_verfahrens_db_aktionen")
+  g1 --> t2("liefert_false_fuer_andere_aktionen")
+  root --> g2["ErlaubtePersistierungTests (2)"]
+  class g2 groupnode
+  g2 --> t3("liefert_true_fuer_erzeugen_ueberschreiben_anhaengen")
+  g2 --> t4("liefert_false_fuer_ueberspringen_und_reines_existieren")
+  root --> g3["DateipfadTests (2)"]
+  class g3 groupnode
+  g3 --> t5("liefert_path_wenn_kontext_ein_path_ist")
+  g3 --> t6("liefert_none_wenn_kontext_kein_path_ist")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IstVerfahrensDbAktionTests
test_liefert_true_fuer_alle_verfahrens_db_aktionen
Prüft, dass ist_verfahrens_db_aktion für alle vier Verfahrens-DB-Aktionscodes True liefert.grün
IstVerfahrensDbAktionTests
test_liefert_false_fuer_andere_aktionen
Prüft, dass ist_verfahrens_db_aktion für Nicht-Verfahrens-DB-Aktionen und ohne Aktion False liefert.grün
ErlaubtePersistierungTests
test_liefert_true_fuer_erzeugen_ueberschreiben_anhaengen
Prüft, dass erlaubte_persistierung für Erzeugen/Überschreiben/Anhängen True liefert.grün
ErlaubtePersistierungTests
test_liefert_false_fuer_ueberspringen_und_reines_existieren
Prüft, dass erlaubte_persistierung bei Überspringen und reinem 'datei_existiert' False liefert.grün
DateipfadTests
test_liefert_path_wenn_kontext_ein_path_ist
Prüft, dass dateipfad den Kontext liefert, wenn dieser bereits ein Path-Objekt ist.grün
DateipfadTests
test_liefert_none_wenn_kontext_kein_path_ist
Prüft, dass dateipfad None liefert, wenn der Kontext kein Path-Objekt ist (z. B. String oder Layer).grün

— (Docstring fehlt)

TestPruefmanager 26
flowchart TD
   root(["test_pruefmanager.py"])
   class root rootnode
-  root --> g1["TestPruefmanager (14)"]
+  root --> g1["TestPruefmanager (26)"]
   class g1 groupnode
   g1 --> t1("ok")
   g1 --> t2("leereingabe_erlaubt_ja")
@@ -343,6 +1103,71 @@
   g1 --> t12("datei_existiert_append")
   g1 --> t13("datei_existiert_cancel")
   g1 --> t14("datei_existiert_in_non_qgis_mode_returns_input")
+  g1 --> t15("verfahrensgebiet_layer_existiert_ergaenzen")
+  g1 --> t16("verfahrensgebiet_layer_existiert_abbrechen")
+  g1 --> t17("verfahrensgebiet_layer_existiert_entfernen")
+  g1 --> t18("verfahrensgebiet_layer_existiert_ersetzen")
+  g1 --> t19("verfahrensgebiet_layer_existiert_in_non_qgis_mode_returns_input")
+  g1 --> t20("report_error_kritische_aktion_ruft_error_auf")
+  g1 --> t21("report_error_warn_aktion_ruft_warning_auf")
+  g1 --> t22("report_error_unbekannte_aktion_faellt_auf_warning_zurueck")
+  g1 --> t23("frage_ja_nein_im_qgis_modus_fragt_nutzer")
+  g1 --> t24("frage_ja_nein_ausserhalb_qgis_liefert_default_ohne_rueckfrage")
+  g1 --> t25("frage_text_im_qgis_modus_fragt_nutzer")
+  g1 --> t26("frage_text_ausserhalb_qgis_liefert_default_ohne_rueckfrage")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPruefmanager
test_ok
Prüft, dass ein bereits erfolgreiches Prüfergebnis ohne Nutzerinteraktion als 'ok' durchgereicht wird.grün
TestPruefmanager
test_leereingabe_erlaubt_ja
Prüft, dass bei erlaubter leerer Eingabe eine Ja-Antwort des Nutzers die Entscheidung 'ok' liefert.grün
TestPruefmanager
test_leereingabe_erlaubt_nein
Prüft, dass bei erlaubter leerer Eingabe eine Nein-Antwort die Aktion 'leereingabe_erlaubt' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_standarddatei_vorschlagen_ja
Prüft, dass eine Ja-Antwort auf den Vorschlag der Standarddatei die Entscheidung 'ok' mit dem Standardpfad als Kontext liefert.grün
TestPruefmanager
test_standarddatei_vorschlagen_nein
Prüft, dass eine Nein-Antwort auf den Vorschlag der Standarddatei die Aktion 'standarddatei_vorschlagen' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_temporaer_erlaubt_ja
Prüft, dass eine Ja-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erzeugen' auslöst.grün
TestPruefmanager
test_temporaer_erlaubt_nein
Prüft, dass eine Nein-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erlaubt' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_layer_unsichtbar_ja
Prüft, dass eine Ja-Antwort bei unsichtbarem Layer den Layer sichtbar schaltet und die Entscheidung 'ok' liefert.grün
TestPruefmanager
test_layer_unsichtbar_nein
Prüft, dass eine Nein-Antwort bei unsichtbarem Layer die Aktion 'layer_unsichtbar' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_unbekannte_aktion
Prüft, dass eine unbekannte Aktion eine Warnung auslöst und unverarbeitet als Fehlschlag zurückgegeben wird.grün
TestPruefmanager
test_datei_existiert_overwrite
Prüft, dass die Nutzerwahl 'überschreiben' bei bestehender Datei zur Aktion 'datei_existiert_ueberschreiben' führt.grün
TestPruefmanager
test_datei_existiert_append
Prüft, dass die Nutzerwahl 'anhängen' bei bestehender Datei zur Aktion 'datei_existiert_anhaengen' führt.grün
TestPruefmanager
test_datei_existiert_cancel
Prüft, dass die Nutzerwahl 'abbrechen' bei bestehender Datei zur Aktion 'datei_existiert_ueberspringen' führt.grün
TestPruefmanager
test_datei_existiert_in_non_qgis_mode_returns_input
Prüft, dass außerhalb des QGIS-Modus bei bestehender Datei keine Rückfrage erfolgt, sondern die ursprüngliche Aktion unentschieden zurückgegeben wird.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_ergaenzen
Prüft, dass die Nutzerwahl 'ergänzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ergaenzen' führt.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_abbrechen
Prüft, dass die Nutzerwahl 'abbrechen' bei bestehendem Verfahrensgebiet ok=False mit Aktion 'verfahrensgebiet_abbrechen' liefert.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_entfernen
Prüft, dass die Nutzerwahl 'entfernen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_entfernen' führt.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_ersetzen
Prüft, dass die Nutzerwahl 'ersetzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ersetzen' führt.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_in_non_qgis_mode_returns_input
Prüft, dass außerhalb des QGIS-Modus bei bestehendem Verfahrensgebiet keine Rückfrage erfolgt.grün
TestPruefmanager
test_report_error_kritische_aktion_ruft_error_auf
Prüft, dass eine kritische Aktion (z. B. 'netzwerkfehler') über error() gemeldet wird, nicht über warning().grün
TestPruefmanager
test_report_error_warn_aktion_ruft_warning_auf
Prüft, dass eine Warn-Aktion (z. B. 'datei_nicht_gefunden') über warning() gemeldet wird, nicht über error().grün
TestPruefmanager
test_report_error_unbekannte_aktion_faellt_auf_warning_zurueck
Prüft, dass eine nicht eingestufte Aktion als Fallback über warning() gemeldet wird.grün
TestPruefmanager
test_frage_ja_nein_im_qgis_modus_fragt_nutzer
Prüft, dass frage_ja_nein im QGIS-Modus den Nutzer fragt und dessen Antwort liefert.grün
TestPruefmanager
test_frage_ja_nein_ausserhalb_qgis_liefert_default_ohne_rueckfrage
Prüft, dass frage_ja_nein außerhalb des QGIS-Modus den Default-Wert liefert, ohne den Nutzer zu fragen.grün
TestPruefmanager
test_frage_text_im_qgis_modus_fragt_nutzer
Prüft, dass frage_text im QGIS-Modus den Nutzer fragt und Text plus OK-Status liefert.grün
TestPruefmanager
test_frage_text_ausserhalb_qgis_liefert_default_ohne_rueckfrage
Prüft, dass frage_text außerhalb des QGIS-Modus den Standardtext mit OK=True liefert, ohne den Nutzer zu fragen.grün

Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg/create_buffer_layer (Issue #65/#67).

PolygonZuLinieTests 7LayerExistsInGpkgTests 4CreateBufferLayerTests 3
flowchart TD
+  root(["test_qgiscore_wrapper_helpers.py"])
+  class root rootnode
+  root --> g1["PolygonZuLinieTests (7)"]
+  class g1 groupnode
+  g1 --> t1("none_liefert_none")
+  g1 --> t2("leere_geometrie_liefert_none")
+  g1 --> t3("stufe1_nutzt_boundary_wenn_vorhanden")
+  g1 --> t4("stufe2_extrahiert_ringe_bei_einfachem_polygon")
+  g1 --> t5("stufe2_extrahiert_alle_ringe_bei_multipolygon")
+  g1 --> t6("stufe3_fallback_wenn_keine_ringe_extrahierbar")
+  g1 --> t7("alle_stufen_scheitern_liefert_none")
+  root --> g2["LayerExistsInGpkgTests (4)"]
+  class g2 groupnode
+  g2 --> t8("fehlende_datei_liefert_false")
+  g2 --> t9("leerer_pfad_oder_layername_liefert_false")
+  g2 --> t10("vorhandener_layer_liefert_true")
+  g2 --> t11("fehlender_layer_liefert_false")
+  root --> g3["CreateBufferLayerTests (3)"]
+  class g3 groupnode
+  g3 --> t12("ohne_qgis_liefert_none")
+  g3 --> t13("fehlender_source_layer_liefert_none")
+  g3 --> t14("source_layer_ohne_getfeatures_liefert_none")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -361,7 +1186,44 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPruefmanager
test_ok
— (Docstring fehlt)grün
TestPruefmanager
test_leereingabe_erlaubt_ja
— (Docstring fehlt)grün
TestPruefmanager
test_leereingabe_erlaubt_nein
— (Docstring fehlt)grün
TestPruefmanager
test_standarddatei_vorschlagen_ja
— (Docstring fehlt)grün
TestPruefmanager
test_standarddatei_vorschlagen_nein
— (Docstring fehlt)grün
TestPruefmanager
test_temporaer_erlaubt_ja
— (Docstring fehlt)grün
TestPruefmanager
test_temporaer_erlaubt_nein
— (Docstring fehlt)grün
TestPruefmanager
test_layer_unsichtbar_ja
— (Docstring fehlt)grün
TestPruefmanager
test_layer_unsichtbar_nein
— (Docstring fehlt)grün
TestPruefmanager
test_unbekannte_aktion
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_overwrite
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_append
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_cancel
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_in_non_qgis_mode_returns_input
— (Docstring fehlt)grün

— (Docstring fehlt)

TestSettingsLogic 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
PolygonZuLinieTests
test_none_liefert_none
Prüft, dass polygon_zu_linie für None-Eingabe None liefert.grün
PolygonZuLinieTests
test_leere_geometrie_liefert_none
Prüft, dass eine leere Geometrie None liefert.grün
PolygonZuLinieTests
test_stufe1_nutzt_boundary_wenn_vorhanden
Prüft, dass eine Geometrie mit boundary() (QGIS 3.x-Pfad) deren Ergebnis direkt liefert.grün
PolygonZuLinieTests
test_stufe2_extrahiert_ringe_bei_einfachem_polygon
Prüft, dass ohne boundary() die Ringe eines einfachen Polygons manuell extrahiert werden.grün
PolygonZuLinieTests
test_stufe2_extrahiert_alle_ringe_bei_multipolygon
Prüft, dass bei einem MultiPolygon die Ringe aller Teilpolygone extrahiert werden.grün
PolygonZuLinieTests
test_stufe3_fallback_wenn_keine_ringe_extrahierbar
Prüft, dass bei fehlgeschlagener Ringextraktion auf convertToType zurückgefallen wird.grün
PolygonZuLinieTests
test_alle_stufen_scheitern_liefert_none
Prüft, dass polygon_zu_linie None liefert, wenn Ringextraktion und convertToType beide scheitern.grün
LayerExistsInGpkgTests
test_fehlende_datei_liefert_false
Prüft, dass eine nicht vorhandene GPKG-Datei False liefert.grün
LayerExistsInGpkgTests
test_leerer_pfad_oder_layername_liefert_false
Prüft, dass ein leerer Pfad oder Layername False liefert.grün
LayerExistsInGpkgTests
test_vorhandener_layer_liefert_true
Prüft, dass ein in gpkg_contents eingetragener Layername als vorhanden erkannt wird.grün
LayerExistsInGpkgTests
test_fehlender_layer_liefert_false
Prüft, dass ein nicht in gpkg_contents eingetragener Layername als nicht vorhanden gilt.grün
CreateBufferLayerTests
test_ohne_qgis_liefert_none
Prüft, dass create_buffer_layer ohne verfügbares QGIS None liefert.grün
CreateBufferLayerTests
test_fehlender_source_layer_liefert_none
Prüft, dass ein fehlender Quelllayer (None) None liefert, auch wenn QGIS verfügbar wäre.grün
CreateBufferLayerTests
test_source_layer_ohne_getfeatures_liefert_none
Prüft, dass ein Quelllayer ohne getFeatures()-Methode None liefert.grün

Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project/find_dock_widgets/add_menu/remove_menu (Issue #65/#67).

AddLayerToProjectTests 4FindDockWidgetsTests 3AddRemoveMenuTests 5
flowchart TD
+  root(["test_qgisui_wrapper_helpers.py"])
+  class root rootnode
+  root --> g1["AddLayerToProjectTests (4)"]
+  class g1 groupnode
+  g1 --> t1("none_layer_liefert_false")
+  g1 --> t2("mock_modus_liefert_true_ohne_projektzugriff")
+  g1 --> t3("mit_qgis_fuegt_layer_dem_projekt_hinzu")
+  g1 --> t4("mit_qgis_fehler_liefert_false")
+  root --> g2["FindDockWidgetsTests (3)"]
+  class g2 groupnode
+  g2 --> t5("ohne_main_window_liefert_leere_liste")
+  g2 --> t6("liefert_dock_widgets_des_main_window")
+  g2 --> t7("fehler_beim_suchen_liefert_leere_liste")
+  root --> g3["AddRemoveMenuTests (5)"]
+  class g3 groupnode
+  g3 --> t8("add_menu_ohne_main_window_tut_nichts")
+  g3 --> t9("add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu")
+  g3 --> t10("add_menu_ohne_menuaction_wird_ignoriert")
+  g3 --> t11("remove_menu_entfernt_ueber_menuaction")
+  g3 --> t12("remove_menu_ohne_main_window_tut_nichts")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
AddLayerToProjectTests
test_none_layer_liefert_false
Prüft, dass add_layer_to_project für einen fehlenden Layer (None) False liefert.grün
AddLayerToProjectTests
test_mock_modus_liefert_true_ohne_projektzugriff
Prüft, dass im Mock-Modus (ohne QGIS) ein gültiger Layer True liefert, ohne das Projekt anzufassen.grün
AddLayerToProjectTests
test_mit_qgis_fuegt_layer_dem_projekt_hinzu
Prüft, dass bei verfügbarem QGIS der Layer über QgsProject.instance().addMapLayer hinzugefügt wird.grün
AddLayerToProjectTests
test_mit_qgis_fehler_liefert_false
Prüft, dass ein Fehler beim Hinzufügen zum Projekt abgefangen wird und False liefert.grün
FindDockWidgetsTests
test_ohne_main_window_liefert_leere_liste
Prüft, dass find_dock_widgets ohne verfügbares Hauptfenster eine leere Liste liefert.grün
FindDockWidgetsTests
test_liefert_dock_widgets_des_main_window
Prüft, dass find_dock_widgets die vom Hauptfenster gefundenen Dock-Widgets durchreicht.grün
FindDockWidgetsTests
test_fehler_beim_suchen_liefert_leere_liste
Prüft, dass ein Fehler beim Suchen der Dock-Widgets abgefangen wird und eine leere Liste liefert.grün
AddRemoveMenuTests
test_add_menu_ohne_main_window_tut_nichts
Prüft, dass add_menu ohne Hauptfenster keine Ausnahme wirft.grün
AddRemoveMenuTests
test_add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu
Prüft, dass ein echtes Qt-Menü (mit menuAction) der Menüleiste hinzugefügt wird.grün
AddRemoveMenuTests
test_add_menu_ohne_menuaction_wird_ignoriert
Prüft, dass ein Objekt ohne menuAction nicht an die Menüleiste übergeben wird.grün
AddRemoveMenuTests
test_remove_menu_entfernt_ueber_menuaction
Prüft, dass remove_menu das Menü über dessen menuAction() aus der Menüleiste entfernt.grün
AddRemoveMenuTests
test_remove_menu_ohne_main_window_tut_nichts
Prüft, dass remove_menu ohne Hauptfenster keine Ausnahme wirft.grün

— (Docstring fehlt)

TestSettingsLogic 4
flowchart TD
   root(["test_settings_logic.py"])
   class root rootnode
   root --> g1["TestSettingsLogic (4)"]
@@ -378,7 +1240,36 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSettingsLogic
test_load
— (Docstring fehlt)grün
TestSettingsLogic
test_save
— (Docstring fehlt)grün
TestSettingsLogic
test_load_handles_partial_values_with_empty_fallback
— (Docstring fehlt)grün
TestSettingsLogic
test_save_empty_string_value_is_persisted
— (Docstring fehlt)grün

— (Docstring fehlt)

TestStilpruefer 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSettingsLogic
test_load
Prüft, dass load() jede bekannte Projektvariable genau einmal über get_variable() liest.grün
TestSettingsLogic
test_save
Prüft, dass save() nur bekannte Variablen-Keys speichert und unbekannte Keys ignoriert.grün
TestSettingsLogic
test_load_handles_partial_values_with_empty_fallback
Prüft, dass load() für jede Variable den (auch leeren) Wert von get_variable() unverändert übernimmt, selbst wenn nicht alle Variablen im Mock konfiguriert sind.grün
TestSettingsLogic
test_save_empty_string_value_is_persisted
Prüft, dass save() einen leeren String als gültigen Wert speichert, statt ihn wie einen fehlenden Key zu behandeln.grün

Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data/_build_input (Issue #65/#67).

SaveDataTests 2LoadDataTests 4BuildInputTests 2
flowchart TD
+  root(["test_settings_tab.py"])
+  class root rootnode
+  root --> g1["SaveDataTests (2)"]
+  class g1 groupnode
+  g1 --> t1("sammelt_text_und_combobox_werte_inklusive_landkreiskennzahl")
+  g1 --> t2("ohne_landkreis_dropdown_wird_keine_kennzahl_ergaenzt")
+  root --> g2["LoadDataTests (4)"]
+  class g2 groupnode
+  g2 --> t3("befuellt_textfelder_aus_gespeicherten_daten")
+  g2 --> t4("ohne_gespeicherten_wert_bleibt_textfeld_leer")
+  g2 --> t5("waehlt_combobox_eintrag_bei_bekanntem_namen")
+  g2 --> t6("unbekannter_gespeicherter_name_waehlt_ersten_eintrag")
+  root --> g3["BuildInputTests (2)"]
+  class g3 groupnode
+  g3 --> t7("landkreis_feld_liefert_combobox_mit_allen_landkreisen")
+  g3 --> t8("anderes_feld_liefert_lineedit")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
SaveDataTests
test_sammelt_text_und_combobox_werte_inklusive_landkreiskennzahl
Prüft, dass save_data Text- und Dropdown-Werte sammelt und die Landkreiskennzahl separat ergänzt.grün
SaveDataTests
test_ohne_landkreis_dropdown_wird_keine_kennzahl_ergaenzt
Prüft, dass ohne Landkreis-Dropdown im Formular keine 'landkreis_kennzahl' ergänzt wird.grün
LoadDataTests
test_befuellt_textfelder_aus_gespeicherten_daten
Prüft, dass load_data ein Textfeld mit dem gespeicherten Wert befüllt.grün
LoadDataTests
test_ohne_gespeicherten_wert_bleibt_textfeld_leer
Prüft, dass load_data ohne gespeicherten Wert das Textfeld auf einen leeren Text setzt.grün
LoadDataTests
test_waehlt_combobox_eintrag_bei_bekanntem_namen
Prüft, dass load_data im Landkreis-Dropdown den gespeicherten Landkreisnamen auswählt.grün
LoadDataTests
test_unbekannter_gespeicherter_name_waehlt_ersten_eintrag
Prüft, dass load_data bei einem nicht mehr vorhandenen Landkreisnamen auf den ersten Eintrag zurückfällt.grün
BuildInputTests
test_landkreis_feld_liefert_combobox_mit_allen_landkreisen
Prüft, dass _build_input für das Landkreis-Feld eine ComboBox mit Platzhalter und allen Landkreisen liefert.grün
BuildInputTests
test_anderes_feld_liefert_lineedit
Prüft, dass _build_input für ein normales Feld ein leeres Textfeld liefert.grün

— (Docstring fehlt)

TestStilpruefer 4
flowchart TD
   root(["test_stilpruefer.py"])
   class root rootnode
   root --> g1["TestStilpruefer (4)"]
@@ -395,7 +1286,34 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilpruefer
test_keine_datei_angegeben
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_mit_qml
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_falsche_endung
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_nicht
— (Docstring fehlt)grün

Unit-Tests für `sn_basis.functions.text_wrapper`.

TestSanitizeBezeichner 7
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilpruefer
test_keine_datei_angegeben
Prüft, dass eine leere Pfadangabe als 'ok' ohne Kontext akzeptiert wird (Stil ist optional).grün
TestStilpruefer
test_datei_existiert_mit_qml
Prüft, dass eine existierende .qml-Datei als 'stil_anwendbar' bestätigt wird.grün
TestStilpruefer
test_datei_existiert_falsche_endung
Prüft, dass eine existierende Datei ohne .qml-Endung als 'falsche_endung' abgelehnt wird.grün
TestStilpruefer
test_datei_existiert_nicht
Prüft, dass ein nicht existierender .qml-Pfad als 'datei_nicht_gefunden' abgelehnt wird.grün

Unit-Tests für sn_basis.functions.sys_wrapper.get_plugin_root().

TestGetPluginRoot 1JoinPathTests 1EnsureDirTests 2GetattrSafeTests 2
flowchart TD
+  root(["test_sys_wrapper.py"])
+  class root rootnode
+  root --> g1["TestGetPluginRoot (1)"]
+  class g1 groupnode
+  g1 --> t1("get_plugin_root_folgt_keiner_verzeichnisverknuepfung")
+  root --> g2["JoinPathTests (1)"]
+  class g2 groupnode
+  g2 --> t2("verbindet_mehrere_pfadbestandteile")
+  root --> g3["EnsureDirTests (2)"]
+  class g3 groupnode
+  g3 --> t3("legt_fehlendes_verzeichnis_inklusive_eltern_an")
+  g3 --> t4("vorhandenes_verzeichnis_wirft_keinen_fehler")
+  root --> g4["GetattrSafeTests (2)"]
+  class g4 groupnode
+  g4 --> t5("liefert_vorhandenes_attribut")
+  g4 --> t6("liefert_default_bei_fehlendem_attribut")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetPluginRoot
test_get_plugin_root_folgt_keiner_verzeichnisverknuepfung
Prüft, dass get_plugin_root() bei einem über eine Verzeichnisverknüpfung (wie von link_qgis_profile.py angelegt) erreichten Modul den verlinkten Pfad liefert, statt zum echten Zielverzeichnis aufzulösen (Verfahrensgebiet #50).grün
JoinPathTests
test_verbindet_mehrere_pfadbestandteile
Prüft, dass join_path mehrere Bestandteile OS-sicher zu einem Path zusammenfügt.grün
EnsureDirTests
test_legt_fehlendes_verzeichnis_inklusive_eltern_an
Prüft, dass ensure_dir ein fehlendes Verzeichnis samt Elternverzeichnissen anlegt.grün
EnsureDirTests
test_vorhandenes_verzeichnis_wirft_keinen_fehler
Prüft, dass ensure_dir bei einem bereits vorhandenen Verzeichnis keinen Fehler wirft.grün
GetattrSafeTests
test_liefert_vorhandenes_attribut
Prüft, dass getattr_safe ein vorhandenes Attribut liefert.grün
GetattrSafeTests
test_liefert_default_bei_fehlendem_attribut
Prüft, dass getattr_safe bei fehlendem Attribut den Default-Wert liefert.grün

Unit-Tests für `sn_basis.functions.text_wrapper`.

TestSanitizeBezeichner 7
flowchart TD
   root(["test_text_wrapper.py"])
   class root rootnode
   root --> g1["TestSanitizeBezeichner (7)"]
@@ -418,7 +1336,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSanitizeBezeichner
test_umlaute_bleiben_erhalten
Prüft, dass Umlaute und ß nicht durch den Ersatz entfernt werden.grün
TestSanitizeBezeichner
test_bindestrich_bleibt_erhalten
Prüft, dass ein Bindestrich im Bezeichner erhalten bleibt (Issue #42).grün
TestSanitizeBezeichner
test_unterstrich_bleibt_erhalten
Prüft, dass ein Unterstrich im Bezeichner erhalten bleibt.grün
TestSanitizeBezeichner
test_leerzeichen_wird_durch_ersatz_zusammengefasst
Prüft, dass Leerzeichen (kein zulässiges Zeichen) zu einem Ersatzzeichen werden.grün
TestSanitizeBezeichner
test_fuehrender_und_folgender_ersatz_wird_entfernt
Prüft, dass ein Ersatzzeichen am Anfang oder Ende des Ergebnisses entfernt wird.grün
TestSanitizeBezeichner
test_eigenes_ersatzzeichen_wird_verwendet
Prüft, dass ein abweichendes Ersatzzeichen anstelle von Unterstrich genutzt wird.grün
TestSanitizeBezeichner
test_nur_unzulaessige_zeichen_ergibt_leeren_string
Prüft, dass ein Bezeichner ganz aus unzulässigen Zeichen zu einem leeren String wird.grün

— (Docstring fehlt)

TestVariableWrapper 6
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSanitizeBezeichner
test_umlaute_bleiben_erhalten
Prüft, dass Umlaute und ß nicht durch den Ersatz entfernt werden.grün
TestSanitizeBezeichner
test_bindestrich_bleibt_erhalten
Prüft, dass ein Bindestrich im Bezeichner erhalten bleibt (Issue #42).grün
TestSanitizeBezeichner
test_unterstrich_bleibt_erhalten
Prüft, dass ein Unterstrich im Bezeichner erhalten bleibt.grün
TestSanitizeBezeichner
test_leerzeichen_wird_durch_ersatz_zusammengefasst
Prüft, dass Leerzeichen (kein zulässiges Zeichen) zu einem Ersatzzeichen werden.grün
TestSanitizeBezeichner
test_fuehrender_und_folgender_ersatz_wird_entfernt
Prüft, dass ein Ersatzzeichen am Anfang oder Ende des Ergebnisses entfernt wird.grün
TestSanitizeBezeichner
test_eigenes_ersatzzeichen_wird_verwendet
Prüft, dass ein abweichendes Ersatzzeichen anstelle von Unterstrich genutzt wird.grün
TestSanitizeBezeichner
test_nur_unzulaessige_zeichen_ergibt_leeren_string
Prüft, dass ein Bezeichner ganz aus unzulässigen Zeichen zu einem leeren String wird.grün

— (Docstring fehlt)

TestVariableWrapper 6
flowchart TD
   root(["test_variable_wrapper.py"])
   class root rootnode
   root --> g1["TestVariableWrapper (6)"]
@@ -439,7 +1357,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVariableWrapper
test_set_variable_project_noop_write_is_skipped
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_project_changed_value_is_written
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_global_writes_prefixed_name
— (Docstring fehlt)grün
TestVariableWrapper
test_get_variable_global_returns_empty_string_fallback
— (Docstring fehlt)grün
TestVariableWrapper
test_get_variable_invalid_scope_raises
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_invalid_scope_raises
— (Docstring fehlt)grün

— (Docstring fehlt)

TestVerfahrensgebietManager 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVariableWrapper
test_set_variable_project_noop_write_is_skipped
Prüft, dass set_variable() im Projekt-Scope nicht schreibt, wenn der neue Wert dem bestehenden entspricht.grün
TestVariableWrapper
test_set_variable_project_changed_value_is_written
Prüft, dass set_variable() im Projekt-Scope bei geändertem Wert mit dem Präfix 'sn_' geschrieben wird.grün
TestVariableWrapper
test_set_variable_global_writes_prefixed_name
Prüft, dass set_variable() im globalen Scope den Variablennamen mit 'sn_' vorangestellt schreibt.grün
TestVariableWrapper
test_get_variable_global_returns_empty_string_fallback
Prüft, dass get_variable() im globalen Scope für eine nicht gesetzte Variable einen leeren String liefert.grün
TestVariableWrapper
test_get_variable_invalid_scope_raises
Prüft, dass get_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst.grün
TestVariableWrapper
test_set_variable_invalid_scope_raises
Prüft, dass set_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst.grün

Tests für sn_basis/functions/verfahrensgebiet_manager.py (zentrale Layer-Verfolgung, Issue #65).

TestVerfahrensgebietManager 5OnHandlerTests 5ListenerSetupTests 3
flowchart TD
   root(["test_verfahrensgebiet_manager.py"])
   class root rootnode
   root --> g1["TestVerfahrensgebietManager (5)"]
@@ -449,16 +1367,36 @@
   g1 --> t3("set_verfahrensgebiet_layer_id_writes_project_variable")
   g1 --> t4("update_without_layer_does_not_write_when_already_empty")
   g1 --> t5("update_with_layer_writes_variable")
+  root --> g2["OnHandlerTests (5)"]
+  class g2 groupnode
+  g2 --> t6("on_project_changed_ohne_layer_setzt_variable_leer")
+  g2 --> t7("on_project_changed_mit_layer_setzt_variable")
+  g2 --> t8("on_layers_added_findet_neuen_layer")
+  g2 --> t9("on_layer_removed_bei_aktuellem_layer_sucht_neu")
+  g2 --> t10("on_layer_removed_bei_anderem_layer_aendert_nichts")
+  root --> g3["ListenerSetupTests (3)"]
+  class g3 groupnode
+  g3 --> t11("setup_ohne_qgis_tut_nichts")
+  g3 --> t12("setup_verbindet_signale_und_ist_idempotent")
+  g3 --> t13("teardown_trennt_alle_signale_und_ist_idempotent")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_set_verfahrensgebiet_layer_id_writes_project_variable
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_update_without_layer_does_not_write_when_already_empty
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
— (Docstring fehlt)grün
+ classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert.grün
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value
Prüft, dass get_verfahrensgebiet_layer_id() bei leerer Projektvariable None statt eines leeren Strings liefert.grün
TestVerfahrensgebietManager
test_set_verfahrensgebiet_layer_id_writes_project_variable
Prüft, dass set_verfahrensgebiet_layer_id() die Layer-ID als Projektvariable 'verfahrensgebiet_layer' speichert.grün
TestVerfahrensgebietManager
test_update_without_layer_does_not_write_when_already_empty
Prüft, dass ohne gefundenen Verfahrensgebiet-Layer keine Schreibaktion erfolgt, wenn die Variable bereits leer ist.grün
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert.grün
OnHandlerTests
test_on_project_changed_ohne_layer_setzt_variable_leer
Prüft, dass _on_project_changed die Variable leert, wenn kein Verfahrensgebiet-Layer existiert.grün
OnHandlerTests
test_on_project_changed_mit_layer_setzt_variable
Prüft, dass _on_project_changed die Layer-ID eines gefundenen Verfahrensgebiet-Layers übernimmt.grün
OnHandlerTests
test_on_layers_added_findet_neuen_layer
Prüft, dass _on_layers_added nach neu hinzugefügten Layern sucht und die Variable aktualisiert.grün
OnHandlerTests
test_on_layer_removed_bei_aktuellem_layer_sucht_neu
Prüft, dass das Entfernen des aktuell aktiven Verfahrensgebiet-Layers eine Neusuche auslöst.grün
OnHandlerTests
test_on_layer_removed_bei_anderem_layer_aendert_nichts
Prüft, dass das Entfernen eines anderen Layers keine Neusuche auslöst.grün
ListenerSetupTests
test_setup_ohne_qgis_tut_nichts
Prüft, dass setup_verfahrensgebiet_listener ohne QGIS keine Signale registriert.grün
ListenerSetupTests
test_setup_verbindet_signale_und_ist_idempotent
Prüft, dass setup_verfahrensgebiet_listener die Projektsignale verbindet und ein zweiter Aufruf nichts doppelt registriert.grün
ListenerSetupTests
test_teardown_trennt_alle_signale_und_ist_idempotent
Prüft, dass teardown_verfahrensgebiet_listener alle Signale trennt und mehrfacher Aufruf nicht fehlschlägt.grün
@@ -468,8 +1406,8 @@

sn_verfahrensgebiet

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 12 Testdateien · 208 Testfälle

-

Unit-Tests für `sn_verfahrensgebiet.functions.beteiligung_pruefung`.

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
@@ -556,10 +1494,10 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 12TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
-  root --> g1["TestFlurstueckAlkis (12)"]
+  root --> g1["TestFlurstueckAlkis (11)"]
   class g1 groupnode
   g1 --> t1("user_cancel_returns_none")
   g1 --> t2("empty_text_after_confirmation_returns_none")
@@ -567,37 +1505,35 @@
   g1 --> t4("success_returns_layer_and_first_status")
   g1 --> t5("sql_filter_contains_all_schluessel")
   g1 --> t6("existing_layer_reload_yes")
-  g1 --> t7("existing_layer_keep_no")
+  g1 --> t7("existing_layer_abbrechen_returns_none")
   g1 --> t8("vorgabe_schluessel_prefills_default_text")
   g1 --> t9("vorgabe_namen_appears_in_label")
-  g1 --> t10("bbox_embedded_in_wfs_uri")
-  g1 --> t11("backwards_compatible_no_new_params")
-  g1 --> t12("ohne_schluessel_abfrage_ueberspringt_ask_text")
+  g1 --> t10("backwards_compatible_no_new_params")
+  g1 --> t11("ohne_schluessel_abfrage_ueberspringt_ask_text")
   root --> g2["TestFlurstueckAlkisAnhaengenDedup (3)"]
   class g2 groupnode
-  g2 --> t13("append_ueberspringt_bereits_vorhandene_flurstuecke")
-  g2 --> t14("append_ohne_kennzeichen_feld_haengt_alles_an")
-  g2 --> t15("ohne_schluessel_abfrage_append_dedup")
+  g2 --> t12("append_ueberspringt_bereits_vorhandene_flurstuecke")
+  g2 --> t13("append_ohne_kennzeichen_feld_haengt_alles_an")
+  g2 --> t14("ohne_schluessel_abfrage_append_dedup")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 warn
   class t5 warn
   class t6 warn
-  class t7 warn
+  class t7 ok
   class t8 warn
   class t9 warn
   class t10 warn
-  class t11 warn
+  class t11 ok
   class t12 ok
   class t13 ok
   class t14 ok
-  class t15 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
ask_text → ok=False, text="" → (None, NONE).grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
ask_text → ok=True, aber Text leer → (None, NONE).grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
WFS-Layer nicht valide → (None, NONE).grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Kein bestehender Layer, WFS valide → (layer, FIRST).rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Der WFS-URI enthält alle eingegebenen Gemarkungsschlüssel.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Bestehender Layer + Nutzer: 'Ja, neu laden' → LoadStatus.RELOAD.rot
TestFlurstueckAlkis
test_existing_layer_keep_no
Bestehender Layer + Nutzer: 'Nein, behalten' → LoadStatus.KEEP.rot
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
vorgabe_schluessel=['1234','5678'] → ask_text erhält '1234, 5678' als default_text.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
vorgabe_namen=['Musterstadt'] → ask_text-label enthält den Namen.rot
TestFlurstueckAlkis
test_bbox_embedded_in_wfs_uri
bbox-Parameter → WFS-URI enthält 'bbox=' Substring.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
flurstueck_alkis(tab) ohne neue Parameter → kein Fehler.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
ohne_schluessel_abfrage=True verwendet vorgabe_schluessel direkt, ohne ask_text aufzurufen.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird.rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird.rot
TestFlurstueckAlkis
test_existing_layer_abbrechen_returns_none
Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird.grün
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
   root(["test_gemarkung_alkis.py"])
   class root rootnode
   root --> g1["TestExtractSchluesselUndName (4)"]
@@ -640,7 +1576,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtractSchluesselUndName
test_standard_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_fallback_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_missing_attribute_returns_empty
— (Docstring fehlt)grün
TestExtractSchluesselUndName
test_none_value_returns_empty
— (Docstring fehlt)grün
TestGemarkungSchluesselbbox
test_success_bbox_filter
WFS-BBox-Filter liefert Features → korrekte Tupel-Liste.übersp.
TestGemarkungSchluesselbbox
test_fallback_when_bbox_fails
BBox-Filter schlägt fehl → Fallback auf Volllade + client-seitige Filterung.übersp.
TestGemarkungSchluesselbbox
test_both_layers_fail_returns_empty
Beide Lade-Strategien scheitern → leere Liste.übersp.
TestGemarkungSchluesselbbox
test_deduplication_and_sorting
Doppelte Schlüssel werden dedupliziert, Ergebnis ist sortiert.übersp.
TestGemarkungSchluesselbbox
test_feature_without_schluessel_skipped
Feature ohne Schlüssel-Attribut wird übersprungen.übersp.
TestGemarkungSchluesselbbox
test_exception_returns_empty
Exception im Ladevorgang → leere Liste (kein Crash).übersp.
TestGemarkungSchluesselnurNummern
test_returns_only_nummern
— (Docstring fehlt)übersp.
TestGemarkungSchluesselnurNummern
test_empty_input_returns_empty
— (Docstring fehlt)übersp.
TestLadeGemarkungenlayer
test_returns_layer_when_service_reachable
Dienst erreichbar (isValid=True) → Layer-Objekt zurückgegeben.grün
TestLadeGemarkungenlayer
test_returns_none_when_service_unreachable
Dienst nicht erreichbar (isValid=False) → None zurückgegeben.grün

Unit-Tests für `sn_verfahrensgebiet.functions.grenzpunkt_alkis`.

TestExtrahiereGmlIds 5TestGrenzpunktAlkis 7
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtractSchluesselUndName
test_standard_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_fallback_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_missing_attribute_returns_empty
— (Docstring fehlt)grün
TestExtractSchluesselUndName
test_none_value_returns_empty
— (Docstring fehlt)grün
TestGemarkungSchluesselbbox
test_success_bbox_filter
WFS-BBox-Filter liefert Features → korrekte Tupel-Liste.übersp.
TestGemarkungSchluesselbbox
test_fallback_when_bbox_fails
BBox-Filter schlägt fehl → Fallback auf Volllade + client-seitige Filterung.übersp.
TestGemarkungSchluesselbbox
test_both_layers_fail_returns_empty
Beide Lade-Strategien scheitern → leere Liste.übersp.
TestGemarkungSchluesselbbox
test_deduplication_and_sorting
Doppelte Schlüssel werden dedupliziert, Ergebnis ist sortiert.übersp.
TestGemarkungSchluesselbbox
test_feature_without_schluessel_skipped
Feature ohne Schlüssel-Attribut wird übersprungen.übersp.
TestGemarkungSchluesselbbox
test_exception_returns_empty
Exception im Ladevorgang → leere Liste (kein Crash).übersp.
TestGemarkungSchluesselnurNummern
test_returns_only_nummern
— (Docstring fehlt)übersp.
TestGemarkungSchluesselnurNummern
test_empty_input_returns_empty
— (Docstring fehlt)übersp.
TestLadeGemarkungenlayer
test_returns_layer_when_service_reachable
Dienst erreichbar (isValid=True) → Layer-Objekt zurückgegeben.grün
TestLadeGemarkungenlayer
test_returns_none_when_service_unreachable
Dienst nicht erreichbar (isValid=False) → None zurückgegeben.grün

Unit-Tests für `sn_verfahrensgebiet.functions.grenzpunkt_alkis`.

TestExtrahiereGmlIds 5TestGrenzpunktAlkis 7TestGrenzpunkteGemarkungsweiseStil 1
flowchart TD
   root(["test_grenzpunkt_alkis.py"])
   class root rootnode
   root --> g1["TestExtrahiereGmlIds (5)"]
@@ -659,6 +1595,9 @@
   g2 --> t10("grenzpunkt_batch_empty")
   g2 --> t11("success_returns_both_layers")
   g2 --> t12("extent_derived_from_vg_layer")
+  root --> g3["TestGrenzpunkteGemarkungsweiseStil (1)"]
+  class g3 groupnode
+  g3 --> t13("stil_wird_nach_dem_laden_auf_den_ergebnis_layer_angewendet")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -671,11 +1610,12 @@
   class t10 warn
   class t11 warn
   class t12 warn
+  class t13 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtrahiereGmlIds
test_xlink_href_format
URN-Format 'urn:adv:oid:DEABC123' → nur 'DEABC123' extrahiert.grün
TestExtrahiereGmlIds
test_plain_gml_id
Plain-GML-ID ohne Doppelpunkt → direkt übernommen.grün
TestExtrahiereGmlIds
test_empty_value_skipped
Leerer gehoertZu-Wert wird übersprungen.grün
TestExtrahiereGmlIds
test_deduplication
Doppelte Referenzen werden dedupliziert.grün
TestExtrahiereGmlIds
test_sorted_output
Rückgabe ist aufsteigend sortiert.grün
TestGrenzpunktAlkis
test_no_vg_layer_returns_none
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_invalid_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_empty_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_no_gml_ids_returns_pta_only
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_grenzpunkt_batch_empty
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_success_returns_both_layers
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_extent_derived_from_vg_layer
Wenn kein extent übergeben → Extent aus VG-Layer holen.rot

Unit-Tests für `sn_verfahrensgebiet.functions.knickpunkt_pruefung`.

TestExtrahiereKnickpunkte 4TestKnickpunktPruefungInputvalidierung 3TestKnickpunktPruefungKlassifizierung 2TestFehlendKnickpunkteViaVerbindungslinien 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtrahiereGmlIds
test_xlink_href_format
URN-Format 'urn:adv:oid:DEABC123' → nur 'DEABC123' extrahiert.grün
TestExtrahiereGmlIds
test_plain_gml_id
Plain-GML-ID ohne Doppelpunkt → direkt übernommen.grün
TestExtrahiereGmlIds
test_empty_value_skipped
Leerer gehoertZu-Wert wird übersprungen.grün
TestExtrahiereGmlIds
test_deduplication
Doppelte Referenzen werden dedupliziert.grün
TestExtrahiereGmlIds
test_sorted_output
Rückgabe ist aufsteigend sortiert.grün
TestGrenzpunktAlkis
test_no_vg_layer_returns_none
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_invalid_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_empty_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_no_gml_ids_returns_pta_only
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_grenzpunkt_batch_empty
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_success_returns_both_layers
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_extent_derived_from_vg_layer
Wenn kein extent übergeben → Extent aus VG-Layer holen.rot
TestGrenzpunkteGemarkungsweiseStil
test_stil_wird_nach_dem_laden_auf_den_ergebnis_layer_angewendet
Prüft, dass grenzpunkte_gemarkungsweise() nach dem Laden apply_style() mit dem neuen ALKIS-Grenzpunkt-Stil auf den fertigen Join-Layer anwendet (#53).grün

Unit-Tests für `sn_verfahrensgebiet.functions.knickpunkt_pruefung`.

TestExtrahiereKnickpunkte 4TestKnickpunktPruefungInputvalidierung 3TestKnickpunktPruefungKlassifizierung 2TestFehlendKnickpunkteViaVerbindungslinien 4
flowchart TD
   root(["test_knickpunkt_pruefung.py"])
   class root rootnode
   root --> g1["TestExtrahiereKnickpunkte (4)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index abcac7b..23d6be2 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 31 Testdateien, 346 Testfälle – 301 grün, 37 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 114 von 346 (33 %).
+Gesamt: 3 Pakete, 55 Testdateien, 648 Testfälle – 605 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 503 von 648 (78 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 14 | 94 | 94 | 0 | 0 | 7 von 94 (7 %) |
-| `sn_verfahrensgebiet` | 12 | 208 | 164 | 36 | 8 | 106 von 208 (51 %) |
+| `sn_basis` | 38 | 396 | 396 | 0 | 0 | 396 von 396 (100 %) |
+| `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **31** | **346** | **301** | **37** | **8** | **114 von 346 (33 %)** |
+| **Summe** | **55** | **648** | **605** | **35** | **8** | **503 von 648 (78 %)** |
 
 ### Legende
 
@@ -31,7 +31,167 @@ Mit Beschreibung (Docstring): 114 von 346 (33 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 14 Testdateien mit 94 Testfällen (94 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 38 Testdateien mit 396 Testfällen (396 grün, 0 rot, 0 übersprungen).
+
+### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).
+
+Testgruppen:
+
+- `CloseEventTests` (5 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `CloseEventTests.test_ruft_cleanup_aller_tabs_auf` | Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft. | grün |
+| `CloseEventTests.test_ignoriert_tabs_ohne_cleanup` | Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt. | grün |
+| `CloseEventTests.test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter` | Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert. | grün |
+| `CloseEventTests.test_setzt_toolbar_action_zurueck` | Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt. | grün |
+| `CloseEventTests.test_ohne_action_wird_nichts_zurueckgesetzt` | Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_anbindung.py`
+
+Worum es in der Datei geht: Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):
+
+Testgruppen:
+
+- `TestMehrfachauswahl` (1 Testfälle): — (Docstring fehlt)
+- `TestDruckanbindung` (4 Testfälle): — (Docstring fehlt)
+- `TestBasemapBereich` (8 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestMehrfachauswahl.test_mock_modus_liefert_vorauswahl_in_listenreihenfolge` | Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert. | grün |
+| `TestDruckanbindung.test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter` | Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist. | grün |
+| `TestDruckanbindung.test_layout_wird_markiert_und_legende_erzeugt` | Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt. | grün |
+| `TestDruckanbindung.test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht` | Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht. | grün |
+| `TestDruckanbindung.test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden` | Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut. | grün |
+| `TestBasemapBereich.test_auswertebereich_wird_gespeichert` | Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet. | grün |
+| `TestBasemapBereich.test_aktualisieren_ohne_vorlage_warnt` | Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet. | grün |
+| `TestBasemapBereich.test_aktualisieren_wertet_gewaehlte_vorlage_aus` | Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet. | grün |
+| `TestBasemapBereich.test_stil_laden_uebernimmt_legendenauswahl` | Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt. | grün |
+| `TestBasemapBereich.test_stil_laden_bricht_bei_fremder_vorlage_ab` | Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird. | grün |
+| `TestBasemapBereich.test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt` | Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird. | grün |
+| `TestBasemapBereich.test_speichern_mit_aktueller_legendenauswahl` | Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird. | grün |
+| `TestBasemapBereich.test_stil_speichern_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_kacheln.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).
+
+Testgruppen:
+
+- `TestSammleObjekte` (4 Testfälle): — (Docstring fehlt)
+- `TestZoomUndStile` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestSammleObjekte.test_gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt` | Prüft, dass Objekte mit gleichem Quell-Layer, Geometrietyp und Attributen über Kachelgrenzen hinweg nur einmal gezählt werden. | grün |
+| `TestSammleObjekte.test_kacheln_ausserhalb_der_geometrie_werden_uebersprungen` | Prüft, dass Kacheln ohne Schnitt mit dem Auswertebereich nicht abgefragt werden. | grün |
+| `TestSammleObjekte.test_abbruch_liefert_none_und_hebt_auswahl_auf` | Prüft, dass ein Abbruch über den Fortschritt None liefert und die Auswahl auf dem Basemap-Layer trotzdem aufgehoben wird. | grün |
+| `TestSammleObjekte.test_anzahl_kacheln_aus_kachelbereich` | Prüft, dass die Kachelzahl aus dem Kachelbereich der Geometrie berechnet wird. | grün |
+| `TestZoomUndStile.test_zoomstufen_wie_im_qgis_renderer` | Prüft, dass Kachelzoom begrenzt, Darstellungszoom unbegrenzt und der gebrochene Zoom aus dem Maßstab bestimmt werden. | grün |
+| `TestZoomUndStile.test_stile_aus_layer_liest_renderer_styles` | Prüft, dass Name, Quell-Layer, Geometrietyp, Zoombereich, Schalter und Filter der Renderer-Styles übernommen werden. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_katalog.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_katalog.py – Legendenkatalog aus der basemap.de-Style-JSON (Basis #88).
+
+Testgruppen:
+
+- `TestRendererStil` (3 Testfälle): — (Docstring fehlt)
+- `TestErzeugeKatalog` (6 Testfälle): — (Docstring fehlt)
+- `TestSymbolwahlUndPruefung` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestRendererStil.test_beschriftung_und_3d_sind_keine_renderer_stile` | Prüft, dass reine Beschriftungen und 3D-Gebäude nicht als Renderer-Style gelten, Icon-Symbole und Flächen schon. | grün |
+| `TestRendererStil.test_ausschluss_ueber_liste_und_praefix` | Prüft, dass Style-IDs über die Ausschlussliste und über Präfixe (Nummernschilder) ausgeschlossen werden. | grün |
+| `TestRendererStil.test_normalisierung_entfernt_variantenkennungen` | Prüft, dass Variantenkennungen wie Kontur, Brücke oder Breitenangaben beim Normalisieren einer Style-ID entfallen. | grün |
+| `TestErzeugeKatalog.test_kontur_decker_und_bruecke_bilden_einen_eintrag` | Prüft, dass Kontur-, Decker-, Fahrbahn- und Brückenvarianten derselben Straßenklasse genau einen Legendeneintrag bilden. | grün |
+| `TestErzeugeKatalog.test_zustand_im_bau_wird_eigener_eintrag` | Prüft, dass „im Bau“ einen eigenen Legendeneintrag mit Zustandsangabe in der Bezeichnung bekommt. | grün |
+| `TestErzeugeKatalog.test_punkt_und_flaeche_derselben_objektart_werden_zusammengefasst` | Prüft, dass das Kran-Symbol aus Bauwerkspunkt und Bauwerksfläche in einem Eintrag landet. | grün |
+| `TestErzeugeKatalog.test_ausgeschlossene_stile_fehlen_im_katalog` | Prüft, dass Hintergrund, Beschriftungen, Nummernschilder und 3D-Gebäude keinem Eintrag zugeordnet werden. | grün |
+| `TestErzeugeKatalog.test_fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten` | Prüft, dass Bezeichnung, Gruppe, Zusammenführung und Symbolwahl eines bestehenden Katalogs bei einem erneuten Lauf erhalten bleiben. | grün |
+| `TestErzeugeKatalog.test_entfallene_style_ids_werden_gemeldet` | Prüft, dass eine Style-ID, die in der neuen Style-JSON fehlt, als entfallen gemeldet und aus dem Katalog entfernt wird. | grün |
+| `TestSymbolwahlUndPruefung.test_symbol_stile_je_rolle_nur_grundvariante` | Prüft, dass für das Legendensymbol je Rolle (Kontur, Decker) nur die erste Grundvariante gewählt wird. | grün |
+| `TestSymbolwahlUndPruefung.test_pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole` | Prüft, dass die Katalogprüfung doppelt zugeordnete Style-IDs und Symbol-Styles außerhalb des Katalogs meldet. | grün |
+| `TestSymbolwahlUndPruefung.test_ausgelieferter_katalog_ist_konsistent` | Prüft, dass der ausgelieferte Katalog data/basemap_legende.json ohne Fehler geprüft wird und Kreisstraße mit Kontur und Decker enthält. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_legende.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_legende.py – Zuordnung, Auswahl und Ablauf der Basemap-Legende (Basis #88).
+
+Testgruppen:
+
+- `TestStileUndEintraege` (4 Testfälle): — (Docstring fehlt)
+- `TestAuswahlUndEinstellungen` (6 Testfälle): — (Docstring fehlt)
+- `TestLayerErkennung` (2 Testfälle): — (Docstring fehlt)
+- `TestAblauf` (6 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestStileUndEintraege.test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich` | Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt). | grün |
+| `TestStileUndEintraege.test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter` | Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe. | grün |
+| `TestStileUndEintraege.test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe` | Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind. | grün |
+| `TestStileUndEintraege.test_ausgeschlossene_und_unbekannte_styles` | Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen. | grün |
+| `TestAuswahlUndEinstellungen.test_vorauswahl_bekannte_an_unbekannte_aus` | Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind. | grün |
+| `TestAuswahlUndEinstellungen.test_auswahl_wird_gemerkt_und_vorbelegt` | Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden. | grün |
+| `TestAuswahlUndEinstellungen.test_merken_ergaenzt_die_bisherige_auswahl` | Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält. | grün |
+| `TestAuswahlUndEinstellungen.test_kaputte_projektvariable_ergibt_leere_auswahl` | Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt. | grün |
+| `TestAuswahlUndEinstellungen.test_auswertebereich_standard_und_umstellung` | Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden. | grün |
+| `TestAuswahlUndEinstellungen.test_legende_erzeugen_standard_an` | Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann. | grün |
+| `TestLayerErkennung.test_erkennung_ueber_quelle_und_namen` | Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen. | grün |
+| `TestLayerErkennung.test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum` | Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen. | grün |
+| `TestAblauf.test_ohne_basemap_layer_passiert_nichts` | Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen. | grün |
+| `TestAblauf.test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt` | Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird. | grün |
+| `TestAblauf.test_abbruch_im_dialog_aendert_nichts` | Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert. | grün |
+| `TestAblauf.test_rueckfall_auf_druckbereich_wird_gemeldet` | Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird. | grün |
+| `TestAblauf.test_ohne_objekte_keine_legende` | Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen. | grün |
+| `TestAblauf.test_viele_kacheln_werden_vorher_bestaetigt` | Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_legende_layout.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).
+
+Testgruppen:
+
+- `TestKennzeichenUndKarte` (2 Testfälle): — (Docstring fehlt)
+- `TestSymbolwahl` (2 Testfälle): — (Docstring fehlt)
+- `TestHilfslayer` (3 Testfälle): — (Docstring fehlt)
+- `TestPlatzierung` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestKennzeichenUndKarte.test_layout_kennzeichen_setzen_und_erkennen` | Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird. | grün |
+| `TestKennzeichenUndKarte.test_hauptkarte_ueber_id` | Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird. | grün |
+| `TestSymbolwahl.test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge` | Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden. | grün |
+| `TestSymbolwahl.test_ohne_aktive_symbol_stile_erster_getroffener_style` | Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert. | grün |
+| `TestHilfslayer.test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer` | Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen. | grün |
+| `TestHilfslayer.test_alte_hilfslayer_werden_entfernt` | Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden. | grün |
+| `TestHilfslayer.test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt` | Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird. | grün |
+| `TestPlatzierung.test_einspaltig_ueber_der_quellenangabe` | Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht. | grün |
+| `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün |
+| `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_stilvorlagen.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).
+
+Testgruppen:
+
+- `TestStilvorlagen` (8 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestStilvorlagen.test_speichern_und_auflisten` | Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird. | grün |
+| `TestStilvorlagen.test_mehrere_varianten_alphabetisch` | Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden. | grün |
+| `TestStilvorlagen.test_leerer_name_oder_fehler_beim_schreiben` | Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden. | grün |
+| `TestStilvorlagen.test_laden_setzt_stil_und_zeichnet_neu` | Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt. | grün |
+| `TestStilvorlagen.test_passung_meldet_fremde_styles` | Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt. | grün |
+| `TestStilvorlagen.test_standard_setzen_und_beim_loeschen_entfernen` | Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt. | grün |
+| `TestStilvorlagen.test_fehlende_begleitdatei_wird_toleriert` | Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird. | grün |
+| `TestStilvorlagen.test_ohne_ordner_leere_liste` | Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_bootstrap.py`
 
@@ -45,16 +205,18 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestDataGrabber` (6 Testfälle): — (Docstring fehlt)
+- `TestDataGrabber` (8 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestDataGrabber.test_analyze_source_type_detects_excel` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_analyze_source_type_returns_unknown_filetype` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_analyze_source_type_detects_service_when_file_check_fails` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_analyze_source_type_returns_unbekannte_quelle_on_full_failure` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_run_returns_success_summary_with_valid_rows` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_run_returns_failure_summary_without_rows` | — (Docstring fehlt) | grün |
+| `TestDataGrabber.test_analyze_source_type_detects_excel` | Prüft, dass eine erreichbare .xlsx-Datei als Quelltyp 'excel' erkannt wird. | grün |
+| `TestDataGrabber.test_analyze_source_type_returns_unknown_filetype` | Prüft, dass eine erreichbare Datei mit unbekannter Endung als 'unbekannter_dateityp' eingestuft wird. | grün |
+| `TestDataGrabber.test_analyze_source_type_detects_service_when_file_check_fails` | Prüft, dass eine Quelle als 'dienst' erkannt wird, wenn die Dateiprüfung fehlschlägt, die URL aber erreichbar ist. | grün |
+| `TestDataGrabber.test_analyze_source_type_returns_unbekannte_quelle_on_full_failure` | Prüft, dass eine Quelle als 'unbekannte_quelle' eingestuft wird, wenn weder Datei- noch Linkprüfung erfolgreich sind. | grün |
+| `TestDataGrabber.test_run_returns_success_summary_with_valid_rows` | Prüft, dass run() bei erfolgreichem Excel-Import eine Erfolgsmeldung mit den geladenen Zeilen liefert. | grün |
+| `TestDataGrabber.test_run_returns_failure_summary_without_rows` | Prüft, dass run() bei nicht erkennbarer Quelle eine Fehlermeldung ohne Zeilen liefert. | grün |
+| `TestDataGrabber.test_aggregate_results_mit_zeilen_liefert_erfolgsergebnis` | Prüft, dass _aggregate_results bei vorhandenen Zeilen ein Erfolgsergebnis mit Zeilenzahl liefert. | grün |
+| `TestDataGrabber.test_aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege` | Prüft, dass _aggregate_results ohne Zeilen ok=False mit Aktion 'keine_validen_eintraege' liefert. | grün |
 
 ### `Plugin_SN_Basis/tests/test_dateipruefer.py`
 
@@ -62,16 +224,135 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestDateipruefer` (6 Testfälle): — (Docstring fehlt)
+- `TestDateipruefer` (17 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestDateipruefer.test_leereingabe_erlaubt` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_leereingabe_nicht_erlaubt` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_standarddatei_vorschlagen` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_temporaer_erlaubt` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_datei_nicht_gefunden` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_datei_ok` | — (Docstring fehlt) | grün |
+| `TestDateipruefer.test_leereingabe_erlaubt` | Prüft, dass eine leere Pfadangabe bei leereingabe_erlaubt=True als 'leereingabe_erlaubt' ohne Kontext gemeldet wird. | grün |
+| `TestDateipruefer.test_leereingabe_nicht_erlaubt` | Prüft, dass eine leere Pfadangabe ohne leereingabe_erlaubt als 'leereingabe_nicht_erlaubt' abgelehnt wird. | grün |
+| `TestDateipruefer.test_standarddatei_vorschlagen` | Prüft, dass bei leerer Pfadangabe mit gesetzter Standarddatei 'standarddatei_vorschlagen' mit dem Standardpfad als Kontext geliefert wird. | grün |
+| `TestDateipruefer.test_temporaer_erlaubt` | Prüft, dass eine leere Pfadangabe bei temporaer_erlaubt=True als 'temporaer_erlaubt' ohne Kontext gemeldet wird. | grün |
+| `TestDateipruefer.test_datei_nicht_gefunden` | Prüft, dass ein nicht existierender Dateipfad als 'datei_nicht_gefunden' mit dem Pfad als Kontext abgelehnt wird. | grün |
+| `TestDateipruefer.test_datei_ok` | Prüft, dass ein existierender Dateipfad als 'ok' mit dem Pfad als Kontext bestätigt wird. | grün |
+| `TestDateipruefer.test_erkenne_dateityp_unterscheidet_excel_datenbank_und_unbekannt` | Prüft, dass erkenne_dateityp .xlsx als 'excel', .gpkg/.sqlite als 'datenbank' und Rest als 'unbekannt' erkennt. | grün |
+| `TestDateipruefer.test_pfad_verbindet_relativen_pfad_mit_basispfad` | Prüft, dass _pfad einen relativen Pfad mit dem Basisverzeichnis verbindet. | grün |
+| `TestDateipruefer.test_pfad_laesst_absoluten_pfad_unveraendert` | Prüft, dass _pfad einen bereits absoluten Pfad nicht mit dem Basisverzeichnis verbindet. | grün |
+| `TestDateipruefer.test_ist_gueltiger_gpkg_pfad_akzeptiert_vorhandenes_verzeichnis` | Prüft, dass ein .gpkg-Pfad mit vorhandenem Zielverzeichnis als gültig gilt. | grün |
+| `TestDateipruefer.test_ist_gueltiger_gpkg_pfad_lehnt_falsche_endung_ab` | Prüft, dass ein Pfad ohne .gpkg-Endung als ungültig gilt, auch bei vorhandenem Verzeichnis. | grün |
+| `TestDateipruefer.test_ist_gueltiger_gpkg_pfad_lehnt_fehlendes_verzeichnis_ab` | Prüft, dass ein .gpkg-Pfad in einem nicht vorhandenen Verzeichnis als ungültig gilt. | grün |
+| `TestDateipruefer.test_ist_leer_erkennt_none_und_nicht_string_als_leer` | Prüft, dass _ist_leer sowohl None als auch einen Nicht-String-Wert als leer meldet. | grün |
+| `TestDateipruefer.test_ist_leer_erkennt_reinen_whitespace_als_leer` | Prüft, dass _ist_leer eine Eingabe aus reinen Leerzeichen als leer meldet. | grün |
+| `TestDateipruefer.test_ist_leer_meldet_pfad_mit_sichtbaren_zeichen_nicht_als_leer` | Prüft, dass _ist_leer einen Pfad mit sichtbaren Zeichen nicht als leer meldet. | grün |
+| `TestDateipruefer.test_leere_eingabe_bevorzugt_leereingabe_erlaubt_vor_standarddatei` | Prüft, dass bei leerer Eingabe leereingabe_erlaubt Vorrang vor einer gesetzten Standarddatei hat. | grün |
+| `TestDateipruefer.test_leere_eingabe_bevorzugt_standarddatei_vor_temporaer_erlaubt` | Prüft, dass bei leerer Eingabe eine gesetzte Standarddatei Vorrang vor temporaer_erlaubt hat. | grün |
+
+### `Plugin_SN_Basis/tests/test_datenabruf.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/Datenabruf.py (Datenabruf-Kernpipeline, Issue #63).
+
+Testgruppen:
+
+- `DetermineSpatialFilterTests` (7 Testfälle): — (Docstring fehlt)
+- `BuildProviderUrlTests` (9 Testfälle): — (Docstring fehlt)
+- `FetchFeaturesTests` (5 Testfälle): — (Docstring fehlt)
+- `DatenabrufOrchestrierungTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `DetermineSpatialFilterTests.test_raumfilter_ohne_liefert_none` | Prüft, dass Raumfilter 'ohne' unabhängig vom Layer immer None liefert. | grün |
+| `DetermineSpatialFilterTests.test_fehlender_layer_liefert_none` | Prüft, dass ohne Verfahrensgebiet-Layer kein Raumfilter gebaut wird. | grün |
+| `DetermineSpatialFilterTests.test_unbekannter_raumfilter_liefert_none` | Prüft, dass ein unbekannter Raumfilter-Wert None liefert, statt einen Fehler zu werfen. | grün |
+| `DetermineSpatialFilterTests.test_verfahrensgebiet_liefert_extent_und_crs` | Prüft, dass Raumfilter 'Verfahrensgebiet' Extent und CRS des Verfahrensgebiet-Layers liefert. | grün |
+| `DetermineSpatialFilterTests.test_verfahrensgebiet_ohne_extent_liefert_none` | Prüft, dass ein Verfahrensgebiet-Layer ohne ermittelbaren Extent None liefert. | grün |
+| `DetermineSpatialFilterTests.test_pufferlayer_liefert_extent_des_pufferlayers` | Prüft, dass Raumfilter 'Pufferlayer' Extent und CRS des erzeugten Pufferlayers liefert. | grün |
+| `DetermineSpatialFilterTests.test_pufferlayer_ohne_puffer_liefert_none` | Prüft, dass 'Pufferlayer' None liefert, wenn kein Pufferlayer erzeugt werden konnte. | grün |
+| `BuildProviderUrlTests.test_wms_liefert_link_unveraendert` | Prüft, dass bei Provider WMS der Link unverändert durchgereicht wird, auch mit Raumfilter. | grün |
+| `BuildProviderUrlTests.test_url_praefix_wird_entfernt` | Prüft, dass ein vorangestelltes 'url=' aus dem Link entfernt wird. | grün |
+| `BuildProviderUrlTests.test_wfs_baut_standardparameter_und_bbox_mit_crs` | Prüft, dass WFS-Abrufe Standardparameter, umbenanntes TYPENAMES und BBOX mit CRS-URN erhalten. | grün |
+| `BuildProviderUrlTests.test_wfs_repariert_doppeltes_fragezeichen` | Prüft, dass ein zweites '?' im WFS-Link zu '&' korrigiert wird, statt eine ungültige URL zu erzeugen. | grün |
+| `BuildProviderUrlTests.test_wfs_ohne_raumfilter_setzt_keine_bbox` | Prüft, dass WFS ohne Raumfilter (bbox_geom=None) keinen BBOX-Parameter setzt. | grün |
+| `BuildProviderUrlTests.test_rest_provider_haengt_query_endpunkt_an` | Prüft, dass REST/ArcGIS-Links ohne '/query'-Endpunkt automatisch ergänzt werden. | grün |
+| `BuildProviderUrlTests.test_rest_provider_mit_bbox_baut_geometry_envelope_und_srid` | Prüft, dass REST-Abrufe mit Raumfilter eine Geometry-Envelope und inSR/outSR aus dem CRS setzen. | grün |
+| `BuildProviderUrlTests.test_default_provider_haengt_generischen_bbox_parameter_an` | Prüft, dass ein unbekannter Provider mit Raumfilter einen generischen bbox-Parameter erhält. | grün |
+| `BuildProviderUrlTests.test_default_provider_ohne_raumfilter_laesst_query_unveraendert` | Prüft, dass ein unbekannter Provider ohne Raumfilter keinen bbox-Parameter ergänzt. | grün |
+| `FetchFeaturesTests.test_wms_liefert_keine_features_ohne_fehler` | Prüft, dass bei Provider WMS kein Featureabruf stattfindet und kein Fehler gemeldet wird. | grün |
+| `FetchFeaturesTests.test_abbruch_vor_abruf_liefert_fehlermeldung` | Prüft, dass ein bereits gesetzter Abbruch vor dem eigentlichen Abruf erkannt wird. | grün |
+| `FetchFeaturesTests.test_json_featurecollection_wird_geparst` | Prüft, dass eine JSON-FeatureCollection-Antwort in eine Feature-Liste umgewandelt wird. | grün |
+| `FetchFeaturesTests.test_netzwerkfehler_wird_als_fehlermeldung_gemeldet` | Prüft, dass ein fehlgeschlagener HTTP-Abruf eine Fehlermeldung statt einer Ausnahme liefert. | grün |
+| `FetchFeaturesTests.test_wfs_xml_liefert_anzahl_aus_numberofmatched` | Prüft, dass eine WFS-XML-Antwort ohne JSON die Trefferzahl aus numberOfMatched übernimmt. | grün |
+| `DatenabrufOrchestrierungTests.test_fehlende_pflichtfelder_werden_als_ungueltig_gemeldet` | Prüft, dass eine Zeile ohne Link/Provider mit Aktion 'pflichtfelder_fehlen' gemeldet und übersprungen wird. | grün |
+| `DatenabrufOrchestrierungTests.test_wms_zeile_ohne_features_landet_in_ausserhalb` | Prüft, dass eine erfolgreich abgerufene WMS-Zeile mit 0 Features als 'ausserhalb' gezählt wird. | grün |
+| `DatenabrufOrchestrierungTests.test_abbruch_ueber_progress_stoppt_weitere_zeilen` | Prüft, dass ein Abbruch über das Progress-Objekt die Verarbeitung weiterer Zeilen stoppt. | grün |
+
+### `Plugin_SN_Basis/tests/test_datenschreiber.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/Datenschreiber.py (Schreib-/Ladepipeline, Issue #63).
+
+Testgruppen:
+
+- `WriteLayerToGpkgTests` (2 Testfälle): — (Docstring fehlt)
+- `ResolveStylePathTests` (3 Testfälle): — (Docstring fehlt)
+- `SchreibeDatenTests` (4 Testfälle): — (Docstring fehlt)
+- `LadeLayerTests` (3 Testfälle): — (Docstring fehlt)
+- `SchreibeLogTests` (2 Testfälle): — (Docstring fehlt)
+- `StoreStyleInGpkgTests` (4 Testfälle): Tests für Datenschreiber._store_style_in_gpkg (Issue #70, nachgezogen aus #63).
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `WriteLayerToGpkgTests.test_ungueltiger_layer_liefert_fehlermeldung` | Prüft, dass ein ungültiger Layer eine Fehlermeldung statt eines Absturzes liefert. | grün |
+| `WriteLayerToGpkgTests.test_gueltiger_layer_wird_ohne_fehler_geschrieben` | Prüft, dass ein gültiger Layer im Mock-Modus ohne Fehlermeldung geschrieben wird. | grün |
+| `ResolveStylePathTests.test_leerer_stilpfad_liefert_none` | Prüft, dass ein leerer oder fehlender Stilpfad None liefert. | grün |
+| `ResolveStylePathTests.test_absoluter_vorhandener_stilpfad_wird_aufgeloest` | Prüft, dass ein absoluter, vorhandener Stilpfad normalisiert und unverändert erkannt wird. | grün |
+| `ResolveStylePathTests.test_nicht_vorhandener_stilpfad_liefert_none` | Prüft, dass ein nicht vorhandener Stilpfad None liefert, statt einen ungültigen Pfad zurückzugeben. | grün |
+| `SchreibeDatenTests.test_ohne_speicherort_wirft_valueerror` | Prüft, dass schreibe_Daten ohne Speicherort einen ValueError wirft. | grün |
+| `SchreibeDatenTests.test_eintrag_ohne_gueltigen_layer_wird_uebersprungen` | Prüft, dass ein Eintrag ohne gültigen Layer übersprungen wird und ein Fehlerergebnis meldet. | grün |
+| `SchreibeDatenTests.test_erfolgreicher_eintrag_liefert_layer_path_und_thema` | Prüft, dass ein gültiger Layer einen Layer-Path mit GPKG-Pfad und Layername liefert. | grün |
+| `SchreibeDatenTests.test_ohne_thema_wird_ident_als_layername_verwendet` | Prüft, dass ohne passendes Thema in processed_results der ident als Layername verwendet wird. | grün |
+| `LadeLayerTests.test_ohne_layer_path_wird_uebersprungen` | Prüft, dass ein Eintrag ohne layer_path übersprungen wird, ohne einen Fehler zu melden. | grün |
+| `LadeLayerTests.test_gueltiger_layer_wird_dem_projekt_hinzugefuegt` | Prüft, dass ein gültig ladbarer Layer ohne Fehlermeldung verarbeitet wird. | grün |
+| `LadeLayerTests.test_ungueltiger_layer_meldet_layer_nicht_gefunden` | Prüft, dass ein ungültig ladbarer Layer als 'layer_nicht_gefunden' gemeldet wird. | grün |
+| `SchreibeLogTests.test_ohne_speicherort_wirft_valueerror` | Prüft, dass schreibe_log ohne Speicherort einen ValueError wirft. | grün |
+| `SchreibeLogTests.test_schreibt_json_datei_mit_ergebnissen_und_gibt_pfad_zurueck` | Prüft, dass schreibe_log eine lesbare JSON-Log-Datei erzeugt und deren Pfad zurückgibt. | grün |
+| `StoreStyleInGpkgTests.test_legt_tabelle_an_und_speichert_stil_als_default` | Prüft, dass _store_style_in_gpkg die layer_styles-Tabelle anlegt und den Stil als Default speichert. | grün |
+| `StoreStyleInGpkgTests.test_setzt_bisherigen_default_stil_desselben_layers_zurueck` | Prüft, dass _store_style_in_gpkg den useAsDefault-Wert vorhandener Stile desselben Layers zurücksetzt. | grün |
+| `StoreStyleInGpkgTests.test_liest_geometriespalte_ueber_geometrycolumn_aus` | Prüft, dass _store_style_in_gpkg die Geometriespalte eines Layers mit geometryColumn() übernimmt. | grün |
+| `StoreStyleInGpkgTests.test_fehlender_stilpfad_meldet_fehler_ueber_pruefmanager_statt_absturz` | Prüft, dass ein nicht lesbarer Stilpfad über den Pruefmanager gemeldet wird statt eine Exception zu werfen. | grün |
+
+### `Plugin_SN_Basis/tests/test_dockmanager.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/dockmanager.py (nur ein Dock gleichzeitig sichtbar, Issue #65).
+
+Testgruppen:
+
+- `DockManagerShowTests` (6 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `DockManagerShowTests.test_none_dock_meldet_fehler_und_zeigt_nichts_an` | Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt. | grün |
+| `DockManagerShowTests.test_setzt_objectname_wenn_leer` | Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält. | grün |
+| `DockManagerShowTests.test_entfernt_andere_sn_docks_aber_nicht_sich_selbst` | Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben. | grün |
+| `DockManagerShowTests.test_zeigt_neues_dock_mit_default_area_an` | Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird. | grün |
+| `DockManagerShowTests.test_fehler_beim_entfernen_wird_als_warnung_gemeldet` | Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird. | grün |
+| `DockManagerShowTests.test_fehler_beim_anzeigen_wird_gemeldet` | Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_excel_importer.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).
+
+Testgruppen:
+
+- `ImportXlsxTests` (5 Testfälle): — (Docstring fehlt)
+- `ExcelImporterKonstruktorTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `ImportXlsxTests.test_erfolgreicher_import_liefert_zeilen_als_dicts` | Prüft, dass eine gültige Excel-Datei als Liste von Dicts mit den Header-Spalten geliefert wird. | grün |
+| `ImportXlsxTests.test_fehlende_datei_liefert_leere_liste` | Prüft, dass eine nicht vorhandene Datei eine leere Liste liefert, statt eine Ausnahme zu werfen. | grün |
+| `ImportXlsxTests.test_arbeitsblatt_ohne_header_liefert_leere_liste_und_meldet_kein_header` | Prüft, dass ein leeres Arbeitsblatt ohne Header-Zeile 'kein_header' meldet und eine leere Liste liefert. | grün |
+| `ImportXlsxTests.test_header_zeile_nur_leere_werte_liefert_kein_header` | Prüft, dass eine Header-Zeile aus ausschließlich leeren Zellen ebenfalls als 'kein_header' gilt. | grün |
+| `ImportXlsxTests.test_nicht_lesbare_datei_liefert_leere_liste_und_meldet_read_error` | Prüft, dass eine Datei mit ungültigem Excel-Inhalt 'read_error' meldet und eine leere Liste liefert. | grün |
+| `ExcelImporterKonstruktorTests.test_ohne_dateipfad_wirft_valueerror` | Prüft, dass ExcelImporter ohne Dateipfad einen ValueError wirft. | grün |
+| `ExcelImporterKonstruktorTests.test_ohne_pruefmanager_wirft_valueerror` | Prüft, dass ExcelImporter ohne Pruefmanager einen ValueError wirft. | grün |
 
 ### `Plugin_SN_Basis/tests/test_laenderkennung.py`
 
@@ -83,11 +364,65 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestGetLaenderkennung.test_bekannter_praefix_gibt_kennung_zurueck` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_praefix_gross_klein_unabhaengig` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_unbekannter_praefix_wirft_keyerror` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_mapping_enthaelt_sachsen` | — (Docstring fehlt) | grün |
+| `TestGetLaenderkennung.test_bekannter_praefix_gibt_kennung_zurueck` | Prüft, dass der Präfix 'sn' die ALKIS-Länderkennung '14' für Sachsen liefert. | grün |
+| `TestGetLaenderkennung.test_praefix_gross_klein_unabhaengig` | Prüft, dass die Präfixsuche groß-/kleinschreibungsunabhängig ist. | grün |
+| `TestGetLaenderkennung.test_unbekannter_praefix_wirft_keyerror` | Prüft, dass ein unbekannter Präfix einen KeyError auslöst. | grün |
+| `TestGetLaenderkennung.test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet` | Prüft, dass ohne Argument der Präfix aus dem Modulnamen des Aufrufers abgeleitet wird. | grün |
+| `TestGetLaenderkennung.test_mapping_enthaelt_sachsen` | Prüft, dass die Mapping-Tabelle LAENDERKENNUNGEN einen Eintrag für Sachsen ('sn' -> '14') enthält. | grün |
+
+### `Plugin_SN_Basis/tests/test_landkreise.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/landkreise.py::_lade_landkreise (Issue #65).
+
+Testgruppen:
+
+- `LadeLandkreiseTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `LadeLandkreiseTests.test_laedt_die_echte_landkreisliste_nicht_leer` | Prüft, dass _lade_landkreise die echte landkreise.json als nicht-leere Liste von (Kennzahl, Name)-Tupeln liefert. | grün |
+| `LadeLandkreiseTests.test_fehlende_datei_liefert_leere_liste` | Prüft, dass eine nicht lesbare Datenquelle eine leere Liste liefert, statt eine Ausnahme zu werfen. | grün |
+
+### `Plugin_SN_Basis/tests/test_layerloader.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/LayerLoader.py (Layer erzeugen/filtern/laden, Issue #63).
+
+Testgruppen:
+
+- `CreateLayerTests` (7 Testfälle): — (Docstring fehlt)
+- `TransformGeometryTests` (3 Testfälle): — (Docstring fehlt)
+- `TransformExtentTests` (2 Testfälle): — (Docstring fehlt)
+- `FilterByExtentTests` (6 Testfälle): — (Docstring fehlt)
+- `FilterByLayerTests` (4 Testfälle): — (Docstring fehlt)
+- `AddToProjectTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `CreateLayerTests.test_wfs_baut_uri_ohne_getfeature_parameter` | Prüft, dass für WFS die GetFeature-spezifischen Parameter aus der URI entfernt werden. | grün |
+| `CreateLayerTests.test_wfs_mit_url_praefix_wird_nicht_doppelt_gesetzt` | Prüft, dass ein bereits vorhandenes 'url='-Präfix im WFS-Link nicht verdoppelt wird. | grün |
+| `CreateLayerTests.test_wms_baut_url_praefix_und_nutzt_rasterlayer` | Prüft, dass WMS-Layer über QgsRasterLayer mit 'url='-Präfix erzeugt werden. | grün |
+| `CreateLayerTests.test_rest_featureserver_ergaenzt_layer_index` | Prüft, dass ein REST-Link auf '.../FeatureServer' automatisch um '/0' ergänzt wird. | grün |
+| `CreateLayerTests.test_ogr_provider_nutzt_link_direkt` | Prüft, dass ein OGR-Provider den Link unverändert an QgsVectorLayer übergibt. | grün |
+| `CreateLayerTests.test_ungueltiger_layer_meldet_layer_nicht_verfuegbar` | Prüft, dass ein ungültig erzeugter Layer als 'layer_nicht_verfuegbar' gemeldet wird und None liefert. | grün |
+| `CreateLayerTests.test_ausnahme_beim_erstellen_wird_abgefangen` | Prüft, dass eine Ausnahme beim Layer-Erstellen abgefangen und als Fehlerergebnis gemeldet wird. | grün |
+| `TransformGeometryTests.test_fehlender_layer_liefert_geometrie_unveraendert` | Prüft, dass ohne Quell- oder Ziel-Layer die Geometrie unverändert zurückgegeben wird. | grün |
+| `TransformGeometryTests.test_gleiche_crs_liefert_geometrie_unveraendert` | Prüft, dass bei identischem CRS von Quelle und Ziel keine Transformation stattfindet. | grün |
+| `TransformGeometryTests.test_unterschiedliche_crs_klont_und_transformiert_geometrie` | Prüft, dass bei unterschiedlichem CRS eine geklonte, transformierte Geometrie geliefert wird. | grün |
+| `TransformExtentTests.test_gleiche_crs_liefert_extent_unveraendert` | Prüft, dass bei identischem CRS von Quelle und Ziel der Extent unverändert bleibt. | grün |
+| `TransformExtentTests.test_unterschiedliche_crs_nutzt_coordinate_transform` | Prüft, dass bei unterschiedlichem CRS der Extent über QgsCoordinateTransform umgerechnet wird. | grün |
+| `FilterByExtentTests.test_ungueltiger_layer_wird_unveraendert_zurueckgegeben` | Prüft, dass ein ungültiger Layer von filter_by_extent unverändert zurückgegeben wird. | grün |
+| `FilterByExtentTests.test_ohne_extent_wird_layer_unveraendert_zurueckgegeben` | Prüft, dass ohne Extent (None) der Layer unverändert zurückgegeben wird. | grün |
+| `FilterByExtentTests.test_falscher_layertyp_wird_unveraendert_zurueckgegeben` | Prüft, dass ein Nicht-Vektor-Layer (z. B. Rasterlayer) unverändert zurückgegeben wird. | grün |
+| `FilterByExtentTests.test_keine_treffer_liefert_none` | Prüft, dass ein Raumfilter ohne Treffer None statt eines leeren Layers liefert. | grün |
+| `FilterByExtentTests.test_abbruch_liefert_none_und_meldet_needs_user_action` | Prüft, dass ein Abbruch während des Raumfilters None liefert und gemeldet wird. | grün |
+| `FilterByExtentTests.test_erfolgreicher_filter_liefert_neuen_layer_mit_features` | Prüft, dass ein erfolgreicher BBOX-Filter einen neuen Speicherlayer mit den gefundenen Features liefert. | grün |
+| `FilterByLayerTests.test_ungueltiger_layer_oder_filterlayer_wird_unveraendert_zurueckgegeben` | Prüft, dass ein ungültiger Haupt- oder Filter-Layer unverändert zurückgegeben wird. | grün |
+| `FilterByLayerTests.test_filterlayer_ohne_features_liefert_none` | Prüft, dass ein Filter-Layer ohne Features (leere Vereinigungsgeometrie) None liefert. | grün |
+| `FilterByLayerTests.test_erfolgreicher_geometriefilter_liefert_neuen_layer` | Prüft, dass ein erfolgreicher Geometriefilter einen neuen Speicherlayer mit den passenden Features liefert. | grün |
+| `FilterByLayerTests.test_keine_ueberschneidung_liefert_none` | Prüft, dass ohne überschneidende Features None statt eines leeren Layers geliefert wird. | grün |
+| `AddToProjectTests.test_gueltiger_layer_wird_dem_projekt_hinzugefuegt` | Prüft, dass ein gültiger Layer über QgsProject.instance().addMapLayer hinzugefügt wird. | grün |
+| `AddToProjectTests.test_ungueltiger_layer_wird_nicht_hinzugefuegt` | Prüft, dass ein ungültiger Layer nicht ans Projekt übergeben wird. | grün |
+| `AddToProjectTests.test_none_layer_wird_nicht_hinzugefuegt` | Prüft, dass ein fehlender Layer (None) nicht ans Projekt übergeben wird. | grün |
 
 ### `Plugin_SN_Basis/tests/test_layerpruefer.py`
 
@@ -99,16 +434,16 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestLayerpruefer.test_layer_exists` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_unsichtbar` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_falscher_layertyp` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_falscher_geotyp` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_leer` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_falsches_crs` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_felder_fehlen` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_datenquelle_unerwartet` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_nicht_editierbar` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_ok` | — (Docstring fehlt) | grün |
+| `TestLayerpruefer.test_layer_exists` | Prüft, dass ein nicht vorhandener Layer als 'layer_nicht_gefunden' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_unsichtbar` | Prüft, dass ein unsichtbarer Layer bei geforderter Sichtbarkeit als 'layer_unsichtbar' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_falscher_layertyp` | Prüft, dass ein Layer mit abweichendem Layertyp (Raster statt Vektor) als 'falscher_layertyp' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_falscher_geotyp` | Prüft, dass ein Layer mit abweichendem Geometrietyp (Punkt statt Polygon) als 'falscher_geotyp' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_leer` | Prüft, dass ein Layer ohne Features als 'layer_leer' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_falsches_crs` | Prüft, dass ein Layer mit abweichendem Koordinatenbezugssystem als 'falsches_crs' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_felder_fehlen` | Prüft, dass ein Layer mit fehlenden Pflichtfeldern als 'felder_fehlen' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_datenquelle_unerwartet` | Prüft, dass eine Datenquelle außerhalb der erlaubten Liste als 'datenquelle_unerwartet' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_nicht_editierbar` | Prüft, dass ein nicht editierbarer Layer bei geforderter Editierbarkeit als 'layer_nicht_editierbar' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_ok` | Prüft, dass ein Layer, der alle Anforderungen erfüllt, als 'ok' bestätigt wird. | grün |
 
 ### `Plugin_SN_Basis/tests/test_linkpruefer.py`
 
@@ -116,15 +451,108 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestLinkpruefer` (5 Testfälle): — (Docstring fehlt)
+- `TestLinkpruefer` (6 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestLinkpruefer.test_remote_link_ok` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_remote_link_error` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_remote_link_network_error` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_local_link_not_found` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_local_link_ok` | — (Docstring fehlt) | grün |
+| `TestLinkpruefer.test_leere_eingabe` | Prüft, dass eine leere Eingabe ohne Netzwerk-/Dateizugriff als 'leer' abgelehnt wird. | grün |
+| `TestLinkpruefer.test_remote_link_ok` | Prüft, dass eine erreichbare Remote-URL (HTTP HEAD ohne Fehler) als 'ok' bestätigt wird. | grün |
+| `TestLinkpruefer.test_remote_link_error` | Prüft, dass eine Remote-URL mit Fehlerantwort als 'url_nicht_erreichbar' abgelehnt wird. | grün |
+| `TestLinkpruefer.test_remote_link_network_error` | Prüft, dass eine ausbleibende Netzwerkantwort (None) als 'netzwerkfehler' gemeldet wird. | grün |
+| `TestLinkpruefer.test_local_link_not_found` | Prüft, dass ein nicht existierender lokaler Pfad als 'pfad_nicht_gefunden' abgelehnt wird. | grün |
+| `TestLinkpruefer.test_local_link_ok` | Prüft, dass ein existierender lokaler Pfad als 'ok' bestätigt wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_ly_existence_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/ly_existence_wrapper.py::layer_is_valid/layer_is_usable (Issue #65).
+
+Testgruppen:
+
+- `LayerIsValidTests` (5 Testfälle): — (Docstring fehlt)
+- `LayerIsUsableTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `LayerIsValidTests.test_none_ist_nicht_gueltig` | Prüft, dass layer_is_valid für None False liefert. | grün |
+| `LayerIsValidTests.test_objekt_ohne_isvalid_ist_nicht_gueltig` | Prüft, dass ein Objekt ohne isValid()-Methode als ungültig gilt. | grün |
+| `LayerIsValidTests.test_gueltiger_layer_liefert_true` | Prüft, dass ein Layer mit isValid()==True als gültig erkannt wird. | grün |
+| `LayerIsValidTests.test_ungueltiger_layer_liefert_false` | Prüft, dass ein Layer mit isValid()==False als ungültig erkannt wird. | grün |
+| `LayerIsValidTests.test_ausnahme_bei_isvalid_liefert_false` | Prüft, dass eine Ausnahme in isValid() abgefangen wird und False liefert. | grün |
+| `LayerIsUsableTests.test_none_ist_nicht_nutzbar` | Prüft, dass layer_is_usable für None False liefert. | grün |
+| `LayerIsUsableTests.test_gueltiger_layer_ist_nutzbar` | Prüft, dass ein vorhandener, gültiger Layer als nutzbar gilt. | grün |
+| `LayerIsUsableTests.test_ungueltiger_layer_ist_nicht_nutzbar` | Prüft, dass ein vorhandener, aber ungültiger Layer als nicht nutzbar gilt. | grün |
+
+### `Plugin_SN_Basis/tests/test_ly_style_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/ly_style_wrapper.py::apply_style_from_path (Issue #65).
+
+Testgruppen:
+
+- `ApplyStyleFromPathTests` (4 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `ApplyStyleFromPathTests.test_fehlender_layer_liefert_false` | Prüft, dass ein fehlender Layer (None) False liefert, ohne einen Stil zu laden. | grün |
+| `ApplyStyleFromPathTests.test_erfolgreiches_laden_liefert_true_und_triggert_repaint` | Prüft, dass ein erfolgreich geladener Stil True liefert und den Layer neu zeichnet. | grün |
+| `ApplyStyleFromPathTests.test_fehlgeschlagenes_laden_liefert_false_ohne_repaint` | Prüft, dass ein fehlgeschlagenes Laden False liefert, ohne den Layer neu zu zeichnen. | grün |
+| `ApplyStyleFromPathTests.test_ausnahme_beim_laden_wird_abgefangen` | Prüft, dass eine Ausnahme beim Laden des Stils abgefangen wird und False liefert. | grün |
+
+### `Plugin_SN_Basis/tests/test_message_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/message_wrapper.py (Issue #65).
+
+Testgruppen:
+
+- `GetMessageBarTests` (2 Testfälle): — (Docstring fehlt)
+- `PushMessageTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `GetMessageBarTests.test_liefert_objekt_mit_pushmessage_methode` | Prüft, dass _get_message_bar ein Objekt mit aufrufbarer pushMessage()-Methode liefert. | grün |
+| `GetMessageBarTests.test_faellt_bei_fehlerhaftem_iface_auf_mock_bar_zurueck` | Prüft, dass ein Fehler beim Zugriff auf iface.messageBar() auf eine funktionierende Mock-MessageBar zurückfällt. | grün |
+| `PushMessageTests.test_liefert_dict_mit_titel_text_level_und_dauer` | Prüft, dass push_message im Mock-Modus ein Dict mit den übergebenen Werten liefert. | grün |
+| `PushMessageTests.test_fehler_in_der_message_bar_wird_abgefangen` | Prüft, dass eine fehlschlagende MessageBar push_message nicht zum Absturz bringt, sondern None liefert. | grün |
+
+### `Plugin_SN_Basis/tests/test_navigation.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/navigation.py::Navigation (Issue #67, ohne init_ui – s. Docstring dort).
+
+Testgruppen:
+
+- `AddActionTests` (2 Testfälle): — (Docstring fehlt)
+- `FinalizeMenuAndToolbarTests` (2 Testfälle): — (Docstring fehlt)
+- `SetActivePluginTests` (2 Testfälle): — (Docstring fehlt)
+- `RemoveActionTests` (3 Testfälle): — (Docstring fehlt)
+- `RemoveAllTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `AddActionTests.test_ohne_plugin_group_liefert_none` | Prüft, dass add_action ohne initialisierte Aktionsgruppe None liefert und nichts anlegt. | grün |
+| `AddActionTests.test_erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu` | Prüft, dass add_action eine Action erzeugt, ihr die Gruppe zuweist und sie priorisiert in der Liste ablegt. | grün |
+| `FinalizeMenuAndToolbarTests.test_ohne_menu_oder_toolbar_tut_nichts` | Prüft, dass finalize_menu_and_toolbar ohne Menü oder Toolbar keine Ausnahme wirft. | grün |
+| `FinalizeMenuAndToolbarTests.test_sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu` | Prüft, dass finalize_menu_and_toolbar Aktionen nach Priorität sortiert in Menü und Toolbar einträgt. | grün |
+| `SetActivePluginTests.test_setzt_nur_aktive_action_auf_checked` | Prüft, dass set_active_plugin nur die übergebene Action markiert, alle anderen abwählt. | grün |
+| `SetActivePluginTests.test_ohne_aktive_action_setzt_alle_auf_false` | Prüft, dass set_active_plugin ohne aktive Action alle bestehenden Actions abwählt. | grün |
+| `RemoveActionTests.test_none_action_tut_nichts` | Prüft, dass remove_action mit None keine Ausnahme wirft und die Liste unverändert lässt. | grün |
+| `RemoveActionTests.test_entfernt_action_aus_menu_toolbar_und_liste` | Prüft, dass remove_action die Action aus Menü, Toolbar und der internen Liste entfernt. | grün |
+| `RemoveActionTests.test_ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste` | Prüft, dass remove_action ohne Menü/Toolbar trotzdem aus der internen Liste entfernt. | grün |
+| `RemoveAllTests.test_entfernt_menu_toolbar_und_setzt_zustand_zurueck` | Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt. | grün |
+| `RemoveAllTests.test_loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt` | Prüft, dass remove_all deleteLater() auf Menü und Toolbar aufruft, damit sie den Qt-Objektbaum tatsächlich verlassen (Issue #72). | grün |
+| `RemoveAllTests.test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf` | Prüft, dass remove_all ohne vorhandenes Menü/Toolbar die Wrapper nicht aufruft, aber die Liste leert. | grün |
+
+### `Plugin_SN_Basis/tests/test_os_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/os_wrapper.py::is_case_sensitive_fs (Issue #67).
+
+Testgruppen:
+
+- `IsCaseSensitiveFsTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `IsCaseSensitiveFsTests.test_windows_ist_nicht_case_sensitiv` | Prüft, dass Windows als nicht case-sensitives Dateisystem gilt. | grün |
+| `IsCaseSensitiveFsTests.test_macos_ist_nicht_case_sensitiv` | Prüft, dass macOS als nicht case-sensitives Dateisystem gilt. | grün |
+| `IsCaseSensitiveFsTests.test_linux_ist_case_sensitiv` | Prüft, dass Linux als case-sensitives Dateisystem gilt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_print_layout.py`
 
@@ -136,11 +564,11 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestPrintLayout.test_create_legend_item_links_map_and_enables_map_filtering` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_create_legend_item_does_not_copy_project_layers_into_legend_model` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_configure_legend_size_uses_content_adaptive_size` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_configure_hauptkarte_grids_uses_wrapper_constants` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme` | — (Docstring fehlt) | grün |
+| `TestPrintLayout.test_create_legend_item_links_map_and_enables_map_filtering` | Prüft, dass eine erzeugte Legende mit der übergebenen Karte verknüpft ist und automatisch nach deren Sichtbarkeit gefiltert wird. | grün |
+| `TestPrintLayout.test_create_legend_item_does_not_copy_project_layers_into_legend_model` | Prüft, dass eine neu erzeugte Legende keine Projekt-Layer in ihr Legendenmodell übernimmt (leere Root-Gruppe). | grün |
+| `TestPrintLayout.test_configure_legend_size_uses_content_adaptive_size` | Prüft, dass die Legendengröße inhaltsadaptiv bestimmt wird (kein manuelles Resize) und die ermittelte Höhe zurückgegeben wird. | grün |
+| `TestPrintLayout.test_configure_hauptkarte_grids_uses_wrapper_constants` | Prüft, dass das Gitternetz der Hauptkarte mit den erwarteten Stil-, Rahmen- und Beschriftungskonstanten (nur rechts/unten beschriftet, Schriftgröße und Hintergrund) konfiguriert wird. | grün |
+| `TestPrintLayout.test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme` | Prüft, dass ein einseitiges Layout die Hauptkarte auf das übergebene Kartenthema festlegt und die Legende mit dieser Karte verknüpft bleibt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_print_logic.py`
 
@@ -152,23 +580,105 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_escalates_to_a3` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_select_standard_page_layout_uses_a4_as_minimum_sheet` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_select_standard_page_layout_limits_hauptkarte_to_a3` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_select_standard_page_layout_returns_none_if_a3_is_too_small` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_for_small_page` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_keeps_large_page` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_does_plot_fit_page_true_for_standard_a4` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_does_plot_fit_page_false_if_objects_exceed_page` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_set_druckbereich_accepts_wie_kartenfenster` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_canvas_extent_returns_valid_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_resolve_extent_uses_canvas_extent_without_buffer` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_resolve_extent_uses_rectangle_extent_without_buffer` | — (Docstring fehlt) | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent` | Prüft, dass für einen kleinen Kartenausschnitt (Baufreigabe) das Format DIN A4 ohne Fallback gewählt wird. | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_escalates_to_a3` | Prüft, dass ein größerer Kartenausschnitt (Baufreigabe) auf DIN A3 eskaliert wird. | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback` | Prüft, dass bei auf DIN A4 begrenztem Höchstformat ein eigentlich A3-großer Ausschnitt mit Fallback-Kennzeichnung auf A4 reduziert wird. | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent` | Prüft, dass ein Ausschnitt, der auch DIN A3 übersteigt, mit Fallback-Kennzeichnung auf DIN A3 begrenzt wird. | grün |
+| `TestPrintLogic.test_select_standard_page_layout_uses_a4_as_minimum_sheet` | Prüft, dass für einen kleinen Ausschnitt trotz Höchstgrenze DIN A3 mindestens DIN A4 als Blattgröße gewählt wird. | grün |
+| `TestPrintLogic.test_select_standard_page_layout_limits_hauptkarte_to_a3` | Prüft, dass ein die Fläche voll ausnutzender Ausschnitt auf DIN A3 als größtes zulässiges Format begrenzt wird. | grün |
+| `TestPrintLogic.test_select_standard_page_layout_returns_none_if_a3_is_too_small` | Prüft, dass kein Layout gewählt wird (None), wenn der Ausschnitt selbst DIN A3 als Höchstformat übersteigt. | grün |
+| `TestPrintLogic.test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent` | Prüft, dass für einen kleinen Ausschnitt DIN A4 gewählt und das Einpassen (force_fit) erzwungen wird. | grün |
+| `TestPrintLogic.test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm` | Prüft, dass bei aktiviertem Formfaktor (Endlosrolle) die Seitenbreite auf die maximale Rollenbreite (2000 mm) begrenzt wird. | grün |
+| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_for_small_page` | Prüft, dass eine zu kleine Seite auf die A4-Mindestgröße angehoben und als erzwungen (forced) markiert wird. | grün |
+| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_keeps_large_page` | Prüft, dass eine bereits ausreichend große Seite (A3) unverändert bleibt und nicht als erzwungen markiert wird. | grün |
+| `TestPrintLogic.test_does_plot_fit_page_true_for_standard_a4` | Prüft, dass ein Kartenobjekt, das in eine DIN A4-Seite passt, als passend erkannt wird. | grün |
+| `TestPrintLogic.test_does_plot_fit_page_false_if_objects_exceed_page` | Prüft, dass ein Kartenobjekt, das die Seitenbreite überschreitet, als nicht passend erkannt wird. | grün |
+| `TestPrintLogic.test_set_druckbereich_accepts_wie_kartenfenster` | Prüft, dass die Auswahl 'wie Kartenfenster' als Projektvariable 'druckbereich' gespeichert wird. | grün |
+| `TestPrintLogic.test_get_canvas_extent_returns_valid_extent` | Prüft, dass der aktuelle Kartenausschnitt des Kartenfensters unverändert zurückgegeben wird. | grün |
+| `TestPrintLogic.test_resolve_extent_uses_canvas_extent_without_buffer` | Prüft, dass bei Druckbereich 'wie Kartenfenster' der Kartenausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird. | grün |
+| `TestPrintLogic.test_resolve_extent_uses_rectangle_extent_without_buffer` | Prüft, dass bei Druckbereich 'Rechteck' der gezeichnete Ausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_print_tab.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/tabs/print_tab.py::PrintTab (Issue #67).
+
+Testgruppen:
+
+- `SetServicesTests` (2 Testfälle): — (Docstring fehlt)
+- `RestoreStateTests` (2 Testfälle): — (Docstring fehlt)
+- `OnDruckpufferClickedTests` (3 Testfälle): — (Docstring fehlt)
+- `OnVorlageErstellenTests` (2 Testfälle): — (Docstring fehlt)
+- `ProjectSignalsTests` (3 Testfälle): — (Docstring fehlt)
+- `CleanupTests` (1 Testfälle): — (Docstring fehlt)
+- `ToggleAtlasTests` (2 Testfälle): — (Docstring fehlt)
+- `RestoreAtlasLayerSelectionTests` (2 Testfälle): — (Docstring fehlt)
+- `ThemeCollectionSignalsTests` (3 Testfälle): — (Docstring fehlt)
+- `RefreshThemaComboLiveTests` (2 Testfälle): — (Docstring fehlt)
+- `GetGespeicherteThemenTests` (2 Testfälle): — (Docstring fehlt)
+- `GetCurrentCanvasScaleTests` (3 Testfälle): — (Docstring fehlt)
+- `OnAtlasLayerChangedTests` (3 Testfälle): — (Docstring fehlt)
+- `OnAuswahlAsAtlasClickedTests` (2 Testfälle): — (Docstring fehlt)
+- `PopulateAtlasLayerComboTests` (2 Testfälle): — (Docstring fehlt)
+- `OnAtlasErzeugenChangedTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `SetServicesTests.test_ruft_alle_set_methoden_mit_combo_werten_auf` | Prüft, dass set_services alle set_*-Methoden von PrintLogic mit den aktuellen Combo-Werten aufruft. | grün |
+| `SetServicesTests.test_ohne_combos_ruft_keine_set_methode_auf` | Prüft, dass set_services ohne vorhandene Combos keine set_*-Methode von PrintLogic aufruft. | grün |
+| `RestoreStateTests.test_gueltige_gespeicherte_werte_werden_uebernommen` | Prüft, dass _restore_state gültige gespeicherte Werte in die jeweiligen Widgets übernimmt. | grün |
+| `RestoreStateTests.test_ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck` | Prüft, dass _restore_state ohne (gültige) gespeicherte Werte auf die Default-Konstanten zurückfällt. | grün |
+| `OnDruckpufferClickedTests.test_gueltige_zahl_setzt_variable_und_button_text` | Prüft, dass eine bestätigte, gültige Zahl den Druckpuffer speichert und den Button-Text aktualisiert. | grün |
+| `OnDruckpufferClickedTests.test_abgebrochen_aendert_nichts` | Prüft, dass ein abgebrochener Dialog den Druckpuffer nicht verändert. | grün |
+| `OnDruckpufferClickedTests.test_ungueltige_zahl_zeigt_hinweis_und_setzt_nichts` | Prüft, dass eine nicht-numerische Eingabe einen Hinweis zeigt und den Druckpuffer nicht setzt. | grün |
+| `OnVorlageErstellenTests.test_ruft_druckvorlage_anlegen_mit_combo_werten_auf` | Prüft, dass _on_vorlage_erstellen druckvorlage_anlegen mit den aktuellen Combo-Werten aufruft. | grün |
+| `OnVorlageErstellenTests.test_ohne_logic_tut_nichts` | Prüft, dass _on_vorlage_erstellen ohne gesetzte PrintLogic keine Ausnahme wirft. | grün |
+| `ProjectSignalsTests.test_connect_verbindet_alle_drei_signale` | Prüft, dass _connect_project_signals alle drei Projektsignale mit _on_project_changed verbindet. | grün |
+| `ProjectSignalsTests.test_disconnect_trennt_alle_drei_signale` | Prüft, dass _disconnect_project_signals alle drei zuvor verbundenen Projektsignale trennt. | grün |
+| `ProjectSignalsTests.test_instance_wirft_ausnahme_wird_abgefangen` | Prüft, dass eine Ausnahme bei QgsProject.instance() in beiden Signalmethoden abgefangen wird. | grün |
+| `CleanupTests.test_ruft_beide_disconnect_methoden_auf` | Prüft, dass cleanup sowohl die Theme- als auch die Projektsignale trennt. | grün |
+| `ToggleAtlasTests.test_checked_true_setzt_arrow_down_und_zeigt_inhalt` | Prüft, dass _toggle_atlas bei checked=True den Pfeil nach unten dreht und den Inhalt einblendet. | grün |
+| `ToggleAtlasTests.test_checked_false_setzt_arrow_right_und_versteckt_inhalt` | Prüft, dass _toggle_atlas bei checked=False den Pfeil nach rechts dreht und den Inhalt ausblendet. | grün |
+| `RestoreAtlasLayerSelectionTests.test_vorhandene_layer_id_setzt_layer_in_combo` | Prüft, dass eine gespeicherte Layer-ID den zugehörigen Layer im Atlas-Dropdown auswählt. | grün |
+| `RestoreAtlasLayerSelectionTests.test_leere_gespeicherte_layer_id_tut_nichts` | Prüft, dass eine leere gespeicherte Layer-ID das Projekt gar nicht erst anfragt. | grün |
+| `ThemeCollectionSignalsTests.test_connect_verbindet_alle_signale` | Prüft, dass _connect_theme_collection_signals alle Signale der Theme-Collection verbindet. | grün |
+| `ThemeCollectionSignalsTests.test_connect_ist_idempotent_wenn_schon_verbunden` | Prüft, dass _connect_theme_collection_signals bei bereits verbundenem Signal nichts erneut tut. | grün |
+| `ThemeCollectionSignalsTests.test_disconnect_trennt_alle_signale` | Prüft, dass _disconnect_theme_collection_signals alle verbundenen Theme-Signale trennt und zurücksetzt. | grün |
+| `RefreshThemaComboLiveTests.test_erhaelt_vorherige_auswahl_wenn_noch_vorhanden` | Prüft, dass _refresh_thema_combo_live die vorherige Auswahl beibehält, wenn sie noch existiert. | grün |
+| `RefreshThemaComboLiveTests.test_faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden` | Prüft, dass _refresh_thema_combo_live auf 'wie Kartenfenster' zurückfällt, wenn die Auswahl verschwunden ist. | grün |
+| `GetGespeicherteThemenTests.test_dedupliziert_und_filtert_leere_eintraege` | Prüft, dass _get_gespeicherte_themen Duplikate und leere/None-Einträge entfernt. | grün |
+| `GetGespeicherteThemenTests.test_fehler_liefert_leere_liste` | Prüft, dass ein Fehler beim Zugriff auf die Theme-Collection eine leere Liste liefert. | grün |
+| `GetCurrentCanvasScaleTests.test_liefert_skalierung_als_float` | Prüft, dass _get_current_canvas_scale die Canvas-Skalierung als float liefert. | grün |
+| `GetCurrentCanvasScaleTests.test_ohne_canvas_liefert_none` | Prüft, dass _get_current_canvas_scale ohne verfügbares Kartenfenster None liefert. | grün |
+| `GetCurrentCanvasScaleTests.test_skalierung_null_liefert_none` | Prüft, dass eine Skalierung von 0 als None statt als 0.0 gilt. | grün |
+| `OnAtlasLayerChangedTests.test_none_oder_leer_setzt_variable_leer` | Prüft, dass None oder ein leerer Wert die Abdeckungslayer-Variable leeren. | grün |
+| `OnAtlasLayerChangedTests.test_layer_objekt_mit_id_methode_setzt_dessen_id` | Prüft, dass ein echtes Layer-Objekt (mit id()-Methode) dessen ID speichert. | grün |
+| `OnAtlasLayerChangedTests.test_string_fallback_sucht_layer_ueber_namen` | Prüft, dass ohne id()-Methode über den Layernamen im Projekt gesucht und dessen ID gespeichert wird. | grün |
+| `OnAuswahlAsAtlasClickedTests.test_erfolgreicher_layer_aktualisiert_atlas_ui` | Prüft, dass ein erfolgreich erzeugter Atlas-Layer den Atlas-Bereich öffnet und ins Dropdown übernimmt. | grün |
+| `OnAuswahlAsAtlasClickedTests.test_ohne_erzeugten_layer_tut_nichts_weiter` | Prüft, dass ohne erzeugten Atlas-Layer keine UI-Aktualisierung stattfindet. | grün |
+| `PopulateAtlasLayerComboTests.test_befuellt_combo_ohne_druckrechteck_und_leere_namen` | Prüft, dass _populate_atlas_layer_combo alle Layernamen außer 'Druckrechteck' und leeren Namen einträgt. | grün |
+| `PopulateAtlasLayerComboTests.test_ohne_atlas_layer_combo_tut_nichts` | Prüft, dass _populate_atlas_layer_combo ohne Atlas-Dropdown keine Ausnahme wirft. | grün |
+| `OnAtlasErzeugenChangedTests.test_aktiviert_speichert_variable_und_aktiviert_widgets` | Prüft, dass ein aktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button aktiviert. | grün |
+| `OnAtlasErzeugenChangedTests.test_deaktiviert_speichert_variable_und_deaktiviert_widgets` | Prüft, dass ein deaktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button deaktiviert. | grün |
+| `OnAtlasErzeugenChangedTests.test_ohne_widgets_wirft_keine_ausnahme` | Prüft, dass _on_atlas_erzeugen_changed ohne Atlas-Combo/-Button keine Ausnahme wirft. | grün |
+
+### `Plugin_SN_Basis/tests/test_pruef_ergebnis.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung/dateipfad (Issue #65/#67).
+
+Testgruppen:
+
+- `IstVerfahrensDbAktionTests` (2 Testfälle): — (Docstring fehlt)
+- `ErlaubtePersistierungTests` (2 Testfälle): — (Docstring fehlt)
+- `DateipfadTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `IstVerfahrensDbAktionTests.test_liefert_true_fuer_alle_verfahrens_db_aktionen` | Prüft, dass ist_verfahrens_db_aktion für alle vier Verfahrens-DB-Aktionscodes True liefert. | grün |
+| `IstVerfahrensDbAktionTests.test_liefert_false_fuer_andere_aktionen` | Prüft, dass ist_verfahrens_db_aktion für Nicht-Verfahrens-DB-Aktionen und ohne Aktion False liefert. | grün |
+| `ErlaubtePersistierungTests.test_liefert_true_fuer_erzeugen_ueberschreiben_anhaengen` | Prüft, dass erlaubte_persistierung für Erzeugen/Überschreiben/Anhängen True liefert. | grün |
+| `ErlaubtePersistierungTests.test_liefert_false_fuer_ueberspringen_und_reines_existieren` | Prüft, dass erlaubte_persistierung bei Überspringen und reinem 'datei_existiert' False liefert. | grün |
+| `DateipfadTests.test_liefert_path_wenn_kontext_ein_path_ist` | Prüft, dass dateipfad den Kontext liefert, wenn dieser bereits ein Path-Objekt ist. | grün |
+| `DateipfadTests.test_liefert_none_wenn_kontext_kein_path_ist` | Prüft, dass dateipfad None liefert, wenn der Kontext kein Path-Objekt ist (z. B. String oder Layer). | grün |
 
 ### `Plugin_SN_Basis/tests/test_pruefmanager.py`
 
@@ -176,24 +686,88 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestPruefmanager` (14 Testfälle): — (Docstring fehlt)
+- `TestPruefmanager` (26 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestPruefmanager.test_ok` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_leereingabe_erlaubt_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_leereingabe_erlaubt_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_standarddatei_vorschlagen_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_standarddatei_vorschlagen_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_temporaer_erlaubt_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_temporaer_erlaubt_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_layer_unsichtbar_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_layer_unsichtbar_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_unbekannte_aktion` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_overwrite` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_append` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_cancel` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_in_non_qgis_mode_returns_input` | — (Docstring fehlt) | grün |
+| `TestPruefmanager.test_ok` | Prüft, dass ein bereits erfolgreiches Prüfergebnis ohne Nutzerinteraktion als 'ok' durchgereicht wird. | grün |
+| `TestPruefmanager.test_leereingabe_erlaubt_ja` | Prüft, dass bei erlaubter leerer Eingabe eine Ja-Antwort des Nutzers die Entscheidung 'ok' liefert. | grün |
+| `TestPruefmanager.test_leereingabe_erlaubt_nein` | Prüft, dass bei erlaubter leerer Eingabe eine Nein-Antwort die Aktion 'leereingabe_erlaubt' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_standarddatei_vorschlagen_ja` | Prüft, dass eine Ja-Antwort auf den Vorschlag der Standarddatei die Entscheidung 'ok' mit dem Standardpfad als Kontext liefert. | grün |
+| `TestPruefmanager.test_standarddatei_vorschlagen_nein` | Prüft, dass eine Nein-Antwort auf den Vorschlag der Standarddatei die Aktion 'standarddatei_vorschlagen' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_temporaer_erlaubt_ja` | Prüft, dass eine Ja-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erzeugen' auslöst. | grün |
+| `TestPruefmanager.test_temporaer_erlaubt_nein` | Prüft, dass eine Nein-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erlaubt' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_layer_unsichtbar_ja` | Prüft, dass eine Ja-Antwort bei unsichtbarem Layer den Layer sichtbar schaltet und die Entscheidung 'ok' liefert. | grün |
+| `TestPruefmanager.test_layer_unsichtbar_nein` | Prüft, dass eine Nein-Antwort bei unsichtbarem Layer die Aktion 'layer_unsichtbar' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_unbekannte_aktion` | Prüft, dass eine unbekannte Aktion eine Warnung auslöst und unverarbeitet als Fehlschlag zurückgegeben wird. | grün |
+| `TestPruefmanager.test_datei_existiert_overwrite` | Prüft, dass die Nutzerwahl 'überschreiben' bei bestehender Datei zur Aktion 'datei_existiert_ueberschreiben' führt. | grün |
+| `TestPruefmanager.test_datei_existiert_append` | Prüft, dass die Nutzerwahl 'anhängen' bei bestehender Datei zur Aktion 'datei_existiert_anhaengen' führt. | grün |
+| `TestPruefmanager.test_datei_existiert_cancel` | Prüft, dass die Nutzerwahl 'abbrechen' bei bestehender Datei zur Aktion 'datei_existiert_ueberspringen' führt. | grün |
+| `TestPruefmanager.test_datei_existiert_in_non_qgis_mode_returns_input` | Prüft, dass außerhalb des QGIS-Modus bei bestehender Datei keine Rückfrage erfolgt, sondern die ursprüngliche Aktion unentschieden zurückgegeben wird. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_ergaenzen` | Prüft, dass die Nutzerwahl 'ergänzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ergaenzen' führt. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_abbrechen` | Prüft, dass die Nutzerwahl 'abbrechen' bei bestehendem Verfahrensgebiet ok=False mit Aktion 'verfahrensgebiet_abbrechen' liefert. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_entfernen` | Prüft, dass die Nutzerwahl 'entfernen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_entfernen' führt. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_ersetzen` | Prüft, dass die Nutzerwahl 'ersetzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ersetzen' führt. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_in_non_qgis_mode_returns_input` | Prüft, dass außerhalb des QGIS-Modus bei bestehendem Verfahrensgebiet keine Rückfrage erfolgt. | grün |
+| `TestPruefmanager.test_report_error_kritische_aktion_ruft_error_auf` | Prüft, dass eine kritische Aktion (z. B. 'netzwerkfehler') über error() gemeldet wird, nicht über warning(). | grün |
+| `TestPruefmanager.test_report_error_warn_aktion_ruft_warning_auf` | Prüft, dass eine Warn-Aktion (z. B. 'datei_nicht_gefunden') über warning() gemeldet wird, nicht über error(). | grün |
+| `TestPruefmanager.test_report_error_unbekannte_aktion_faellt_auf_warning_zurueck` | Prüft, dass eine nicht eingestufte Aktion als Fallback über warning() gemeldet wird. | grün |
+| `TestPruefmanager.test_frage_ja_nein_im_qgis_modus_fragt_nutzer` | Prüft, dass frage_ja_nein im QGIS-Modus den Nutzer fragt und dessen Antwort liefert. | grün |
+| `TestPruefmanager.test_frage_ja_nein_ausserhalb_qgis_liefert_default_ohne_rueckfrage` | Prüft, dass frage_ja_nein außerhalb des QGIS-Modus den Default-Wert liefert, ohne den Nutzer zu fragen. | grün |
+| `TestPruefmanager.test_frage_text_im_qgis_modus_fragt_nutzer` | Prüft, dass frage_text im QGIS-Modus den Nutzer fragt und Text plus OK-Status liefert. | grün |
+| `TestPruefmanager.test_frage_text_ausserhalb_qgis_liefert_default_ohne_rueckfrage` | Prüft, dass frage_text außerhalb des QGIS-Modus den Standardtext mit OK=True liefert, ohne den Nutzer zu fragen. | grün |
+
+### `Plugin_SN_Basis/tests/test_qgiscore_wrapper_helpers.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg/create_buffer_layer (Issue #65/#67).
+
+Testgruppen:
+
+- `PolygonZuLinieTests` (7 Testfälle): — (Docstring fehlt)
+- `LayerExistsInGpkgTests` (4 Testfälle): — (Docstring fehlt)
+- `CreateBufferLayerTests` (3 Testfälle): Deckt nur die Wächter ab: die eigentliche Pufferbildung importiert `qgis.core.QgsFeature`
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `PolygonZuLinieTests.test_none_liefert_none` | Prüft, dass polygon_zu_linie für None-Eingabe None liefert. | grün |
+| `PolygonZuLinieTests.test_leere_geometrie_liefert_none` | Prüft, dass eine leere Geometrie None liefert. | grün |
+| `PolygonZuLinieTests.test_stufe1_nutzt_boundary_wenn_vorhanden` | Prüft, dass eine Geometrie mit boundary() (QGIS 3.x-Pfad) deren Ergebnis direkt liefert. | grün |
+| `PolygonZuLinieTests.test_stufe2_extrahiert_ringe_bei_einfachem_polygon` | Prüft, dass ohne boundary() die Ringe eines einfachen Polygons manuell extrahiert werden. | grün |
+| `PolygonZuLinieTests.test_stufe2_extrahiert_alle_ringe_bei_multipolygon` | Prüft, dass bei einem MultiPolygon die Ringe aller Teilpolygone extrahiert werden. | grün |
+| `PolygonZuLinieTests.test_stufe3_fallback_wenn_keine_ringe_extrahierbar` | Prüft, dass bei fehlgeschlagener Ringextraktion auf convertToType zurückgefallen wird. | grün |
+| `PolygonZuLinieTests.test_alle_stufen_scheitern_liefert_none` | Prüft, dass polygon_zu_linie None liefert, wenn Ringextraktion und convertToType beide scheitern. | grün |
+| `LayerExistsInGpkgTests.test_fehlende_datei_liefert_false` | Prüft, dass eine nicht vorhandene GPKG-Datei False liefert. | grün |
+| `LayerExistsInGpkgTests.test_leerer_pfad_oder_layername_liefert_false` | Prüft, dass ein leerer Pfad oder Layername False liefert. | grün |
+| `LayerExistsInGpkgTests.test_vorhandener_layer_liefert_true` | Prüft, dass ein in gpkg_contents eingetragener Layername als vorhanden erkannt wird. | grün |
+| `LayerExistsInGpkgTests.test_fehlender_layer_liefert_false` | Prüft, dass ein nicht in gpkg_contents eingetragener Layername als nicht vorhanden gilt. | grün |
+| `CreateBufferLayerTests.test_ohne_qgis_liefert_none` | Prüft, dass create_buffer_layer ohne verfügbares QGIS None liefert. | grün |
+| `CreateBufferLayerTests.test_fehlender_source_layer_liefert_none` | Prüft, dass ein fehlender Quelllayer (None) None liefert, auch wenn QGIS verfügbar wäre. | grün |
+| `CreateBufferLayerTests.test_source_layer_ohne_getfeatures_liefert_none` | Prüft, dass ein Quelllayer ohne getFeatures()-Methode None liefert. | grün |
+
+### `Plugin_SN_Basis/tests/test_qgisui_wrapper_helpers.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project/find_dock_widgets/add_menu/remove_menu (Issue #65/#67).
+
+Testgruppen:
+
+- `AddLayerToProjectTests` (4 Testfälle): — (Docstring fehlt)
+- `FindDockWidgetsTests` (3 Testfälle): — (Docstring fehlt)
+- `AddRemoveMenuTests` (5 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `AddLayerToProjectTests.test_none_layer_liefert_false` | Prüft, dass add_layer_to_project für einen fehlenden Layer (None) False liefert. | grün |
+| `AddLayerToProjectTests.test_mock_modus_liefert_true_ohne_projektzugriff` | Prüft, dass im Mock-Modus (ohne QGIS) ein gültiger Layer True liefert, ohne das Projekt anzufassen. | grün |
+| `AddLayerToProjectTests.test_mit_qgis_fuegt_layer_dem_projekt_hinzu` | Prüft, dass bei verfügbarem QGIS der Layer über QgsProject.instance().addMapLayer hinzugefügt wird. | grün |
+| `AddLayerToProjectTests.test_mit_qgis_fehler_liefert_false` | Prüft, dass ein Fehler beim Hinzufügen zum Projekt abgefangen wird und False liefert. | grün |
+| `FindDockWidgetsTests.test_ohne_main_window_liefert_leere_liste` | Prüft, dass find_dock_widgets ohne verfügbares Hauptfenster eine leere Liste liefert. | grün |
+| `FindDockWidgetsTests.test_liefert_dock_widgets_des_main_window` | Prüft, dass find_dock_widgets die vom Hauptfenster gefundenen Dock-Widgets durchreicht. | grün |
+| `FindDockWidgetsTests.test_fehler_beim_suchen_liefert_leere_liste` | Prüft, dass ein Fehler beim Suchen der Dock-Widgets abgefangen wird und eine leere Liste liefert. | grün |
+| `AddRemoveMenuTests.test_add_menu_ohne_main_window_tut_nichts` | Prüft, dass add_menu ohne Hauptfenster keine Ausnahme wirft. | grün |
+| `AddRemoveMenuTests.test_add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu` | Prüft, dass ein echtes Qt-Menü (mit menuAction) der Menüleiste hinzugefügt wird. | grün |
+| `AddRemoveMenuTests.test_add_menu_ohne_menuaction_wird_ignoriert` | Prüft, dass ein Objekt ohne menuAction nicht an die Menüleiste übergeben wird. | grün |
+| `AddRemoveMenuTests.test_remove_menu_entfernt_ueber_menuaction` | Prüft, dass remove_menu das Menü über dessen menuAction() aus der Menüleiste entfernt. | grün |
+| `AddRemoveMenuTests.test_remove_menu_ohne_main_window_tut_nichts` | Prüft, dass remove_menu ohne Hauptfenster keine Ausnahme wirft. | grün |
 
 ### `Plugin_SN_Basis/tests/test_settings_logic.py`
 
@@ -205,10 +779,31 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestSettingsLogic.test_load` | — (Docstring fehlt) | grün |
-| `TestSettingsLogic.test_save` | — (Docstring fehlt) | grün |
-| `TestSettingsLogic.test_load_handles_partial_values_with_empty_fallback` | — (Docstring fehlt) | grün |
-| `TestSettingsLogic.test_save_empty_string_value_is_persisted` | — (Docstring fehlt) | grün |
+| `TestSettingsLogic.test_load` | Prüft, dass load() jede bekannte Projektvariable genau einmal über get_variable() liest. | grün |
+| `TestSettingsLogic.test_save` | Prüft, dass save() nur bekannte Variablen-Keys speichert und unbekannte Keys ignoriert. | grün |
+| `TestSettingsLogic.test_load_handles_partial_values_with_empty_fallback` | Prüft, dass load() für jede Variable den (auch leeren) Wert von get_variable() unverändert übernimmt, selbst wenn nicht alle Variablen im Mock konfiguriert sind. | grün |
+| `TestSettingsLogic.test_save_empty_string_value_is_persisted` | Prüft, dass save() einen leeren String als gültigen Wert speichert, statt ihn wie einen fehlenden Key zu behandeln. | grün |
+
+### `Plugin_SN_Basis/tests/test_settings_tab.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data/_build_input (Issue #65/#67).
+
+Testgruppen:
+
+- `SaveDataTests` (2 Testfälle): — (Docstring fehlt)
+- `LoadDataTests` (4 Testfälle): — (Docstring fehlt)
+- `BuildInputTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `SaveDataTests.test_sammelt_text_und_combobox_werte_inklusive_landkreiskennzahl` | Prüft, dass save_data Text- und Dropdown-Werte sammelt und die Landkreiskennzahl separat ergänzt. | grün |
+| `SaveDataTests.test_ohne_landkreis_dropdown_wird_keine_kennzahl_ergaenzt` | Prüft, dass ohne Landkreis-Dropdown im Formular keine 'landkreis_kennzahl' ergänzt wird. | grün |
+| `LoadDataTests.test_befuellt_textfelder_aus_gespeicherten_daten` | Prüft, dass load_data ein Textfeld mit dem gespeicherten Wert befüllt. | grün |
+| `LoadDataTests.test_ohne_gespeicherten_wert_bleibt_textfeld_leer` | Prüft, dass load_data ohne gespeicherten Wert das Textfeld auf einen leeren Text setzt. | grün |
+| `LoadDataTests.test_waehlt_combobox_eintrag_bei_bekanntem_namen` | Prüft, dass load_data im Landkreis-Dropdown den gespeicherten Landkreisnamen auswählt. | grün |
+| `LoadDataTests.test_unbekannter_gespeicherter_name_waehlt_ersten_eintrag` | Prüft, dass load_data bei einem nicht mehr vorhandenen Landkreisnamen auf den ersten Eintrag zurückfällt. | grün |
+| `BuildInputTests.test_landkreis_feld_liefert_combobox_mit_allen_landkreisen` | Prüft, dass _build_input für das Landkreis-Feld eine ComboBox mit Platzhalter und allen Landkreisen liefert. | grün |
+| `BuildInputTests.test_anderes_feld_liefert_lineedit` | Prüft, dass _build_input für ein normales Feld ein leeres Textfeld liefert. | grün |
 
 ### `Plugin_SN_Basis/tests/test_stilpruefer.py`
 
@@ -220,10 +815,30 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestStilpruefer.test_keine_datei_angegeben` | — (Docstring fehlt) | grün |
-| `TestStilpruefer.test_datei_existiert_mit_qml` | — (Docstring fehlt) | grün |
-| `TestStilpruefer.test_datei_existiert_falsche_endung` | — (Docstring fehlt) | grün |
-| `TestStilpruefer.test_datei_existiert_nicht` | — (Docstring fehlt) | grün |
+| `TestStilpruefer.test_keine_datei_angegeben` | Prüft, dass eine leere Pfadangabe als 'ok' ohne Kontext akzeptiert wird (Stil ist optional). | grün |
+| `TestStilpruefer.test_datei_existiert_mit_qml` | Prüft, dass eine existierende .qml-Datei als 'stil_anwendbar' bestätigt wird. | grün |
+| `TestStilpruefer.test_datei_existiert_falsche_endung` | Prüft, dass eine existierende Datei ohne .qml-Endung als 'falsche_endung' abgelehnt wird. | grün |
+| `TestStilpruefer.test_datei_existiert_nicht` | Prüft, dass ein nicht existierender .qml-Pfad als 'datei_nicht_gefunden' abgelehnt wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_sys_wrapper.py`
+
+Worum es in der Datei geht: Unit-Tests für sn_basis.functions.sys_wrapper.get_plugin_root().
+
+Testgruppen:
+
+- `TestGetPluginRoot` (1 Testfälle): — (Docstring fehlt)
+- `JoinPathTests` (1 Testfälle): — (Docstring fehlt)
+- `EnsureDirTests` (2 Testfälle): — (Docstring fehlt)
+- `GetattrSafeTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestGetPluginRoot.test_get_plugin_root_folgt_keiner_verzeichnisverknuepfung` | Prüft, dass get_plugin_root() bei einem über eine Verzeichnisverknüpfung (wie von link_qgis_profile.py angelegt) erreichten Modul den verlinkten Pfad liefert, statt zum echten Zielverzeichnis aufzulösen (Verfahrensgebiet #50). | grün |
+| `JoinPathTests.test_verbindet_mehrere_pfadbestandteile` | Prüft, dass join_path mehrere Bestandteile OS-sicher zu einem Path zusammenfügt. | grün |
+| `EnsureDirTests.test_legt_fehlendes_verzeichnis_inklusive_eltern_an` | Prüft, dass ensure_dir ein fehlendes Verzeichnis samt Elternverzeichnissen anlegt. | grün |
+| `EnsureDirTests.test_vorhandenes_verzeichnis_wirft_keinen_fehler` | Prüft, dass ensure_dir bei einem bereits vorhandenen Verzeichnis keinen Fehler wirft. | grün |
+| `GetattrSafeTests.test_liefert_vorhandenes_attribut` | Prüft, dass getattr_safe ein vorhandenes Attribut liefert. | grün |
+| `GetattrSafeTests.test_liefert_default_bei_fehlendem_attribut` | Prüft, dass getattr_safe bei fehlendem Attribut den Default-Wert liefert. | grün |
 
 ### `Plugin_SN_Basis/tests/test_text_wrapper.py`
 
@@ -253,32 +868,42 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestVariableWrapper.test_set_variable_project_noop_write_is_skipped` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_set_variable_project_changed_value_is_written` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_set_variable_global_writes_prefixed_name` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_get_variable_global_returns_empty_string_fallback` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_get_variable_invalid_scope_raises` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_set_variable_invalid_scope_raises` | — (Docstring fehlt) | grün |
+| `TestVariableWrapper.test_set_variable_project_noop_write_is_skipped` | Prüft, dass set_variable() im Projekt-Scope nicht schreibt, wenn der neue Wert dem bestehenden entspricht. | grün |
+| `TestVariableWrapper.test_set_variable_project_changed_value_is_written` | Prüft, dass set_variable() im Projekt-Scope bei geändertem Wert mit dem Präfix 'sn_' geschrieben wird. | grün |
+| `TestVariableWrapper.test_set_variable_global_writes_prefixed_name` | Prüft, dass set_variable() im globalen Scope den Variablennamen mit 'sn_' vorangestellt schreibt. | grün |
+| `TestVariableWrapper.test_get_variable_global_returns_empty_string_fallback` | Prüft, dass get_variable() im globalen Scope für eine nicht gesetzte Variable einen leeren String liefert. | grün |
+| `TestVariableWrapper.test_get_variable_invalid_scope_raises` | Prüft, dass get_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst. | grün |
+| `TestVariableWrapper.test_set_variable_invalid_scope_raises` | Prüft, dass set_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst. | grün |
 
 ### `Plugin_SN_Basis/tests/test_verfahrensgebiet_manager.py`
 
-Worum es in der Datei geht: — (Docstring fehlt)
+Worum es in der Datei geht: Tests für sn_basis/functions/verfahrensgebiet_manager.py (zentrale Layer-Verfolgung, Issue #65).
 
 Testgruppen:
 
 - `TestVerfahrensgebietManager` (5 Testfälle): — (Docstring fehlt)
+- `OnHandlerTests` (5 Testfälle): — (Docstring fehlt)
+- `ListenerSetupTests` (3 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_value` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_set_verfahrensgebiet_layer_id_writes_project_variable` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_update_without_layer_does_not_write_when_already_empty` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_update_with_layer_writes_variable` | — (Docstring fehlt) | grün |
+| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_value` | Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert. | grün |
+| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value` | Prüft, dass get_verfahrensgebiet_layer_id() bei leerer Projektvariable None statt eines leeren Strings liefert. | grün |
+| `TestVerfahrensgebietManager.test_set_verfahrensgebiet_layer_id_writes_project_variable` | Prüft, dass set_verfahrensgebiet_layer_id() die Layer-ID als Projektvariable 'verfahrensgebiet_layer' speichert. | grün |
+| `TestVerfahrensgebietManager.test_update_without_layer_does_not_write_when_already_empty` | Prüft, dass ohne gefundenen Verfahrensgebiet-Layer keine Schreibaktion erfolgt, wenn die Variable bereits leer ist. | grün |
+| `TestVerfahrensgebietManager.test_update_with_layer_writes_variable` | Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert. | grün |
+| `OnHandlerTests.test_on_project_changed_ohne_layer_setzt_variable_leer` | Prüft, dass _on_project_changed die Variable leert, wenn kein Verfahrensgebiet-Layer existiert. | grün |
+| `OnHandlerTests.test_on_project_changed_mit_layer_setzt_variable` | Prüft, dass _on_project_changed die Layer-ID eines gefundenen Verfahrensgebiet-Layers übernimmt. | grün |
+| `OnHandlerTests.test_on_layers_added_findet_neuen_layer` | Prüft, dass _on_layers_added nach neu hinzugefügten Layern sucht und die Variable aktualisiert. | grün |
+| `OnHandlerTests.test_on_layer_removed_bei_aktuellem_layer_sucht_neu` | Prüft, dass das Entfernen des aktuell aktiven Verfahrensgebiet-Layers eine Neusuche auslöst. | grün |
+| `OnHandlerTests.test_on_layer_removed_bei_anderem_layer_aendert_nichts` | Prüft, dass das Entfernen eines anderen Layers keine Neusuche auslöst. | grün |
+| `ListenerSetupTests.test_setup_ohne_qgis_tut_nichts` | Prüft, dass setup_verfahrensgebiet_listener ohne QGIS keine Signale registriert. | grün |
+| `ListenerSetupTests.test_setup_verbindet_signale_und_ist_idempotent` | Prüft, dass setup_verfahrensgebiet_listener die Projektsignale verbindet und ein zweiter Aufruf nichts doppelt registriert. | grün |
+| `ListenerSetupTests.test_teardown_trennt_alle_signale_und_ist_idempotent` | Prüft, dass teardown_verfahrensgebiet_listener alle Signale trennt und mehrfacher Aufruf nicht fehlschlägt. | grün |
 
 ## Paket `sn_verfahrensgebiet`
 
-Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 208 Testfällen (164 grün, 36 rot, 8 übersprungen).
+Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 208 Testfällen (166 grün, 34 rot, 8 übersprungen).
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
 
@@ -341,23 +966,22 @@ Worum es in der Datei geht: Unit-Tests für das flurstueck_alkis-Modul.
 
 Testgruppen:
 
-- `TestFlurstueckAlkis` (12 Testfälle): — (Docstring fehlt)
+- `TestFlurstueckAlkis` (11 Testfälle): — (Docstring fehlt)
 - `TestFlurstueckAlkisAnhaengenDedup` (3 Testfälle): Anhängen an einen bestehenden Flurstück-Layer (Issue #28).
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestFlurstueckAlkis.test_user_cancel_returns_none` | ask_text → ok=False, text="" → (None, NONE). | grün |
-| `TestFlurstueckAlkis.test_empty_text_after_confirmation_returns_none` | ask_text → ok=True, aber Text leer → (None, NONE). | grün |
-| `TestFlurstueckAlkis.test_invalid_wfs_layer_returns_none` | WFS-Layer nicht valide → (None, NONE). | grün |
-| `TestFlurstueckAlkis.test_success_returns_layer_and_first_status` | Kein bestehender Layer, WFS valide → (layer, FIRST). | rot (Baseline) |
-| `TestFlurstueckAlkis.test_sql_filter_contains_all_schluessel` | Der WFS-URI enthält alle eingegebenen Gemarkungsschlüssel. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_existing_layer_reload_yes` | Bestehender Layer + Nutzer: 'Ja, neu laden' → LoadStatus.RELOAD. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_existing_layer_keep_no` | Bestehender Layer + Nutzer: 'Nein, behalten' → LoadStatus.KEEP. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_vorgabe_schluessel_prefills_default_text` | vorgabe_schluessel=['1234','5678'] → ask_text erhält '1234, 5678' als default_text. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_vorgabe_namen_appears_in_label` | vorgabe_namen=['Musterstadt'] → ask_text-label enthält den Namen. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_bbox_embedded_in_wfs_uri` | bbox-Parameter → WFS-URI enthält 'bbox=' Substring. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_backwards_compatible_no_new_params` | flurstueck_alkis(tab) ohne neue Parameter → kein Fehler. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_ohne_schluessel_abfrage_ueberspringt_ask_text` | ohne_schluessel_abfrage=True verwendet vorgabe_schluessel direkt, ohne ask_text aufzurufen. | grün |
+| `TestFlurstueckAlkis.test_user_cancel_returns_none` | Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert. | grün |
+| `TestFlurstueckAlkis.test_empty_text_after_confirmation_returns_none` | Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert. | grün |
+| `TestFlurstueckAlkis.test_invalid_wfs_layer_returns_none` | Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert. | grün |
+| `TestFlurstueckAlkis.test_success_returns_layer_and_first_status` | Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_sql_filter_contains_all_schluessel` | Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_existing_layer_reload_yes` | Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_existing_layer_abbrechen_returns_none` | Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird. | grün |
+| `TestFlurstueckAlkis.test_vorgabe_schluessel_prefills_default_text` | Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_vorgabe_namen_appears_in_label` | Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_backwards_compatible_no_new_params` | Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_ohne_schluessel_abfrage_ueberspringt_ask_text` | Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird. | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_append_ueberspringt_bereits_vorhandene_flurstuecke` | Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden. | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_append_ohne_kennzeichen_feld_haengt_alles_an` | Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten). | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_ohne_schluessel_abfrage_append_dedup` | ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47). | grün |
@@ -398,6 +1022,7 @@ Testgruppen:
 
 - `TestExtrahiereGmlIds` (5 Testfälle): — (Docstring fehlt)
 - `TestGrenzpunktAlkis` (7 Testfälle): — (Docstring fehlt)
+- `TestGrenzpunkteGemarkungsweiseStil` (1 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -413,6 +1038,7 @@ Testgruppen:
 | `TestGrenzpunktAlkis.test_grenzpunkt_batch_empty` | — (Docstring fehlt) | rot (Baseline) |
 | `TestGrenzpunktAlkis.test_success_returns_both_layers` | — (Docstring fehlt) | rot (Baseline) |
 | `TestGrenzpunktAlkis.test_extent_derived_from_vg_layer` | Wenn kein extent übergeben → Extent aus VG-Layer holen. | rot (Baseline) |
+| `TestGrenzpunkteGemarkungsweiseStil.test_stil_wird_nach_dem_laden_auf_den_ergebnis_layer_angewendet` | Prüft, dass grenzpunkte_gemarkungsweise() nach dem Laden apply_style() mit dem neuen ALKIS-Grenzpunkt-Stil auf den fertigen Join-Layer anwendet (#53). | grün |
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_knickpunkt_pruefung.py`
 
@@ -762,130 +1388,9 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 87 von 94 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 396 Testfällen ohne Beschreibung
 
-#### `Plugin_SN_Basis/tests/test_datagrabber.py` (6 von 6) – auch der Modul-Docstring fehlt
-
-- `TestDataGrabber.test_analyze_source_type_detects_excel`
-- `TestDataGrabber.test_analyze_source_type_returns_unknown_filetype`
-- `TestDataGrabber.test_analyze_source_type_detects_service_when_file_check_fails`
-- `TestDataGrabber.test_analyze_source_type_returns_unbekannte_quelle_on_full_failure`
-- `TestDataGrabber.test_run_returns_success_summary_with_valid_rows`
-- `TestDataGrabber.test_run_returns_failure_summary_without_rows`
-
-#### `Plugin_SN_Basis/tests/test_dateipruefer.py` (6 von 6) – auch der Modul-Docstring fehlt
-
-- `TestDateipruefer.test_leereingabe_erlaubt`
-- `TestDateipruefer.test_leereingabe_nicht_erlaubt`
-- `TestDateipruefer.test_standarddatei_vorschlagen`
-- `TestDateipruefer.test_temporaer_erlaubt`
-- `TestDateipruefer.test_datei_nicht_gefunden`
-- `TestDateipruefer.test_datei_ok`
-
-#### `Plugin_SN_Basis/tests/test_laenderkennung.py` (5 von 5)
-
-- `TestGetLaenderkennung.test_bekannter_praefix_gibt_kennung_zurueck`
-- `TestGetLaenderkennung.test_praefix_gross_klein_unabhaengig`
-- `TestGetLaenderkennung.test_unbekannter_praefix_wirft_keyerror`
-- `TestGetLaenderkennung.test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet`
-- `TestGetLaenderkennung.test_mapping_enthaelt_sachsen`
-
-#### `Plugin_SN_Basis/tests/test_layerpruefer.py` (10 von 10) – auch der Modul-Docstring fehlt
-
-- `TestLayerpruefer.test_layer_exists`
-- `TestLayerpruefer.test_layer_unsichtbar`
-- `TestLayerpruefer.test_falscher_layertyp`
-- `TestLayerpruefer.test_falscher_geotyp`
-- `TestLayerpruefer.test_layer_leer`
-- `TestLayerpruefer.test_falsches_crs`
-- `TestLayerpruefer.test_felder_fehlen`
-- `TestLayerpruefer.test_datenquelle_unerwartet`
-- `TestLayerpruefer.test_layer_nicht_editierbar`
-- `TestLayerpruefer.test_layer_ok`
-
-#### `Plugin_SN_Basis/tests/test_linkpruefer.py` (5 von 5) – auch der Modul-Docstring fehlt
-
-- `TestLinkpruefer.test_remote_link_ok`
-- `TestLinkpruefer.test_remote_link_error`
-- `TestLinkpruefer.test_remote_link_network_error`
-- `TestLinkpruefer.test_local_link_not_found`
-- `TestLinkpruefer.test_local_link_ok`
-
-#### `Plugin_SN_Basis/tests/test_print_layout.py` (5 von 5) – auch der Modul-Docstring fehlt
-
-- `TestPrintLayout.test_create_legend_item_links_map_and_enables_map_filtering`
-- `TestPrintLayout.test_create_legend_item_does_not_copy_project_layers_into_legend_model`
-- `TestPrintLayout.test_configure_legend_size_uses_content_adaptive_size`
-- `TestPrintLayout.test_configure_hauptkarte_grids_uses_wrapper_constants`
-- `TestPrintLayout.test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme`
-
-#### `Plugin_SN_Basis/tests/test_print_logic.py` (17 von 17) – auch der Modul-Docstring fehlt
-
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent`
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_escalates_to_a3`
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback`
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent`
-- `TestPrintLogic.test_select_standard_page_layout_uses_a4_as_minimum_sheet`
-- `TestPrintLogic.test_select_standard_page_layout_limits_hauptkarte_to_a3`
-- `TestPrintLogic.test_select_standard_page_layout_returns_none_if_a3_is_too_small`
-- `TestPrintLogic.test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent`
-- `TestPrintLogic.test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm`
-- `TestPrintLogic.test_apply_standard_minimum_a4_fallback_for_small_page`
-- `TestPrintLogic.test_apply_standard_minimum_a4_fallback_keeps_large_page`
-- `TestPrintLogic.test_does_plot_fit_page_true_for_standard_a4`
-- `TestPrintLogic.test_does_plot_fit_page_false_if_objects_exceed_page`
-- `TestPrintLogic.test_set_druckbereich_accepts_wie_kartenfenster`
-- `TestPrintLogic.test_get_canvas_extent_returns_valid_extent`
-- `TestPrintLogic.test_resolve_extent_uses_canvas_extent_without_buffer`
-- `TestPrintLogic.test_resolve_extent_uses_rectangle_extent_without_buffer`
-
-#### `Plugin_SN_Basis/tests/test_pruefmanager.py` (14 von 14) – auch der Modul-Docstring fehlt
-
-- `TestPruefmanager.test_ok`
-- `TestPruefmanager.test_leereingabe_erlaubt_ja`
-- `TestPruefmanager.test_leereingabe_erlaubt_nein`
-- `TestPruefmanager.test_standarddatei_vorschlagen_ja`
-- `TestPruefmanager.test_standarddatei_vorschlagen_nein`
-- `TestPruefmanager.test_temporaer_erlaubt_ja`
-- `TestPruefmanager.test_temporaer_erlaubt_nein`
-- `TestPruefmanager.test_layer_unsichtbar_ja`
-- `TestPruefmanager.test_layer_unsichtbar_nein`
-- `TestPruefmanager.test_unbekannte_aktion`
-- `TestPruefmanager.test_datei_existiert_overwrite`
-- `TestPruefmanager.test_datei_existiert_append`
-- `TestPruefmanager.test_datei_existiert_cancel`
-- `TestPruefmanager.test_datei_existiert_in_non_qgis_mode_returns_input`
-
-#### `Plugin_SN_Basis/tests/test_settings_logic.py` (4 von 4) – auch der Modul-Docstring fehlt
-
-- `TestSettingsLogic.test_load`
-- `TestSettingsLogic.test_save`
-- `TestSettingsLogic.test_load_handles_partial_values_with_empty_fallback`
-- `TestSettingsLogic.test_save_empty_string_value_is_persisted`
-
-#### `Plugin_SN_Basis/tests/test_stilpruefer.py` (4 von 4) – auch der Modul-Docstring fehlt
-
-- `TestStilpruefer.test_keine_datei_angegeben`
-- `TestStilpruefer.test_datei_existiert_mit_qml`
-- `TestStilpruefer.test_datei_existiert_falsche_endung`
-- `TestStilpruefer.test_datei_existiert_nicht`
-
-#### `Plugin_SN_Basis/tests/test_variable_wrapper.py` (6 von 6) – auch der Modul-Docstring fehlt
-
-- `TestVariableWrapper.test_set_variable_project_noop_write_is_skipped`
-- `TestVariableWrapper.test_set_variable_project_changed_value_is_written`
-- `TestVariableWrapper.test_set_variable_global_writes_prefixed_name`
-- `TestVariableWrapper.test_get_variable_global_returns_empty_string_fallback`
-- `TestVariableWrapper.test_get_variable_invalid_scope_raises`
-- `TestVariableWrapper.test_set_variable_invalid_scope_raises`
-
-#### `Plugin_SN_Basis/tests/test_verfahrensgebiet_manager.py` (5 von 5) – auch der Modul-Docstring fehlt
-
-- `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_value`
-- `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value`
-- `TestVerfahrensgebietManager.test_set_verfahrensgebiet_layer_id_writes_project_variable`
-- `TestVerfahrensgebietManager.test_update_without_layer_does_not_write_when_already_empty`
-- `TestVerfahrensgebietManager.test_update_with_layer_writes_variable`
+Keine Lücken.
 
 ### `sn_verfahrensgebiet` – 102 von 208 Testfällen ohne Beschreibung
 
@@ -931,7 +1436,7 @@ dann verschwindet er hier von selbst.
 - `TestGemarkungSchluesselnurNummern.test_returns_only_nummern`
 - `TestGemarkungSchluesselnurNummern.test_empty_input_returns_empty`
 
-#### `Plugin_SN_Verfahrensgebiet/tests/test_grenzpunkt_alkis.py` (6 von 12)
+#### `Plugin_SN_Verfahrensgebiet/tests/test_grenzpunkt_alkis.py` (6 von 13)
 
 - `TestGrenzpunktAlkis.test_no_vg_layer_returns_none`
 - `TestGrenzpunktAlkis.test_invalid_punktort_layer`
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index fa5e6db..1c11907 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -5,6 +5,13 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Umgebung und Werkzeuge
 
+- **`qt_wrapper` meldet unter QGIS 3.44 `QT_VERSION == 6`, obwohl Qt5 läuft.** Der „PyQt6“-Zweig importiert
+  `qgis.PyQt`, das unter QGIS 3 PyQt5 ist; die Qt6-Schreibweise der Enums (`Qt.ItemFlag.ItemIsUserCheckable`)
+  funktioniert mit PyQt 5.15 trotzdem. `QT_VERSION` also nicht als Qt-Hauptversion verstehen, nur als
+  „echtes Qt (≠ 0)“. (02.10.2026, QGIS 3.44.4 geprüft, Basis #88)
+- **Lücken im Mock-Modus:** `QgsGeometry` hat im Mock kein `fromRect`, `QgsLayoutPoint`/`QgsLayoutSize` haben
+  `x`/`width` als Attribute statt als Methoden; `QgsRectangle` hatte bis Basis #88 gar keinen Mock. Tests patchen
+  solche Stellen gezielt, statt die Produktionsfunktion umzubauen. (02.10.2026)
 - **QGIS 3.40 ist seit Januar 2026 End of Life.** Aktueller LTR ist 3.44, Stable 4.2 (wird 25.09.2026 LTR). `workspace.toml` und alle `metadata.template` stehen noch auf `3.40` – offene Produktentscheidung, siehe `docs/konzept/07_qgis_tests_plan.md` Abschnitt 2. (04.09.2026, QGIS-Release-Fahrplan)
 - **Zwei Qt-Welten.** QGIS 3.40/3.44 laufen mit Qt5/PyQt5, QGIS 4 mit Qt6/PyQt6. `qt_wrapper` lädt PyQt6 zuerst, dann PyQt5, sonst Mock. Enum-Zugriffe unterscheiden sich (`Qt.AlignmentFlag.AlignTop` statt `Qt.AlignTop`), deshalb nur Wrapper-Aliase verwenden. (09/2026, Code)
 - **Mock-Modus ist kein QGIS.** `python scripts/run_tests.py` läuft in Sekunden, prüft aber nur Logik gegen Mock-Klassen. Geometrieoperationen, WFS, Layouts und Signale müssen in QGIS geprüft werden (`tests/test_qgis.bat` unter Windows). Manuelle QGIS-Tests im PR dokumentieren. (09/2026)
@@ -19,6 +26,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## QGIS-Tests (geplant, siehe `docs/konzept/07_qgis_tests_plan.md`)
 
+- **Spike/Abnahme ohne QGIS-Oberfläche:** Skript mit `QT_QPA_PLATFORM=offscreen` über `python-qgis.bat` starten,
+  das Plugin-Repo per Junction (`mklink /J \sn_basis `) unter dem Paketnamen in `sys.path`
+  legen. Für Widgets und Dialoge `QgsApplication([], True)` (mit GUI) – ohne GUI stürzt z. B.
+  `QgsSymbolLayerUtils.symbolPreviewIcon` ab. Modale Dialoge: `exec_dialog` patchen und im Ersatz die Widgets
+  prüfen. Layout-Ergebnis mit `QgsLayoutExporter.renderRegionToImage` als PNG ansehen; ohne installierte Schriften
+  erscheinen Texte als Kästchen. `exportToImage` liefert bei Erfolg `0`. (02.10.2026, Basis #88)
 - **`QT_QPA_PLATFORM=offscreen` muss gesetzt sein, bevor die `QgsApplication` erzeugt wird.** Danach wirkt es nicht mehr. (04.09.2026, Recherche)
 - **`qgis.utils.iface` ist nach `start_app()` `None`.** Entweder `pytest-qgis` verwenden (patcht automatisch) oder selbst `qgis.utils.iface = get_iface()` setzen – und keine Module zu früh importieren. (04.09.2026)
 - **`QgsProject.instance()` ist global.** Ohne Aufräumen zwischen den Tests beeinflussen sie sich gegenseitig. (04.09.2026)
@@ -40,6 +53,15 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Daten und Dienste
 
+- **basemap.de-Vektorkacheln (Basis #88):** QGIS zeigt für Vektorkachel-Layer keine Legende. Objekte im Gebiet
+  liefert `QgsVectorTileLayer.selectByGeometry()` (lädt die Kacheln selbst; `getRawTile` ist in Python gesperrt).
+  Kacheln enden bei Zoom 15. Der Renderer prüft Styles mit `scaleToZoomLevel(maßstab, False)` und setzt
+  `@vector_tile_zoom = scaleToZoom(maßstab)` – diese Variable fehlt in der Legende, zoomabhängige Strichmuster
+  sind dort sonst unsichtbar. Beim Import der Style-JSON werden Beschriftungen zu Labeling (Hausnummern =
+  Labeling-Style `Hauskoordinate`), `Nummer_*`-Schilder zu Renderer-Symbolen, `fill-extrusion` (3D) entfällt.
+  Der Legendenkatalog `sn_basis/data/basemap_legende.json` gehört zur Style-Version `bm_web_col` v2; nach einer
+  neuen Version `python modules/basemap_katalog.py` laufen lassen und Meldungen prüfen. Eine QML-Stilvorlage des
+  Layers ist rund 3,7 MB groß. (02.10.2026)
 - **WFS-Beschränkungen der sächsischen ALKIS-Dienste:** POST/GML-Filter werden blockiert, URL-Länge ist begrenzt, serverseitige Feature-Limits. Deshalb arbeiten Flurstücks- und Grenzpunktabrufe mit BBOX, gemarkungsweisen Batches und Nutzerdialogen. Nicht „vereinfachen“, ohne das gegen den Dienst zu testen. Vollständig mit erprobten Anfragen: `docs/wissen/wfs_dienste.md`, Objektarten/Attribute: `docs/wissen/alkis_objektarten.md`. (Verfahrensgebiet, Code)
 - **Zwei Linklisten.** Zentrale `Linkliste/linkliste.csv` (Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) und die vom Plugin genutzte `Plugin_SN_Plan41/assets/Linkliste.xlsx` (`ident, gruppe, kartenebene, inhalt, link, provider, stildatei`) sind nicht verbunden. Änderungen an Diensten in **beiden** nachziehen, bis die Zusammenführung entschieden ist. (09/2026)
 - **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)
@@ -50,6 +72,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Code-Konventionen, die man kennen muss
 
+- **Legendenelemente im Layout (QGIS 3.44):** `QgsLayoutItemLegend.adjustBoxSize()` wirkt erst, nachdem das
+  Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über
+  `QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))`
+  berechnen; für mehrere Spalten zusätzlich `setSplitLayer(True)`, sonst bleibt ein Layer in einer Spalte.
+  Eine Legende mit `setLegendFilterByMapEnabled(True)` blendet Layer aus, die nicht in der Karte liegen, und ihre
+  Höhe steht erst nach dem Rendern fest. (02.10.2026, Spike Basis #88)
 - **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen).
 - **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen.
 - **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe.

From c92a2ddb8657c9b1e1549f6e003c08eaff984c28 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Fri, 2 Oct 2026 12:33:18 +0200
Subject: [PATCH 03/20] Basemap-Punktlinien (Basis #90): Ursache und Korrektur
 dokumentiert
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- fallstricke.md: Strichlänge 0 aus Mapbox-Styles zeichnet Qt nicht
- Konzept 10, Abschnitt 10: Punkt „Durchlass/Baumreihe ohne Symbol“ geklärt
- modulkarte.md: basemap_stilkorrektur; STATUS.md Punkt 15 nachgezogen
- Testkatalog neu erzeugt

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |  1 +
 docs/konzept/10_basemap_legende.md |  4 ++-
 docs/status/STATUS.md              |  7 ++--
 docs/tests/testkatalog.html        | 51 +++++++++++++++++++++++-------
 docs/tests/testkatalog.md          | 34 ++++++++++++++++----
 docs/wissen/fallstricke.md         |  6 ++++
 6 files changed, 80 insertions(+), 23 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index ac50a37..521a38a 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -63,6 +63,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer |
 | `modules/basemap_legende_layout.py` | 396 | Legendensymbole aus Projektlayer-Styles zusammensetzen, leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
 | `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) |
+| `modules/basemap_stilkorrektur.py` | 99 | Darstellungsfehler des Basemap-Layers korrigieren: Punktlinien mit Strichlänge 0 sichtbar machen (#90) |
 | `modules/basemap_stilvorlagen.py` | 181 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil), Standard in `QSettings` |
 
 ### Sonstiges
diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md
index 29b1108..8d1babb 100644
--- a/docs/konzept/10_basemap_legende.md
+++ b/docs/konzept/10_basemap_legende.md
@@ -256,7 +256,9 @@ Durchgang der Bezeichnungen ist gemacht; **die fachliche Abnahme steht aus.**
 
 **Bekannte Punkte für die Durchsicht:**
 - „Platz“ (Verkehr, Siedlung) hat im Basemap-Stil eine weiße Füllung ohne Rand und ist in der Legende kaum zu sehen.
-- „Durchlass, Düker“ und „Baumreihe“ zeigen in der Legende kein sichtbares Symbol (Ursache noch nicht geklärt).
+- ~~„Durchlass, Düker“ und „Baumreihe“ ohne sichtbares Symbol~~ – Ursache gefunden und behoben in **Basis #90**:
+  basemap.de zeichnet Punktlinien mit Strichlänge 0, die Qt nicht zeichnet (auch nicht in der Karte). Knopf
+  „Punktlinien korrigieren“ im Basemap-Bereich (12 Styles betroffen).
 - Gewässerbreiten sind zu „Fließgewässer (Linie)“ zusammengefasst; Symbol ist die 6-m-Linie.
 
 **Nicht geprüft:** QGIS 4 (Qt6) – lokal nicht verfügbar. QGIS 3.44.4 (Qt5) offscreen geprüft: Auswertung,
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index c566d27..2b3539b 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -64,9 +64,10 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     `sql=`-Filtern). Vergleich 6↔7 und Anpassungsliste: `docs/konzept/09_alkis7_umstellung.md`.
     Nächster Schritt: Gitea-Issue in `Plugin_SN_Verfahrensgebiet`.
 15. **Basemap-Legende im Druck (Basis #88).** Umgesetzt auf `Plugin_SN_Basis:feature/88-basemap-legende`
-    (Konzept `docs/konzept/10_basemap_legende.md`, ADR 0010), PR steht aus. Offen: **fachliche Abnahme des
-    Legendenkatalogs** `sn_basis/data/basemap_legende.json` (Bezeichnungen, Gruppen, Zusammenfassungen; drei
-    Einträge ohne gut sichtbares Symbol, siehe Konzept Abschnitt 10) und Test unter QGIS 4.
+    (Konzept `docs/konzept/10_basemap_legende.md`, ADR 0010). Offen: **fachliche Abnahme des
+    Legendenkatalogs** `sn_basis/data/basemap_legende.json` (Bezeichnungen, Gruppen, Zusammenfassungen; „Platz“
+    als weiße Fläche ohne Rand, siehe Konzept Abschnitt 10) und Test unter QGIS 4. #88 ist gemergt (PR #89);
+    Punktlinien-Korrektur (#90) auf `Plugin_SN_Basis:bugfix/90-basemap-punktlinien`, PR steht aus.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 8de74cd..5b42641 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
648Testfälle
-
605grün
+
656Testfälle
+
613grün
35rot (Baseline)
78%mit Docstring
-
55Testdateien
+
56Testdateien
Verteilung je Paket
-
sn_basis38 Dateien · 396 Testfälle
+
sn_basis39 Dateien · 404 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 38 Testdateien · 396 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 39 Testdateien · 404 Testfälle

- @@ -172,7 +172,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 8
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 10
flowchart TD
   root(["test_basemap_anbindung.py"])
   class root rootnode
   root --> g1["TestMehrfachauswahl (1)"]
@@ -184,7 +184,7 @@
   g2 --> t3("layout_wird_markiert_und_legende_erzeugt")
   g2 --> t4("fehler_der_basemap_legende_verhindert_die_vorlage_nicht")
   g2 --> t5("druck_tab_aktualisiert_basemap_bereich_falls_vorhanden")
-  root --> g3["TestBasemapBereich (8)"]
+  root --> g3["TestBasemapBereich (10)"]
   class g3 groupnode
   g3 --> t6("auswertebereich_wird_gespeichert")
   g3 --> t7("aktualisieren_ohne_vorlage_warnt")
@@ -193,7 +193,9 @@
   g3 --> t10("stil_laden_bricht_bei_fremder_vorlage_ab")
   g3 --> t11("vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt")
   g3 --> t12("speichern_mit_aktueller_legendenauswahl")
-  g3 --> t13("stil_speichern_ohne_basemap_layer_warnt")
+  g3 --> t13("punktlinien_korrigieren_meldet_anzahl")
+  g3 --> t14("punktlinien_ohne_basemap_layer_warnt")
+  g3 --> t15("stil_speichern_ohne_basemap_layer_warnt")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -207,11 +209,13 @@
   class t11 ok
   class t12 ok
   class t13 ok
+  class t14 ok
+  class t15 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grün
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grün
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grün
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grün
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grün
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grün
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grün
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grün
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grün
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grün
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grün

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grün
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grün
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grün
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grün
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grün
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grün
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grün
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grün
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grün
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grün
TestBasemapBereich
test_punktlinien_korrigieren_meldet_anzahl
Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet.grün
TestBasemapBereich
test_punktlinien_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird.grün
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grün

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
   root(["test_basemap_kacheln.py"])
   class root rootnode
   root --> g1["TestSammleObjekte (4)"]
@@ -357,7 +361,30 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).

TestStilvorlagen 8
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90).

TestMusterUndAusdruck 2TestLayerkorrektur 4
flowchart TD
+  root(["test_basemap_stilkorrektur.py"])
+  class root rootnode
+  root --> g1["TestMusterUndAusdruck (2)"]
+  class g1 groupnode
+  g1 --> t1("strichlaenge_null_im_festen_muster")
+  g1 --> t2("strichlaenge_null_im_zoomabhaengigen_ausdruck")
+  root --> g2["TestLayerkorrektur (4)"]
+  class g2 groupnode
+  g2 --> t3("punktlinien_werden_korrigiert_andere_styles_nicht")
+  g2 --> t4("zweiter_lauf_korrigiert_nichts")
+  g2 --> t5("nur_ausdruck_ohne_festes_muster")
+  g2 --> t6("layer_ohne_renderer")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMusterUndAusdruck
test_strichlaenge_null_im_festen_muster
Prüft, dass Strichlängen 0 (gerade Positionen) im Strichmuster ersetzt und Lücken der Länge 0 nicht verändert werden.grün
TestMusterUndAusdruck
test_strichlaenge_null_im_zoomabhaengigen_ausdruck
Prüft, dass jede Strichlänge 0 in den array(...)-Mustern des Konverter-Ausdrucks ersetzt wird und der übrige Ausdruck gleich bleibt.grün
TestLayerkorrektur
test_punktlinien_werden_korrigiert_andere_styles_nicht
Prüft, dass nur Styles mit Strichlänge 0 korrigiert werden – Muster und Ausdruck – und die Anzahl zurückkommt.grün
TestLayerkorrektur
test_zweiter_lauf_korrigiert_nichts
Prüft, dass ein zweiter Lauf keine Styles mehr korrigiert und nicht neu zeichnen lässt.grün
TestLayerkorrektur
test_nur_ausdruck_ohne_festes_muster
Prüft, dass auch ein Style korrigiert wird, dessen Strichlänge 0 nur im zoomabhängigen Ausdruck steht.grün
TestLayerkorrektur
test_layer_ohne_renderer
Prüft, dass ein Layer ohne Renderer 0 korrigierte Styles liefert.grün

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).

TestStilvorlagen 8
flowchart TD
   root(["test_basemap_stilvorlagen.py"])
   class root rootnode
   root --> g1["TestStilvorlagen (8)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 23d6be2..61ea24d 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 55 Testdateien, 648 Testfälle – 605 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 503 von 648 (78 %).
+Gesamt: 3 Pakete, 56 Testdateien, 656 Testfälle – 613 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 511 von 656 (78 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 38 | 396 | 396 | 0 | 0 | 396 von 396 (100 %) |
+| `sn_basis` | 39 | 404 | 404 | 0 | 0 | 404 von 404 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **55** | **648** | **605** | **35** | **8** | **503 von 648 (78 %)** |
+| **Summe** | **56** | **656** | **613** | **35** | **8** | **511 von 656 (78 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 503 von 648 (78 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 38 Testdateien mit 396 Testfällen (396 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 39 Testdateien mit 404 Testfällen (404 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -57,7 +57,7 @@ Testgruppen:
 
 - `TestMehrfachauswahl` (1 Testfälle): — (Docstring fehlt)
 - `TestDruckanbindung` (4 Testfälle): — (Docstring fehlt)
-- `TestBasemapBereich` (8 Testfälle): — (Docstring fehlt)
+- `TestBasemapBereich` (10 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -73,6 +73,8 @@ Testgruppen:
 | `TestBasemapBereich.test_stil_laden_bricht_bei_fremder_vorlage_ab` | Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird. | grün |
 | `TestBasemapBereich.test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt` | Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird. | grün |
 | `TestBasemapBereich.test_speichern_mit_aktueller_legendenauswahl` | Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird. | grün |
+| `TestBasemapBereich.test_punktlinien_korrigieren_meldet_anzahl` | Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet. | grün |
+| `TestBasemapBereich.test_punktlinien_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird. | grün |
 | `TestBasemapBereich.test_stil_speichern_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird. | grün |
 
 ### `Plugin_SN_Basis/tests/test_basemap_kacheln.py`
@@ -174,6 +176,24 @@ Testgruppen:
 | `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün |
 | `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün |
 
+### `Plugin_SN_Basis/tests/test_basemap_stilkorrektur.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90).
+
+Testgruppen:
+
+- `TestMusterUndAusdruck` (2 Testfälle): — (Docstring fehlt)
+- `TestLayerkorrektur` (4 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestMusterUndAusdruck.test_strichlaenge_null_im_festen_muster` | Prüft, dass Strichlängen 0 (gerade Positionen) im Strichmuster ersetzt und Lücken der Länge 0 nicht verändert werden. | grün |
+| `TestMusterUndAusdruck.test_strichlaenge_null_im_zoomabhaengigen_ausdruck` | Prüft, dass jede Strichlänge 0 in den array(...)-Mustern des Konverter-Ausdrucks ersetzt wird und der übrige Ausdruck gleich bleibt. | grün |
+| `TestLayerkorrektur.test_punktlinien_werden_korrigiert_andere_styles_nicht` | Prüft, dass nur Styles mit Strichlänge 0 korrigiert werden – Muster und Ausdruck – und die Anzahl zurückkommt. | grün |
+| `TestLayerkorrektur.test_zweiter_lauf_korrigiert_nichts` | Prüft, dass ein zweiter Lauf keine Styles mehr korrigiert und nicht neu zeichnen lässt. | grün |
+| `TestLayerkorrektur.test_nur_ausdruck_ohne_festes_muster` | Prüft, dass auch ein Style korrigiert wird, dessen Strichlänge 0 nur im zoomabhängigen Ausdruck steht. | grün |
+| `TestLayerkorrektur.test_layer_ohne_renderer` | Prüft, dass ein Layer ohne Renderer 0 korrigierte Styles liefert. | grün |
+
 ### `Plugin_SN_Basis/tests/test_basemap_stilvorlagen.py`
 
 Worum es in der Datei geht: Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).
@@ -1388,7 +1408,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 396 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 404 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 1c11907..83bc28f 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -53,6 +53,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Daten und Dienste
 
+- **Punktlinien aus Mapbox-Styles sind in QGIS unsichtbar.** basemap.de (wie viele MapLibre-Stile) zeichnet Punktlinien
+  als `line-dasharray` mit Strichlänge 0 und `line-cap: round`. QGIS übernimmt das beim Import 1:1, Qt zeichnet
+  Striche der Länge 0 aber gar nicht – auch nicht als Punkt, weder in der Karte noch in der Legende. Betroffen
+  sind bei basemap.de 12 Styles (Baumreihe, Hecke, Durchlass, Seilbahnen …). Abhilfe: Strichlänge 0 durch 0,01
+  ersetzen (`sn_basis.modules.basemap_stilkorrektur`). Beim Prüfen solcher Effekte die Beschriftung abschalten –
+  sonst zählen Labels als „sichtbare Pixel“. (02.10.2026, QGIS 3.44 / PyQt 5.15, Basis #90; QGIS 4 ungeprüft)
 - **basemap.de-Vektorkacheln (Basis #88):** QGIS zeigt für Vektorkachel-Layer keine Legende. Objekte im Gebiet
   liefert `QgsVectorTileLayer.selectByGeometry()` (lädt die Kacheln selbst; `getRawTile` ist in Python gesperrt).
   Kacheln enden bei Zoom 15. Der Renderer prüft Styles mit `scaleToZoomLevel(maßstab, False)` und setzt

From 99a8156036a8e17e6b4bffc04db79f25e24c1b84 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Fri, 2 Oct 2026 12:50:13 +0200
Subject: [PATCH 04/20] =?UTF-8?q?Basemap-Legende=20(Basis=20#92):=20wei?=
 =?UTF-8?q?=C3=9Fe=20Fl=C3=A4chen=20mit=20Legendenrand=20dokumentiert?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Konzept 10, Abschnitt 10: Punkt „Platz kaum zu sehen“ als behoben vermerkt
- STATUS.md Punkt 15: Stand nach Merge von #88/#90, #92 als PR ausstehend
- modulkarte.md: basemap_legende_layout.py (465 Zeilen)
- Testkatalog neu erzeugt

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |  2 +-
 docs/konzept/10_basemap_legende.md |  3 ++-
 docs/status/STATUS.md              | 10 ++++----
 docs/tests/testkatalog.html        | 39 ++++++++++++++++++++++++------
 docs/tests/testkatalog.md          | 30 ++++++++++++++++++-----
 5 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 521a38a..4e01f1a 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -61,7 +61,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) |
 | `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg |
 | `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer |
-| `modules/basemap_legende_layout.py` | 396 | Legendensymbole aus Projektlayer-Styles zusammensetzen, leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
+| `modules/basemap_legende_layout.py` | 465 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
 | `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) |
 | `modules/basemap_stilkorrektur.py` | 99 | Darstellungsfehler des Basemap-Layers korrigieren: Punktlinien mit Strichlänge 0 sichtbar machen (#90) |
 | `modules/basemap_stilvorlagen.py` | 181 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil), Standard in `QSettings` |
diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md
index 8d1babb..14ab406 100644
--- a/docs/konzept/10_basemap_legende.md
+++ b/docs/konzept/10_basemap_legende.md
@@ -255,7 +255,8 @@ Begründung der Legendenvariante: ADR 0010.
 Durchgang der Bezeichnungen ist gemacht; **die fachliche Abnahme steht aus.**
 
 **Bekannte Punkte für die Durchsicht:**
-- „Platz“ (Verkehr, Siedlung) hat im Basemap-Stil eine weiße Füllung ohne Rand und ist in der Legende kaum zu sehen.
+- ~~„Platz“ (Verkehr, Siedlung) in der Legende kaum zu sehen~~ – behoben in **Basis #92**: weiße Flächen ohne Kontur
+  bekommen im Legendensymbol einen grauen Rand (0,15 mm); die Karte bleibt unverändert.
 - ~~„Durchlass, Düker“ und „Baumreihe“ ohne sichtbares Symbol~~ – Ursache gefunden und behoben in **Basis #90**:
   basemap.de zeichnet Punktlinien mit Strichlänge 0, die Qt nicht zeichnet (auch nicht in der Karte). Knopf
   „Punktlinien korrigieren“ im Basemap-Bereich (12 Styles betroffen).
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 2b3539b..0a46a38 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -63,11 +63,11 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     (fest verdrahteter NAS-Namespace `gid/6.0` in `grenzpunkt_alkis.py`, abgeflachte WFS-Feldnamen in
     `sql=`-Filtern). Vergleich 6↔7 und Anpassungsliste: `docs/konzept/09_alkis7_umstellung.md`.
     Nächster Schritt: Gitea-Issue in `Plugin_SN_Verfahrensgebiet`.
-15. **Basemap-Legende im Druck (Basis #88).** Umgesetzt auf `Plugin_SN_Basis:feature/88-basemap-legende`
-    (Konzept `docs/konzept/10_basemap_legende.md`, ADR 0010). Offen: **fachliche Abnahme des
-    Legendenkatalogs** `sn_basis/data/basemap_legende.json` (Bezeichnungen, Gruppen, Zusammenfassungen; „Platz“
-    als weiße Fläche ohne Rand, siehe Konzept Abschnitt 10) und Test unter QGIS 4. #88 ist gemergt (PR #89);
-    Punktlinien-Korrektur (#90) auf `Plugin_SN_Basis:bugfix/90-basemap-punktlinien`, PR steht aus.
+15. **Basemap-Legende im Druck (Basis #88).** In `unstable` gemergt: #88 (PR #89), Punktlinien-Korrektur #90
+    (PR #91). Rand für weiße Flächen (#92) auf `Plugin_SN_Basis:bugfix/92-basemap-legende-weisse-flaechen`,
+    PR steht aus. Offen: **fachliche Abnahme des Legendenkatalogs** `sn_basis/data/basemap_legende.json`
+    (Bezeichnungen, Gruppen, Zusammenfassungen; Konzept `docs/konzept/10_basemap_legende.md` Abschnitt 10,
+    ADR 0010) und Test unter QGIS 4.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 5b42641..081daab 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
656Testfälle
-
613grün
+
662Testfälle
+
619grün
35rot (Baseline)
78%mit Docstring
-
56Testdateien
+
57Testdateien
Verteilung je Paket
-
sn_basis39 Dateien · 404 Testfälle
+
sn_basis40 Dateien · 410 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 39 Testdateien · 404 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 40 Testdateien · 410 Testfälle

- @@ -361,7 +361,30 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90).

TestMusterUndAusdruck 2TestLayerkorrektur 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
+  root(["test_basemap_legende_rand.py"])
+  class root rootnode
+  root --> g1["TestBrauchtLegendenrand (4)"]
+  class g1 groupnode
+  g1 --> t1("weisse_flaeche_ohne_rand")
+  g1 --> t2("farbige_flaeche_oder_flaeche_mit_kontur")
+  g1 --> t3("heller_rand_und_fast_weiss_zaehlen_als_unsichtbar")
+  g1 --> t4("andere_ebenen_und_leeres_symbol")
+  root --> g2["TestErgaenzeLegendenrand (2)"]
+  class g2 groupnode
+  g2 --> t5("weisse_flaeche_bekommt_grauen_rand")
+  g2 --> t6("farbige_flaeche_und_linien_bleiben_unveraendert")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBrauchtLegendenrand
test_weisse_flaeche_ohne_rand
Prüft, dass eine weiße Fläche ohne Umrandung einen Legendenrand braucht.grün
TestBrauchtLegendenrand
test_farbige_flaeche_oder_flaeche_mit_kontur
Prüft, dass farbige Flächen und weiße Flächen mit dunkler Kontur oder eigener Konturlinie keinen Rand bekommen.grün
TestBrauchtLegendenrand
test_heller_rand_und_fast_weiss_zaehlen_als_unsichtbar
Prüft, dass fast weiße Füllungen und helle oder durchsichtige Ränder als unsichtbar gelten.grün
TestBrauchtLegendenrand
test_andere_ebenen_und_leeres_symbol
Prüft, dass Symbole mit anderen Ebenen (Muster, Punkte) und leere Symbole keinen Rand bekommen.grün
TestErgaenzeLegendenrand
test_weisse_flaeche_bekommt_grauen_rand
Prüft, dass eine weiße Fläche ohne Kontur eine graue Randlinie als oberste Ebene bekommt.grün
TestErgaenzeLegendenrand
test_farbige_flaeche_und_linien_bleiben_unveraendert
Prüft, dass farbige Flächen, Flächen mit Kontur und Liniensymbole keinen Rand bekommen.grün

Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90).

TestMusterUndAusdruck 2TestLayerkorrektur 4
flowchart TD
   root(["test_basemap_stilkorrektur.py"])
   class root rootnode
   root --> g1["TestMusterUndAusdruck (2)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 61ea24d..3b7934c 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 56 Testdateien, 656 Testfälle – 613 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 511 von 656 (78 %).
+Gesamt: 3 Pakete, 57 Testdateien, 662 Testfälle – 619 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 517 von 662 (78 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 39 | 404 | 404 | 0 | 0 | 404 von 404 (100 %) |
+| `sn_basis` | 40 | 410 | 410 | 0 | 0 | 410 von 410 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **56** | **656** | **613** | **35** | **8** | **511 von 656 (78 %)** |
+| **Summe** | **57** | **662** | **619** | **35** | **8** | **517 von 662 (78 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 511 von 656 (78 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 39 Testdateien mit 404 Testfällen (404 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 40 Testdateien mit 410 Testfällen (410 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -176,6 +176,24 @@ Testgruppen:
 | `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün |
 | `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün |
 
+### `Plugin_SN_Basis/tests/test_basemap_legende_rand.py`
+
+Worum es in der Datei geht: Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).
+
+Testgruppen:
+
+- `TestBrauchtLegendenrand` (4 Testfälle): — (Docstring fehlt)
+- `TestErgaenzeLegendenrand` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestBrauchtLegendenrand.test_weisse_flaeche_ohne_rand` | Prüft, dass eine weiße Fläche ohne Umrandung einen Legendenrand braucht. | grün |
+| `TestBrauchtLegendenrand.test_farbige_flaeche_oder_flaeche_mit_kontur` | Prüft, dass farbige Flächen und weiße Flächen mit dunkler Kontur oder eigener Konturlinie keinen Rand bekommen. | grün |
+| `TestBrauchtLegendenrand.test_heller_rand_und_fast_weiss_zaehlen_als_unsichtbar` | Prüft, dass fast weiße Füllungen und helle oder durchsichtige Ränder als unsichtbar gelten. | grün |
+| `TestBrauchtLegendenrand.test_andere_ebenen_und_leeres_symbol` | Prüft, dass Symbole mit anderen Ebenen (Muster, Punkte) und leere Symbole keinen Rand bekommen. | grün |
+| `TestErgaenzeLegendenrand.test_weisse_flaeche_bekommt_grauen_rand` | Prüft, dass eine weiße Fläche ohne Kontur eine graue Randlinie als oberste Ebene bekommt. | grün |
+| `TestErgaenzeLegendenrand.test_farbige_flaeche_und_linien_bleiben_unveraendert` | Prüft, dass farbige Flächen, Flächen mit Kontur und Liniensymbole keinen Rand bekommen. | grün |
+
 ### `Plugin_SN_Basis/tests/test_basemap_stilkorrektur.py`
 
 Worum es in der Datei geht: Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90).
@@ -1408,7 +1426,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 404 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 410 Testfällen ohne Beschreibung
 
 Keine Lücken.
 

From 9c7c4b900d45d4e6c8da3ed408d2d12c86cd18f7 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Fri, 2 Oct 2026 13:07:03 +0200
Subject: [PATCH 05/20] Kartenrand-Anordnung (Basis #93) dokumentiert
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- schnittstellen.md Abschnitt 12: Element-IDs, die print_kartenrand braucht; Seitengröße nur Mindestgröße
- fallstricke.md: minimumSize liefert vor dem ersten Zeichnen die ungefilterte Legendenhöhe
- modulkarte.md: print_kartenrand, aktualisierte Zeilenzahlen
- STATUS.md: Punkt 15 (Stand #88/#90/#92, QGIS 4 geprüft), neuer Punkt 16
- Testkatalog neu erzeugt

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |  9 +++---
 docs/architektur/schnittstellen.md | 10 ++++++
 docs/status/STATUS.md              |  8 +++--
 docs/tests/testkatalog.html        | 49 +++++++++++++++++++++++++-----
 docs/tests/testkatalog.md          | 35 +++++++++++++++++----
 docs/wissen/fallstricke.md         |  4 +++
 6 files changed, 94 insertions(+), 21 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 4e01f1a..a74b35a 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -26,7 +26,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 |---|---:|---|
 | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) |
 | `functions/qt_wrapper.py` ⚠ | 832 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` |
-| `functions/qgiscore_wrapper.py` ⚠ | 1 382 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
+| `functions/qgiscore_wrapper.py` ⚠ | 1 428 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
 | `functions/qgisui_wrapper.py` | 258 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner; Mock-`iface` |
 | `functions/dialog_wrapper.py` | 521 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) |
 | `functions/message_wrapper.py` | 85 | MessageBar: `info`, `warning`, `error`, `success`, `push_message` |
@@ -57,7 +57,8 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/LayerLoader.py` | 422 | `create_layer(provider, link, thema)`, `apply_style`, `filter_by_extent`, `filter_by_layer`, `add_to_project` |
 | `modules/Datenschreiber.py` | 448 | `schreibe_Daten(...)` in GeoPackage, `lade_Layer(...)` ins Projekt, `schreibe_log(...)` HTML-Protokoll |
 | `modules/print_logic.py` ⚠ | 1 360 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf |
-| `modules/print_layout.py` ⚠ | 1 111 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) |
+| `modules/print_layout.py` ⚠ | 1 127 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) |
+| `modules/print_kartenrand.py` | 189 | Kartenrand (Legenden, Quellenangabe, Kartenschild) überlappungsfrei anordnen: 1–3 Legendenspalten, Rand breiter, Seite ggf. höher (#93) |
 | `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) |
 | `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg |
 | `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer |
@@ -139,8 +140,8 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl
 | `sn_verfahrensgebiet/functions/grenzpunkt_alkis.py` | 1 674 |
 | `sn_basis/modules/print_logic.py` | 1 360 |
 | `sn_plan41/ui/tab_a_logic.py` | 1 239 |
-| `sn_basis/functions/qgiscore_wrapper.py` | 1 382 |
-| `sn_basis/modules/print_layout.py` | 1 111 |
+| `sn_basis/functions/qgiscore_wrapper.py` | 1 428 |
+| `sn_basis/modules/print_layout.py` | 1 127 |
 | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 |
 | `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 |
 | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 |
diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md
index dc8eafa..41a4768 100644
--- a/docs/architektur/schnittstellen.md
+++ b/docs/architektur/schnittstellen.md
@@ -151,3 +151,13 @@ flowchart LR
   `sn_basis/basemap_standardstil`.
 - **Neue öffentliche Funktion:** `dialog_wrapper.ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)` –
   Liste mit Häkchen, Gruppen, Suche; liefert gewählte Schlüssel oder `None` bei Abbruch.
+
+## 12. Kartenrand im Standardlayout (Basis #93)
+
+`print_kartenrand.ordne_kartenrand_an(layout)` läuft am Ende von `PrintLayout.create_single_page_layout` und findet
+die Elemente **über ihre IDs**: `Hauptkarte`, `Legende`, `Legende Basemap`, `Quellenangabe`, `Kartenschild`,
+`Kartenschild_Linie_1` … `_6` und die Schriftfeld-Beschriftungen (`Herausgeber` … `Aktenzeichen`, Liste
+`UNTERE_ELEMENTE`). Wer im Layoutaufbau eine dieser IDs ändert oder ein neues Element unter die Legenden setzt,
+muss die Liste anpassen. Die Seitengröße aus `print_logic` ist danach nur noch eine Mindestgröße: Breite und Höhe
+können wachsen, die Karte bleibt bei 10 mm / 10 mm mit unveränderter Größe.
+
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 0a46a38..8cc95c4 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -64,10 +64,12 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     `sql=`-Filtern). Vergleich 6↔7 und Anpassungsliste: `docs/konzept/09_alkis7_umstellung.md`.
     Nächster Schritt: Gitea-Issue in `Plugin_SN_Verfahrensgebiet`.
 15. **Basemap-Legende im Druck (Basis #88).** In `unstable` gemergt: #88 (PR #89), Punktlinien-Korrektur #90
-    (PR #91). Rand für weiße Flächen (#92) auf `Plugin_SN_Basis:bugfix/92-basemap-legende-weisse-flaechen`,
-    PR steht aus. Offen: **fachliche Abnahme des Legendenkatalogs** `sn_basis/data/basemap_legende.json`
+    (PR #91), Rand für weiße Flächen #92. Offen: **fachliche Abnahme des Legendenkatalogs** `sn_basis/data/basemap_legende.json`
     (Bezeichnungen, Gruppen, Zusammenfassungen; Konzept `docs/konzept/10_basemap_legende.md` Abschnitt 10,
-    ADR 0010) und Test unter QGIS 4.
+    ADR 0010; bisher unauffällig). Unter QGIS 4 geprüft (Plugin funktioniert, Rückmeldung 02.10.2026).
+16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Umgesetzt auf
+    `Plugin_SN_Basis:bugfix/93-kartenrand-ueberlappung`, PR steht aus. Offen: Basemap-Legende wird dreispaltig sehr
+    breit (alle Spalten so breit wie der längste Text) – ggf. ungleiche Spaltenbreiten prüfen.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 081daab..785e557 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
662Testfälle
-
619grün
+
672Testfälle
+
629grün
35rot (Baseline)
78%mit Docstring
-
57Testdateien
+
58Testdateien
Verteilung je Paket
-
sn_basis40 Dateien · 410 Testfälle
+
sn_basis41 Dateien · 420 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 40 Testdateien · 410 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 41 Testdateien · 420 Testfälle

- @@ -934,7 +934,40 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).

TestBerechneAnordnung 5TestOrdneKartenrandAn 3TestAnbindungPrintLayout 2
flowchart TD
+  root(["test_print_kartenrand.py"])
+  class root rootnode
+  root --> g1["TestBerechneAnordnung (5)"]
+  class g1 groupnode
+  g1 --> t1("passt_einspaltig_nichts_aendert_sich")
+  g1 --> t2("mehr_spalten_und_breiterer_rand")
+  g1 --> t3("seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen")
+  g1 --> t4("seite_wird_nie_kleiner")
+  g1 --> t5("ohne_legenden_nur_kartenschild")
+  root --> g2["TestOrdneKartenrandAn (3)"]
+  class g2 groupnode
+  g2 --> t6("seite_hoeher_und_kartenschild_verschoben")
+  g2 --> t7("passt_nichts_wird_verschoben")
+  g2 --> t8("ohne_karte_keine_anordnung")
+  root --> g3["TestAnbindungPrintLayout (2)"]
+  class g3 groupnode
+  g3 --> t9("vorlage_ordnet_kartenrand_an")
+  g3 --> t10("fehler_bei_der_anordnung_verhindert_die_vorlage_nicht")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBerechneAnordnung
test_passt_einspaltig_nichts_aendert_sich
Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben.grün
TestBerechneAnordnung
test_mehr_spalten_und_breiterer_rand
Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird.grün
TestBerechneAnordnung
test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen
Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen.grün
TestBerechneAnordnung
test_seite_wird_nie_kleiner
Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen.grün
TestBerechneAnordnung
test_ohne_legenden_nur_kartenschild
Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird.grün
TestOrdneKartenrandAn
test_seite_hoeher_und_kartenschild_verschoben
Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen.grün
TestOrdneKartenrandAn
test_passt_nichts_wird_verschoben
Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.grün
TestOrdneKartenrandAn
test_ohne_karte_keine_anordnung
Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt.grün
TestAnbindungPrintLayout
test_vorlage_ordnet_kartenrand_an
Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt.grün
TestAnbindungPrintLayout
test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
   root(["test_print_layout.py"])
   class root rootnode
   root --> g1["TestPrintLayout (5)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 3b7934c..c2ce7a1 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 57 Testdateien, 662 Testfälle – 619 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 517 von 662 (78 %).
+Gesamt: 3 Pakete, 58 Testdateien, 672 Testfälle – 629 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 527 von 672 (78 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 40 | 410 | 410 | 0 | 0 | 410 von 410 (100 %) |
+| `sn_basis` | 41 | 420 | 420 | 0 | 0 | 420 von 420 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **57** | **662** | **619** | **35** | **8** | **517 von 662 (78 %)** |
+| **Summe** | **58** | **672** | **629** | **35** | **8** | **527 von 672 (78 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 517 von 662 (78 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 40 Testdateien mit 410 Testfällen (410 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 41 Testdateien mit 420 Testfällen (420 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -592,6 +592,29 @@ Testgruppen:
 | `IsCaseSensitiveFsTests.test_macos_ist_nicht_case_sensitiv` | Prüft, dass macOS als nicht case-sensitives Dateisystem gilt. | grün |
 | `IsCaseSensitiveFsTests.test_linux_ist_case_sensitiv` | Prüft, dass Linux als case-sensitives Dateisystem gilt. | grün |
 
+### `Plugin_SN_Basis/tests/test_print_kartenrand.py`
+
+Worum es in der Datei geht: Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).
+
+Testgruppen:
+
+- `TestBerechneAnordnung` (5 Testfälle): — (Docstring fehlt)
+- `TestOrdneKartenrandAn` (3 Testfälle): — (Docstring fehlt)
+- `TestAnbindungPrintLayout` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestBerechneAnordnung.test_passt_einspaltig_nichts_aendert_sich` | Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben. | grün |
+| `TestBerechneAnordnung.test_mehr_spalten_und_breiterer_rand` | Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird. | grün |
+| `TestBerechneAnordnung.test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen` | Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen. | grün |
+| `TestBerechneAnordnung.test_seite_wird_nie_kleiner` | Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen. | grün |
+| `TestBerechneAnordnung.test_ohne_legenden_nur_kartenschild` | Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird. | grün |
+| `TestOrdneKartenrandAn.test_seite_hoeher_und_kartenschild_verschoben` | Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen. | grün |
+| `TestOrdneKartenrandAn.test_passt_nichts_wird_verschoben` | Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden. | grün |
+| `TestOrdneKartenrandAn.test_ohne_karte_keine_anordnung` | Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt. | grün |
+| `TestAnbindungPrintLayout.test_vorlage_ordnet_kartenrand_an` | Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt. | grün |
+| `TestAnbindungPrintLayout.test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht` | Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht. | grün |
+
 ### `Plugin_SN_Basis/tests/test_print_layout.py`
 
 Worum es in der Datei geht: — (Docstring fehlt)
@@ -1426,7 +1449,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 410 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 420 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 83bc28f..897304b 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -84,6 +84,10 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
   berechnen; für mehrere Spalten zusätzlich `setSplitLayer(True)`, sonst bleibt ein Layer in einer Spalte.
   Eine Legende mit `setLegendFilterByMapEnabled(True)` blendet Layer aus, die nicht in der Karte liegen, und ihre
   Höhe steht erst nach dem Rendern fest. (02.10.2026, Spike Basis #88)
+  Auch `QgsLegendRenderer.minimumSize` liefert vor dem ersten Zeichnen die **ungefilterte** Höhe (Beispiel: 344 statt
+  177 mm). Verlässlich vermessen: Legende einmal mit geringer Auflösung zeichnen
+  (`QgsLayoutExporter(layout).renderRegionToImage(legende.sceneBoundingRect(), QSize(), 25)`), danach wirken
+  Filter und `adjustBoxSize`. (02.10.2026, Basis #93)
 - **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen).
 - **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen.
 - **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe.

From 4779624b14555975c1ad01a898b2d0831231fd76 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Fri, 2 Oct 2026 13:20:16 +0200
Subject: [PATCH 06/20] Mehrspaltige Legenden (Basis #96) dokumentiert

- fallstricke.md: gleiche Spaltenbreiten, Textumbruch, Legendentitel bricht nicht automatisch um
- STATUS.md Punkt 16: #93 gemergt, #96 als PR ausstehend
- Testkatalog neu erzeugt

Co-Authored-By: Claude Opus 5.5 
---
 docs/status/STATUS.md       |  6 ++---
 docs/tests/testkatalog.html | 44 ++++++++++++++++++++++---------------
 docs/tests/testkatalog.md   | 20 ++++++++++-------
 docs/wissen/fallstricke.md  |  3 +++
 4 files changed, 44 insertions(+), 29 deletions(-)

diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 8cc95c4..6bc3612 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -67,9 +67,9 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     (PR #91), Rand für weiße Flächen #92. Offen: **fachliche Abnahme des Legendenkatalogs** `sn_basis/data/basemap_legende.json`
     (Bezeichnungen, Gruppen, Zusammenfassungen; Konzept `docs/konzept/10_basemap_legende.md` Abschnitt 10,
     ADR 0010; bisher unauffällig). Unter QGIS 4 geprüft (Plugin funktioniert, Rückmeldung 02.10.2026).
-16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Umgesetzt auf
-    `Plugin_SN_Basis:bugfix/93-kartenrand-ueberlappung`, PR steht aus. Offen: Basemap-Legende wird dreispaltig sehr
-    breit (alle Spalten so breit wie der längste Text) – ggf. ungleiche Spaltenbreiten prüfen.
+16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Gemergt (PR #95). Mehrspaltige Legenden schmaler
+    (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel) auf
+    `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 785e557..4cdaf3f 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
672Testfälle
-
629grün
+
676Testfälle
+
633grün
35rot (Baseline)
-
78%mit Docstring
+
79%mit Docstring
58Testdateien
Verteilung je Paket
-
sn_basis41 Dateien · 420 Testfälle
+
sn_basis41 Dateien · 424 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 41 Testdateien · 420 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 41 Testdateien · 424 Testfälle

- @@ -326,7 +326,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 6
flowchart TD
   root(["test_basemap_legende_layout.py"])
   class root rootnode
   root --> g1["TestKennzeichenUndKarte (2)"]
@@ -342,11 +342,14 @@
   g3 --> t5("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
   g3 --> t6("alte_hilfslayer_werden_entfernt")
   g3 --> t7("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
-  root --> g4["TestPlatzierung (3)"]
+  root --> g4["TestPlatzierung (6)"]
   class g4 groupnode
   g4 --> t8("einspaltig_ueber_der_quellenangabe")
   g4 --> t9("mehrspaltig_wenn_hoehe_nicht_reicht")
-  g4 --> t10("nie_breiter_als_der_seitenrand_mit_hinweis")
+  g4 --> t10("spalten_ungleich_breit")
+  g4 --> t11("mindestgroesse_mit_ungleichen_spalten_und_umbruch")
+  g4 --> t12("titel_zweizeilig")
+  g4 --> t13("nie_breiter_als_der_seitenrand_mit_hinweis")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -357,11 +360,14 @@
   class t8 ok
   class t9 ok
   class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_spalten_ungleich_breit
Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96).grün
TestPlatzierung
test_mindestgroesse_mit_ungleichen_spalten_und_umbruch
Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96).grün
TestPlatzierung
test_titel_zweizeilig
Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96).grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
   root(["test_basemap_legende_rand.py"])
   class root rootnode
   root --> g1["TestBrauchtLegendenrand (4)"]
@@ -934,7 +940,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).

TestBerechneAnordnung 5TestOrdneKartenrandAn 3TestAnbindungPrintLayout 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).

TestBerechneAnordnung 5TestOrdneKartenrandAn 4TestAnbindungPrintLayout 2
flowchart TD
   root(["test_print_kartenrand.py"])
   class root rootnode
   root --> g1["TestBerechneAnordnung (5)"]
@@ -944,15 +950,16 @@
   g1 --> t3("seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen")
   g1 --> t4("seite_wird_nie_kleiner")
   g1 --> t5("ohne_legenden_nur_kartenschild")
-  root --> g2["TestOrdneKartenrandAn (3)"]
+  root --> g2["TestOrdneKartenrandAn (4)"]
   class g2 groupnode
   g2 --> t6("seite_hoeher_und_kartenschild_verschoben")
-  g2 --> t7("passt_nichts_wird_verschoben")
-  g2 --> t8("ohne_karte_keine_anordnung")
+  g2 --> t7("spalten_ungleich_breit_und_text_umgebrochen")
+  g2 --> t8("passt_nichts_wird_verschoben")
+  g2 --> t9("ohne_karte_keine_anordnung")
   root --> g3["TestAnbindungPrintLayout (2)"]
   class g3 groupnode
-  g3 --> t9("vorlage_ordnet_kartenrand_an")
-  g3 --> t10("fehler_bei_der_anordnung_verhindert_die_vorlage_nicht")
+  g3 --> t10("vorlage_ordnet_kartenrand_an")
+  g3 --> t11("fehler_bei_der_anordnung_verhindert_die_vorlage_nicht")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -963,11 +970,12 @@
   class t8 ok
   class t9 ok
   class t10 ok
+  class t11 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBerechneAnordnung
test_passt_einspaltig_nichts_aendert_sich
Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben.grün
TestBerechneAnordnung
test_mehr_spalten_und_breiterer_rand
Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird.grün
TestBerechneAnordnung
test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen
Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen.grün
TestBerechneAnordnung
test_seite_wird_nie_kleiner
Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen.grün
TestBerechneAnordnung
test_ohne_legenden_nur_kartenschild
Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird.grün
TestOrdneKartenrandAn
test_seite_hoeher_und_kartenschild_verschoben
Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen.grün
TestOrdneKartenrandAn
test_passt_nichts_wird_verschoben
Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.grün
TestOrdneKartenrandAn
test_ohne_karte_keine_anordnung
Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt.grün
TestAnbindungPrintLayout
test_vorlage_ordnet_kartenrand_an
Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt.grün
TestAnbindungPrintLayout
test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBerechneAnordnung
test_passt_einspaltig_nichts_aendert_sich
Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben.grün
TestBerechneAnordnung
test_mehr_spalten_und_breiterer_rand
Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird.grün
TestBerechneAnordnung
test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen
Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen.grün
TestBerechneAnordnung
test_seite_wird_nie_kleiner
Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen.grün
TestBerechneAnordnung
test_ohne_legenden_nur_kartenschild
Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird.grün
TestOrdneKartenrandAn
test_seite_hoeher_und_kartenschild_verschoben
Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen.grün
TestOrdneKartenrandAn
test_spalten_ungleich_breit_und_text_umgebrochen
Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96).grün
TestOrdneKartenrandAn
test_passt_nichts_wird_verschoben
Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.grün
TestOrdneKartenrandAn
test_ohne_karte_keine_anordnung
Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt.grün
TestAnbindungPrintLayout
test_vorlage_ordnet_kartenrand_an
Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt.grün
TestAnbindungPrintLayout
test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
   root(["test_print_layout.py"])
   class root rootnode
   root --> g1["TestPrintLayout (5)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index c2ce7a1..7a47038 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 58 Testdateien, 672 Testfälle – 629 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 527 von 672 (78 %).
+Gesamt: 3 Pakete, 58 Testdateien, 676 Testfälle – 633 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 531 von 676 (79 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 41 | 420 | 420 | 0 | 0 | 420 von 420 (100 %) |
+| `sn_basis` | 41 | 424 | 424 | 0 | 0 | 424 von 424 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **58** | **672** | **629** | **35** | **8** | **527 von 672 (78 %)** |
+| **Summe** | **58** | **676** | **633** | **35** | **8** | **531 von 676 (79 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 527 von 672 (78 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 41 Testdateien mit 420 Testfällen (420 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 41 Testdateien mit 424 Testfällen (424 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -161,7 +161,7 @@ Testgruppen:
 - `TestKennzeichenUndKarte` (2 Testfälle): — (Docstring fehlt)
 - `TestSymbolwahl` (2 Testfälle): — (Docstring fehlt)
 - `TestHilfslayer` (3 Testfälle): — (Docstring fehlt)
-- `TestPlatzierung` (3 Testfälle): — (Docstring fehlt)
+- `TestPlatzierung` (6 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -174,6 +174,9 @@ Testgruppen:
 | `TestHilfslayer.test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt` | Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird. | grün |
 | `TestPlatzierung.test_einspaltig_ueber_der_quellenangabe` | Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht. | grün |
 | `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün |
+| `TestPlatzierung.test_spalten_ungleich_breit` | Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96). | grün |
+| `TestPlatzierung.test_mindestgroesse_mit_ungleichen_spalten_und_umbruch` | Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96). | grün |
+| `TestPlatzierung.test_titel_zweizeilig` | Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96). | grün |
 | `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_basemap_legende_rand.py`
@@ -599,7 +602,7 @@ Worum es in der Datei geht: Tests für modules/print_kartenrand.py – Anordnung
 Testgruppen:
 
 - `TestBerechneAnordnung` (5 Testfälle): — (Docstring fehlt)
-- `TestOrdneKartenrandAn` (3 Testfälle): — (Docstring fehlt)
+- `TestOrdneKartenrandAn` (4 Testfälle): — (Docstring fehlt)
 - `TestAnbindungPrintLayout` (2 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
@@ -610,6 +613,7 @@ Testgruppen:
 | `TestBerechneAnordnung.test_seite_wird_nie_kleiner` | Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen. | grün |
 | `TestBerechneAnordnung.test_ohne_legenden_nur_kartenschild` | Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird. | grün |
 | `TestOrdneKartenrandAn.test_seite_hoeher_und_kartenschild_verschoben` | Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen. | grün |
+| `TestOrdneKartenrandAn.test_spalten_ungleich_breit_und_text_umgebrochen` | Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96). | grün |
 | `TestOrdneKartenrandAn.test_passt_nichts_wird_verschoben` | Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden. | grün |
 | `TestOrdneKartenrandAn.test_ohne_karte_keine_anordnung` | Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt. | grün |
 | `TestAnbindungPrintLayout.test_vorlage_ordnet_kartenrand_an` | Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt. | grün |
@@ -1449,7 +1453,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 420 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 424 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 897304b..f817cbf 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -88,6 +88,9 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
   177 mm). Verlässlich vermessen: Legende einmal mit geringer Auflösung zeichnen
   (`QgsLayoutExporter(layout).renderRegionToImage(legende.sceneBoundingRect(), QSize(), 25)`), danach wirken
   Filter und `adjustBoxSize`. (02.10.2026, Basis #93)
+  Mehrspaltige Legenden: Mit `setEqualColumnWidth(True)` bestimmt der längste Text die Breite **aller** Spalten.
+  Schmaler wird es mit ungleichen Spalten und `setAutoWrapLinesAfter(mm)`; der Legendentitel bricht dabei **nicht**
+  automatisch um und bestimmt die Mindestbreite – Zeilenumbruch (`\n`) im Titel setzen. (02.10.2026, Basis #96)
 - **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen).
 - **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen.
 - **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe.

From 0a81fd75faebd052b695d71cdb9bb5731b7be48d Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Fri, 2 Oct 2026 13:26:35 +0200
Subject: [PATCH 07/20] Legenden-Mindestbreite (Basis #96) dokumentiert

- schnittstellen.md Abschnitt 12: Mindestbreite 180 mm (= Kartenschild), Layout-Eigenschaft
  sn_legende_mindestbreite, Listener in sn_basis.main
- fallstricke.md: setMinimumSize wird nicht im Projekt gespeichert
- modulkarte.md, STATUS.md, Testkatalog

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |  4 +--
 docs/architektur/schnittstellen.md |  5 +++
 docs/status/STATUS.md              |  3 +-
 docs/tests/testkatalog.html        | 50 ++++++++++++++++++------------
 docs/tests/testkatalog.md          | 22 ++++++++-----
 docs/wissen/fallstricke.md         |  3 ++
 6 files changed, 57 insertions(+), 30 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index a74b35a..855a2e6 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -58,11 +58,11 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/Datenschreiber.py` | 448 | `schreibe_Daten(...)` in GeoPackage, `lade_Layer(...)` ins Projekt, `schreibe_log(...)` HTML-Protokoll |
 | `modules/print_logic.py` ⚠ | 1 360 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf |
 | `modules/print_layout.py` ⚠ | 1 127 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) |
-| `modules/print_kartenrand.py` | 189 | Kartenrand (Legenden, Quellenangabe, Kartenschild) überlappungsfrei anordnen: 1–3 Legendenspalten, Rand breiter, Seite ggf. höher (#93) |
+| `modules/print_kartenrand.py` | 284 | Kartenrand (Legenden, Quellenangabe, Kartenschild) überlappungsfrei anordnen: 1–3 Legendenspalten, Rand breiter, Seite ggf. höher (#93); Legenden-Mindestbreite 180 mm samt Listener für das Neuladen (#96) |
 | `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) |
 | `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg |
 | `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer |
-| `modules/basemap_legende_layout.py` | 465 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
+| `modules/basemap_legende_layout.py` | 471 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
 | `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) |
 | `modules/basemap_stilkorrektur.py` | 99 | Darstellungsfehler des Basemap-Layers korrigieren: Punktlinien mit Strichlänge 0 sichtbar machen (#90) |
 | `modules/basemap_stilvorlagen.py` | 181 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil), Standard in `QSettings` |
diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md
index 41a4768..49e4bd6 100644
--- a/docs/architektur/schnittstellen.md
+++ b/docs/architektur/schnittstellen.md
@@ -161,3 +161,8 @@ die Elemente **über ihre IDs**: `Hauptkarte`, `Legende`, `Legende Basemap`, `Qu
 muss die Liste anpassen. Die Seitengröße aus `print_logic` ist danach nur noch eine Mindestgröße: Breite und Höhe
 können wachsen, die Karte bleibt bei 10 mm / 10 mm mit unveränderter Größe.
 
+Legenden sind mindestens so breit wie das Kartenschild (180 mm, `MIN_SPALTENBREITE_MM`; die 210 mm in
+`print_logic.STANDARD_LAYOUT_FIXBREITE_MM` sind der gesamte Kartenrand mit Abständen). Weil QGIS die Mindestgröße
+nicht speichert, trägt das Layout die Eigenschaft `sn_legende_mindestbreite`; `sn_basis.main` verbindet beim Start
+`setup_mindestbreiten_listener()` (readProject) und trennt ihn in `unload()` (Basis #96).
+
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 6bc3612..957e749 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -68,7 +68,8 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     (Bezeichnungen, Gruppen, Zusammenfassungen; Konzept `docs/konzept/10_basemap_legende.md` Abschnitt 10,
     ADR 0010; bisher unauffällig). Unter QGIS 4 geprüft (Plugin funktioniert, Rückmeldung 02.10.2026).
 16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Gemergt (PR #95). Mehrspaltige Legenden schmaler
-    (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel) auf
+    (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das
+    Kartenschild) auf
     `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 4cdaf3f..16b3044 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
676Testfälle
-
633grün
+
681Testfälle
+
638grün
35rot (Baseline)
79%mit Docstring
58Testdateien
Verteilung je Paket
-
sn_basis41 Dateien · 424 Testfälle
+
sn_basis41 Dateien · 429 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 41 Testdateien · 424 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 41 Testdateien · 429 Testfälle

- @@ -326,7 +326,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 6
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 7
flowchart TD
   root(["test_basemap_legende_layout.py"])
   class root rootnode
   root --> g1["TestKennzeichenUndKarte (2)"]
@@ -342,14 +342,15 @@
   g3 --> t5("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
   g3 --> t6("alte_hilfslayer_werden_entfernt")
   g3 --> t7("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
-  root --> g4["TestPlatzierung (6)"]
+  root --> g4["TestPlatzierung (7)"]
   class g4 groupnode
   g4 --> t8("einspaltig_ueber_der_quellenangabe")
   g4 --> t9("mehrspaltig_wenn_hoehe_nicht_reicht")
   g4 --> t10("spalten_ungleich_breit")
   g4 --> t11("mindestgroesse_mit_ungleichen_spalten_und_umbruch")
-  g4 --> t12("titel_zweizeilig")
-  g4 --> t13("nie_breiter_als_der_seitenrand_mit_hinweis")
+  g4 --> t12("neue_basemap_legende_mindestens_so_breit_wie_kartenschild")
+  g4 --> t13("titel_zweizeilig")
+  g4 --> t14("nie_breiter_als_der_seitenrand_mit_hinweis")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -363,11 +364,12 @@
   class t11 ok
   class t12 ok
   class t13 ok
+  class t14 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_spalten_ungleich_breit
Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96).grün
TestPlatzierung
test_mindestgroesse_mit_ungleichen_spalten_und_umbruch
Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96).grün
TestPlatzierung
test_titel_zweizeilig
Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96).grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_spalten_ungleich_breit
Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96).grün
TestPlatzierung
test_mindestgroesse_mit_ungleichen_spalten_und_umbruch
Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96).grün
TestPlatzierung
test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild
Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt.grün
TestPlatzierung
test_titel_zweizeilig
Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96).grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
   root(["test_basemap_legende_rand.py"])
   class root rootnode
   root --> g1["TestBrauchtLegendenrand (4)"]
@@ -940,7 +942,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).

TestBerechneAnordnung 5TestOrdneKartenrandAn 4TestAnbindungPrintLayout 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).

TestBerechneAnordnung 5TestOrdneKartenrandAn 5TestMindestbreiteBeimLaden 3TestAnbindungPrintLayout 2
flowchart TD
   root(["test_print_kartenrand.py"])
   class root rootnode
   root --> g1["TestBerechneAnordnung (5)"]
@@ -950,16 +952,22 @@
   g1 --> t3("seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen")
   g1 --> t4("seite_wird_nie_kleiner")
   g1 --> t5("ohne_legenden_nur_kartenschild")
-  root --> g2["TestOrdneKartenrandAn (4)"]
+  root --> g2["TestOrdneKartenrandAn (5)"]
   class g2 groupnode
   g2 --> t6("seite_hoeher_und_kartenschild_verschoben")
   g2 --> t7("spalten_ungleich_breit_und_text_umgebrochen")
-  g2 --> t8("passt_nichts_wird_verschoben")
-  g2 --> t9("ohne_karte_keine_anordnung")
-  root --> g3["TestAnbindungPrintLayout (2)"]
+  g2 --> t8("legenden_mindestens_so_breit_wie_kartenschild")
+  g2 --> t9("passt_nichts_wird_verschoben")
+  g2 --> t10("ohne_karte_keine_anordnung")
+  root --> g3["TestMindestbreiteBeimLaden (3)"]
   class g3 groupnode
-  g3 --> t10("vorlage_ordnet_kartenrand_an")
-  g3 --> t11("fehler_bei_der_anordnung_verhindert_die_vorlage_nicht")
+  g3 --> t11("mindestbreite_nur_fuer_vermerkte_layouts")
+  g3 --> t12("listener_verbinden_und_trennen")
+  g3 --> t13("ohne_qgis_kein_listener")
+  root --> g4["TestAnbindungPrintLayout (2)"]
+  class g4 groupnode
+  g4 --> t14("vorlage_ordnet_kartenrand_an")
+  g4 --> t15("fehler_bei_der_anordnung_verhindert_die_vorlage_nicht")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -971,11 +979,15 @@
   class t9 ok
   class t10 ok
   class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBerechneAnordnung
test_passt_einspaltig_nichts_aendert_sich
Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben.grün
TestBerechneAnordnung
test_mehr_spalten_und_breiterer_rand
Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird.grün
TestBerechneAnordnung
test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen
Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen.grün
TestBerechneAnordnung
test_seite_wird_nie_kleiner
Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen.grün
TestBerechneAnordnung
test_ohne_legenden_nur_kartenschild
Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird.grün
TestOrdneKartenrandAn
test_seite_hoeher_und_kartenschild_verschoben
Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen.grün
TestOrdneKartenrandAn
test_spalten_ungleich_breit_und_text_umgebrochen
Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96).grün
TestOrdneKartenrandAn
test_passt_nichts_wird_verschoben
Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.grün
TestOrdneKartenrandAn
test_ohne_karte_keine_anordnung
Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt.grün
TestAnbindungPrintLayout
test_vorlage_ordnet_kartenrand_an
Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt.grün
TestAnbindungPrintLayout
test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBerechneAnordnung
test_passt_einspaltig_nichts_aendert_sich
Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben.grün
TestBerechneAnordnung
test_mehr_spalten_und_breiterer_rand
Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird.grün
TestBerechneAnordnung
test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen
Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen.grün
TestBerechneAnordnung
test_seite_wird_nie_kleiner
Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen.grün
TestBerechneAnordnung
test_ohne_legenden_nur_kartenschild
Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird.grün
TestOrdneKartenrandAn
test_seite_hoeher_und_kartenschild_verschoben
Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen.grün
TestOrdneKartenrandAn
test_spalten_ungleich_breit_und_text_umgebrochen
Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96).grün
TestOrdneKartenrandAn
test_legenden_mindestens_so_breit_wie_kartenschild
Prüft, dass die Legenden die Breite des Kartenschilds als Mindestbreite bekommen und das Layout sie für das Neuladen vermerkt.grün
TestOrdneKartenrandAn
test_passt_nichts_wird_verschoben
Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.grün
TestOrdneKartenrandAn
test_ohne_karte_keine_anordnung
Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt.grün
TestMindestbreiteBeimLaden
test_mindestbreite_nur_fuer_vermerkte_layouts
Prüft, dass beim Laden die Mindestbreite nur an Layouts mit Vermerk wieder gesetzt wird.grün
TestMindestbreiteBeimLaden
test_listener_verbinden_und_trennen
Prüft, dass der Listener readProject einmal verbindet, beim Trennen wieder löst und beides mehrfach aufgerufen werden darf.grün
TestMindestbreiteBeimLaden
test_ohne_qgis_kein_listener
Prüft, dass im Mock-Modus kein Listener verbunden wird.grün
TestAnbindungPrintLayout
test_vorlage_ordnet_kartenrand_an
Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt.grün
TestAnbindungPrintLayout
test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
   root(["test_print_layout.py"])
   class root rootnode
   root --> g1["TestPrintLayout (5)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 7a47038..defb867 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 58 Testdateien, 676 Testfälle – 633 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 531 von 676 (79 %).
+Gesamt: 3 Pakete, 58 Testdateien, 681 Testfälle – 638 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 536 von 681 (79 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 41 | 424 | 424 | 0 | 0 | 424 von 424 (100 %) |
+| `sn_basis` | 41 | 429 | 429 | 0 | 0 | 429 von 429 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **58** | **676** | **633** | **35** | **8** | **531 von 676 (79 %)** |
+| **Summe** | **58** | **681** | **638** | **35** | **8** | **536 von 681 (79 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 531 von 676 (79 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 41 Testdateien mit 424 Testfällen (424 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 41 Testdateien mit 429 Testfällen (429 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -161,7 +161,7 @@ Testgruppen:
 - `TestKennzeichenUndKarte` (2 Testfälle): — (Docstring fehlt)
 - `TestSymbolwahl` (2 Testfälle): — (Docstring fehlt)
 - `TestHilfslayer` (3 Testfälle): — (Docstring fehlt)
-- `TestPlatzierung` (6 Testfälle): — (Docstring fehlt)
+- `TestPlatzierung` (7 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -176,6 +176,7 @@ Testgruppen:
 | `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün |
 | `TestPlatzierung.test_spalten_ungleich_breit` | Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96). | grün |
 | `TestPlatzierung.test_mindestgroesse_mit_ungleichen_spalten_und_umbruch` | Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96). | grün |
+| `TestPlatzierung.test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild` | Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt. | grün |
 | `TestPlatzierung.test_titel_zweizeilig` | Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96). | grün |
 | `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün |
 
@@ -602,7 +603,8 @@ Worum es in der Datei geht: Tests für modules/print_kartenrand.py – Anordnung
 Testgruppen:
 
 - `TestBerechneAnordnung` (5 Testfälle): — (Docstring fehlt)
-- `TestOrdneKartenrandAn` (4 Testfälle): — (Docstring fehlt)
+- `TestOrdneKartenrandAn` (5 Testfälle): — (Docstring fehlt)
+- `TestMindestbreiteBeimLaden` (3 Testfälle): — (Docstring fehlt)
 - `TestAnbindungPrintLayout` (2 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
@@ -614,8 +616,12 @@ Testgruppen:
 | `TestBerechneAnordnung.test_ohne_legenden_nur_kartenschild` | Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird. | grün |
 | `TestOrdneKartenrandAn.test_seite_hoeher_und_kartenschild_verschoben` | Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen. | grün |
 | `TestOrdneKartenrandAn.test_spalten_ungleich_breit_und_text_umgebrochen` | Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96). | grün |
+| `TestOrdneKartenrandAn.test_legenden_mindestens_so_breit_wie_kartenschild` | Prüft, dass die Legenden die Breite des Kartenschilds als Mindestbreite bekommen und das Layout sie für das Neuladen vermerkt. | grün |
 | `TestOrdneKartenrandAn.test_passt_nichts_wird_verschoben` | Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden. | grün |
 | `TestOrdneKartenrandAn.test_ohne_karte_keine_anordnung` | Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt. | grün |
+| `TestMindestbreiteBeimLaden.test_mindestbreite_nur_fuer_vermerkte_layouts` | Prüft, dass beim Laden die Mindestbreite nur an Layouts mit Vermerk wieder gesetzt wird. | grün |
+| `TestMindestbreiteBeimLaden.test_listener_verbinden_und_trennen` | Prüft, dass der Listener readProject einmal verbindet, beim Trennen wieder löst und beides mehrfach aufgerufen werden darf. | grün |
+| `TestMindestbreiteBeimLaden.test_ohne_qgis_kein_listener` | Prüft, dass im Mock-Modus kein Listener verbunden wird. | grün |
 | `TestAnbindungPrintLayout.test_vorlage_ordnet_kartenrand_an` | Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt. | grün |
 | `TestAnbindungPrintLayout.test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht` | Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht. | grün |
 
@@ -1453,7 +1459,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 424 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 429 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index f817cbf..31bb6ff 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -91,6 +91,9 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
   Mehrspaltige Legenden: Mit `setEqualColumnWidth(True)` bestimmt der längste Text die Breite **aller** Spalten.
   Schmaler wird es mit ungleichen Spalten und `setAutoWrapLinesAfter(mm)`; der Legendentitel bricht dabei **nicht**
   automatisch um und bestimmt die Mindestbreite – Zeilenumbruch (`\n`) im Titel setzen. (02.10.2026, Basis #96)
+  `setMinimumSize` an Layoutelementen wird **nicht im Projekt gespeichert**: Nach dem Neuladen schrumpft eine
+  Legende mit „an Inhalt anpassen“ bei der nächsten Größenberechnung wieder (gemessen 180 → 43,7 mm). Mindestgröße
+  am Layout vermerken und beim Laden (`readProject`) neu setzen. (02.10.2026, Basis #96)
 - **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen).
 - **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen.
 - **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe.

From 67f11e0da7909ec9c36b216504032b974c37fe47 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Mon, 5 Oct 2026 21:27:36 +0200
Subject: [PATCH 08/20] Basemap-Themen (Basis #100) dokumentiert
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Schnittstellen (Thema/Stil, Layer-Eigenschaften, neue Wrapper-Funktionen),
Fallstricke (Relief mit mehreren Quellen, Prüfwert, P10 Vektor),
Glossar (Thema, Stil), Modulkarte, Testkatalog und STATUS.

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |   9 +--
 docs/architektur/schnittstellen.md |  17 ++++-
 docs/status/STATUS.md              |   5 ++
 docs/tests/testkatalog.html        | 101 ++++++++++++++++++++++++++---
 docs/tests/testkatalog.md          |  60 +++++++++++++++--
 docs/wissen/fallstricke.md         |  14 ++++
 docs/wissen/glossar.md             |   3 +
 7 files changed, 189 insertions(+), 20 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 855a2e6..7b1f6d1 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -18,7 +18,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `ui/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück |
 | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) |
 | `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` |
-| `ui/widgets/basemap_bereich.py` | 271 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Auswertebereich, Legende aktualisieren, Stilvorlagen |
+| `ui/widgets/basemap_bereich.py` | 433 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen |
 
 ### functions/ – Wrapper-Schicht (Qt/QGIS/OS mit Mock-Fallback)
 
@@ -27,8 +27,8 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) |
 | `functions/qt_wrapper.py` ⚠ | 832 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` |
 | `functions/qgiscore_wrapper.py` ⚠ | 1 428 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
-| `functions/qgisui_wrapper.py` | 258 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner; Mock-`iface` |
-| `functions/dialog_wrapper.py` | 521 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) |
+| `functions/qgisui_wrapper.py` | 271 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, aktiver Layer (`get_active_layer`); Mock-`iface` |
+| `functions/dialog_wrapper.py` | 570 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ask_auswahl` (frei benannte Knöpfe), `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) |
 | `functions/message_wrapper.py` | 85 | MessageBar: `info`, `warning`, `error`, `success`, `push_message` |
 | `functions/variable_wrapper.py` | 120 | `get_variable(key, scope)`, `set_variable(...)` mit Präfix `sn_`; Mock-Speicher |
 | `functions/settings_logic.py` | 50 | `SettingsLogic.VARIABLEN`: Liste der gespeicherten Projektvariablen, `load()/save()` |
@@ -65,7 +65,8 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/basemap_legende_layout.py` | 471 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
 | `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) |
 | `modules/basemap_stilkorrektur.py` | 99 | Darstellungsfehler des Basemap-Layers korrigieren: Punktlinien mit Strichlänge 0 sichtbar machen (#90) |
-| `modules/basemap_stilvorlagen.py` | 181 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil), Standard in `QSettings` |
+| `modules/basemap_stilvorlagen.py` | 188 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil, mit Thema), Standard in `QSettings` |
+| `modules/basemap_themen.py` | 245 | Themen des Basemap-Layers (Farbe, Grau): Layer anlegen, an der Quell-URL erkennen, Thema wechseln, Prüfwert der Darstellung für ungespeicherte Änderungen (#100) |
 
 ### Sonstiges
 
diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md
index 49e4bd6..5c33dde 100644
--- a/docs/architektur/schnittstellen.md
+++ b/docs/architektur/schnittstellen.md
@@ -148,9 +148,24 @@ flowchart LR
 - **Legendenkatalog:** `sn_basis/data/basemap_legende.json` (Style-ID → Eintrag), erzeugt/aktualisiert mit
   `python modules/basemap_katalog.py`; gehört zur Style-Version `bm_web_col` v2.
 - **Stilvorlagen:** lokal unter `/sn_basis/basemap_stile/`, Standard in `QSettings`
-  `sn_basis/basemap_standardstil`.
+  `sn_basis/basemap_standardstil`. Die Begleit-JSON trägt seit #100 das Feld `thema` (Schlüssel aus
+  `basemap_themen.THEMEN`; fehlt bei älteren Vorlagen).
+- **Thema und Stil (Basis #100):** *Thema* = vom Dienst als Style-JSON gelieferte Darstellung (`farbe` →
+  `bm_web_col.json`, `grau` → `bm_web_gry.json`); *Stil* = lokale Anpassung (Stilvorlage). Alle Themen nutzen
+  dieselbe Kachel-URL `…/tiles/v2/bm_web_de_3857/{z}/{x}/{y}.pbf`; das Thema steht im Quellparameter `styleUrl`.
+  „Schon vorhanden“ heißt: ein Layer mit identischer Kachel-URL (Quellparameter `url`).
+- **Layer-Eigenschaften des Basemap-Layers (#100):** `sn_basemap_thema` (Schlüssel des zuletzt geladenen Themas)
+  und `sn_basemap_pruefwert` (SHA-256 der QML von Symbologie und Beschriftung ohne QGIS-Version, gesetzt nach
+  Laden eines Themas oder Stils und nach Speichern als Stil). Fehlt der Prüfwert, gilt der Änderungsstand als
+  unbekannt und vor dem Themenwechsel wird nachgefragt.
+- **Themenwechsel:** `basemap_themen.wechsle_thema` stellt die Quelle per `setDataSource` um und lädt
+  `loadDefaultStyle()`; Layer-ID, Position im Layerbaum und Bezüge in Layouts bleiben. Ziel ist der im Layerbaum
+  ausgewählte Basemap-Layer, sonst der erste des Projekts.
 - **Neue öffentliche Funktion:** `dialog_wrapper.ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)` –
   Liste mit Häkchen, Gruppen, Suche; liefert gewählte Schlüssel oder `None` bei Abbruch.
+- **Neue öffentliche Funktionen (#100):** `dialog_wrapper.ask_auswahl(title, message, optionen, default, abbrechen)` –
+  Rückfrage mit frei benannten Knöpfen `[(schluessel, beschriftung), …]`, liefert den Schlüssel; Esc/Fehler →
+  `abbrechen`, Mock-Modus → `default`. `qgisui_wrapper.get_active_layer()` – ausgewählter Layer oder `None`.
 
 ## 12. Kartenrand im Standardlayout (Basis #93)
 
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 957e749..7bec6af 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -71,6 +71,11 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das
     Kartenschild) auf
     `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus.
+17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Umgesetzt auf
+    `Plugin_SN_Basis:feature/100-basemap-themen` (Doku: `Projektwissen:docs/100-basemap-themen`), PR steht aus.
+    Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Offen: **QGIS-Test**
+    (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Stabilität des Prüfwerts). Folge-Issues: #101
+    (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 16b3044..bbacea9 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
681Testfälle
-
638grün
+
711Testfälle
+
668grün
35rot (Baseline)
-
79%mit Docstring
-
58Testdateien
+
80%mit Docstring
+
59Testdateien
Verteilung je Paket
-
sn_basis41 Dateien · 429 Testfälle
+
sn_basis42 Dateien · 459 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 41 Testdateien · 429 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 42 Testdateien · 459 Testfälle

- @@ -440,7 +440,90 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grün
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grün
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grün
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grün
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grün
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grün
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grün
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grün

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 8
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grün
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grün
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grün
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grün
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grün
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grün
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grün
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grün

Tests für modules/basemap_themen.py und den Abschnitt „Basemap-Layer“ im Basemap-Bereich (Basis #100):

TestQuelleUndThema 4TestPruefwert 3TestLayerUndThemenwechsel 7TestStilvorlageMitThema 1TestAuswahlDialog 1TestBasemapHinzufuegen 5TestThemaWechseln 8TestZielLayer 1
flowchart TD
+  root(["test_basemap_themen.py"])
+  class root rootnode
+  root --> g1["TestQuelleUndThema (4)"]
+  class g1 groupnode
+  g1 --> t1("quelle_enthaelt_kachel_url_und_style_des_themas")
+  g1 --> t2("unbekannte_style_json_ergibt_kein_thema")
+  g1 --> t3("unbekannter_schluessel_faellt_auf_farbe_zurueck")
+  g1 --> t4("vorhandener_layer_wird_an_der_quell_url_erkannt")
+  root --> g2["TestPruefwert (3)"]
+  class g2 groupnode
+  g2 --> t5("gleiche_darstellung_gleicher_pruefwert_auch_bei_anderer_qgis_version")
+  g2 --> t6("ohne_ausgabe_der_darstellung_kein_pruefwert")
+  g2 --> t7("aenderungsstand_unbekannt_unveraendert_geaendert")
+  root --> g3["TestLayerUndThemenwechsel (7)"]
+  class g3 groupnode
+  g3 --> t8("neuer_layer_hat_thema_namen_und_gemerkten_stand")
+  g3 --> t9("ungueltiger_layer_oder_fehlender_themenstil_meldet_fehler")
+  g3 --> t10("ohne_vektorkacheln_kein_layer")
+  g3 --> t11("layer_kommt_ganz_unten_in_den_layerbaum")
+  g3 --> t12("themenwechsel_stellt_quelle_um_und_merkt_neuen_stand")
+  g3 --> t13("themenwechsel_behaelt_eigenen_layernamen")
+  g3 --> t14("themenwechsel_ohne_umstellbare_quelle_meldet_fehler")
+  root --> g4["TestStilvorlageMitThema (1)"]
+  class g4 groupnode
+  g4 --> t15("stilvorlage_vermerkt_das_thema")
+  root --> g5["TestAuswahlDialog (1)"]
+  class g5 groupnode
+  g5 --> t16("mock_modus_liefert_vorbelegung")
+  root --> g6["TestBasemapHinzufuegen (5)"]
+  class g6 groupnode
+  g6 --> t17("ohne_vorhandenen_layer_wird_ohne_rueckfrage_hinzugefuegt")
+  g6 --> t18("vorhandener_layer_wird_in_der_rueckfrage_genannt_abbrechen")
+  g6 --> t19("zusaetzlich_laden_fuegt_zweiten_layer_hinzu")
+  g6 --> t20("ersetzen_laedt_vorhandenen_layer_im_gewaehlten_thema")
+  g6 --> t21("fehler_beim_anlegen_wird_gemeldet")
+  root --> g7["TestThemaWechseln (8)"]
+  class g7 groupnode
+  g7 --> t22("unveraenderter_layer_wechselt_ohne_rueckfrage")
+  g7 --> t23("gleiches_thema_aendert_nichts")
+  g7 --> t24("geaenderte_darstellung_abbrechen_behaelt_thema")
+  g7 --> t25("geaenderte_darstellung_verwerfen_wechselt")
+  g7 --> t26("als_stil_speichern_sichert_vor_dem_wechsel")
+  g7 --> t27("abgebrochenes_speichern_verhindert_den_wechsel")
+  g7 --> t28("unbekannter_stand_fragt_nach")
+  g7 --> t29("ohne_basemap_layer_nur_vorauswahl")
+  root --> g8["TestZielLayer (1)"]
+  class g8 groupnode
+  g8 --> t30("ausgewaehlter_basemap_layer_hat_vorrang")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 ok
+  class t28 ok
+  class t29 ok
+  class t30 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestQuelleUndThema
test_quelle_enthaelt_kachel_url_und_style_des_themas
Prüft, dass die Layerquelle eines Themas die gemeinsame Kachel-URL und die Style-JSON des Themas enthält.grün
TestQuelleUndThema
test_unbekannte_style_json_ergibt_kein_thema
Prüft, dass ein Layer mit fremder oder fehlender Style-JSON keinem Thema zugeordnet wird.grün
TestQuelleUndThema
test_unbekannter_schluessel_faellt_auf_farbe_zurueck
Prüft, dass ein unbekannter Themenschlüssel auf das Standardthema „Farbe“ zurückfällt.grün
TestQuelleUndThema
test_vorhandener_layer_wird_an_der_quell_url_erkannt
Prüft, dass ein vorhandener Basemap-Layer an der Kachel-URL erkannt wird – unabhängig von Name, Thema und URL-Kodierung.grün
TestPruefwert
test_gleiche_darstellung_gleicher_pruefwert_auch_bei_anderer_qgis_version
Prüft, dass dieselbe Darstellung denselben Prüfwert ergibt, auch wenn die QGIS-Version in der QML abweicht.grün
TestPruefwert
test_ohne_ausgabe_der_darstellung_kein_pruefwert
Prüft, dass kein Prüfwert entsteht, wenn der Layer seine Darstellung nicht ausgeben kann.grün
TestPruefwert
test_aenderungsstand_unbekannt_unveraendert_geaendert
Prüft, dass der Änderungsstand ohne gemerkten Prüfwert „unbekannt“ ist, nach dem Merken „unverändert“ und nach einer Änderung „geändert“.grün
TestLayerUndThemenwechsel
test_neuer_layer_hat_thema_namen_und_gemerkten_stand
Prüft, dass ein neuer Basemap-Layer im gewählten Thema entsteht, den Themenstil lädt und als unverändert gilt.grün
TestLayerUndThemenwechsel
test_ungueltiger_layer_oder_fehlender_themenstil_meldet_fehler
Prüft, dass ein ungültiger Layer oder ein nicht ladbarer Themenstil als ValueError gemeldet wird.grün
TestLayerUndThemenwechsel
test_ohne_vektorkacheln_kein_layer
Prüft, dass ohne Vektorkachel-Unterstützung (Mock-Modus) kein Layer erzeugt, sondern ein Fehler gemeldet wird.grün
TestLayerUndThemenwechsel
test_layer_kommt_ganz_unten_in_den_layerbaum
Prüft, dass der Basemap-Layer ohne automatische Einordnung ins Projekt kommt und unten an den Layerbaum angehängt wird.grün
TestLayerUndThemenwechsel
test_themenwechsel_stellt_quelle_um_und_merkt_neuen_stand
Prüft, dass der Themenwechsel die Style-JSON der Quelle umstellt, den Themenstil lädt, den automatischen Namen anpasst und den neuen Stand merkt.grün
TestLayerUndThemenwechsel
test_themenwechsel_behaelt_eigenen_layernamen
Prüft, dass ein vom Nutzer vergebener Layername beim Themenwechsel erhalten bleibt.grün
TestLayerUndThemenwechsel
test_themenwechsel_ohne_umstellbare_quelle_meldet_fehler
Prüft, dass ein Layer ohne setDataSource den Themenwechsel mit einem ValueError ablehnt.grün
TestStilvorlageMitThema
test_stilvorlage_vermerkt_das_thema
Prüft, dass eine gespeicherte Stilvorlage das Thema des Layers in ihrer Begleitdatei und in der Liste vermerkt.grün
TestAuswahlDialog
test_mock_modus_liefert_vorbelegung
Prüft, dass die Rückfrage mit frei benannten Knöpfen im Mock-Modus den vorbelegten Schlüssel liefert.grün
TestBasemapHinzufuegen
test_ohne_vorhandenen_layer_wird_ohne_rueckfrage_hinzugefuegt
Prüft, dass ohne Basemap-Layer gleicher Quelle sofort ein Layer im gewählten Thema hinzugefügt wird.grün
TestBasemapHinzufuegen
test_vorhandener_layer_wird_in_der_rueckfrage_genannt_abbrechen
Prüft, dass ein vorhandener Layer gleicher Quelle in der Rückfrage genannt wird und „Abbrechen“ nichts verändert.grün
TestBasemapHinzufuegen
test_zusaetzlich_laden_fuegt_zweiten_layer_hinzu
Prüft, dass „Zusätzlich laden“ einen weiteren Basemap-Layer neben dem vorhandenen hinzufügt.grün
TestBasemapHinzufuegen
test_ersetzen_laedt_vorhandenen_layer_im_gewaehlten_thema
Prüft, dass „Ersetzen“ keinen neuen Layer anlegt, sondern den vorhandenen im gewählten Thema neu lädt.grün
TestBasemapHinzufuegen
test_fehler_beim_anlegen_wird_gemeldet
Prüft, dass ein nicht erreichbarer Dienst als Warnung gemeldet und kein Layer hinzugefügt wird.grün
TestThemaWechseln
test_unveraenderter_layer_wechselt_ohne_rueckfrage
Prüft, dass ein unveränderter Basemap-Layer ohne Rückfrage auf das gewählte Thema wechselt.grün
TestThemaWechseln
test_gleiches_thema_aendert_nichts
Prüft, dass die Wahl des Themas, das der Layer schon hat, nichts neu lädt.grün
TestThemaWechseln
test_geaenderte_darstellung_abbrechen_behaelt_thema
Prüft, dass bei geänderter Darstellung nachgefragt wird und „Abbrechen“ Thema und Darstellung unverändert lässt.grün
TestThemaWechseln
test_geaenderte_darstellung_verwerfen_wechselt
Prüft, dass „Verwerfen“ die geänderte Darstellung aufgibt und das Thema wechselt.grün
TestThemaWechseln
test_als_stil_speichern_sichert_vor_dem_wechsel
Prüft, dass „Als Stil speichern“ die Darstellung vor dem Themenwechsel als Stilvorlage sichert.grün
TestThemaWechseln
test_abgebrochenes_speichern_verhindert_den_wechsel
Prüft, dass das Thema nicht wechselt, wenn „Als Stil speichern“ gewählt, die Namensabfrage aber abgebrochen wird.grün
TestThemaWechseln
test_unbekannter_stand_fragt_nach
Prüft, dass bei einem Layer ohne gemerkten Prüfwert (vor dieser Funktion geladen) vorsichtshalber nachgefragt wird.grün
TestThemaWechseln
test_ohne_basemap_layer_nur_vorauswahl
Prüft, dass die Themenwahl ohne Basemap-Layer nur als Vorauswahl für „Basemap hinzufügen“ dient und nichts meldet.grün
TestZielLayer
test_ausgewaehlter_basemap_layer_hat_vorrang
Prüft, dass der im Layerbaum ausgewählte Basemap-Layer Ziel des Themenwechsels ist, sonst der erste Basemap-Layer.grün

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 8
flowchart TD
   root(["test_datagrabber.py"])
   class root rootnode
   root --> g1["TestDataGrabber (8)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index defb867..914c237 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 58 Testdateien, 681 Testfälle – 638 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 536 von 681 (79 %).
+Gesamt: 3 Pakete, 59 Testdateien, 711 Testfälle – 668 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 566 von 711 (80 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 41 | 429 | 429 | 0 | 0 | 429 von 429 (100 %) |
+| `sn_basis` | 42 | 459 | 459 | 0 | 0 | 459 von 459 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **58** | **681** | **638** | **35** | **8** | **536 von 681 (79 %)** |
+| **Summe** | **59** | **711** | **668** | **35** | **8** | **566 von 711 (80 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 536 von 681 (79 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 41 Testdateien mit 429 Testfällen (429 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 42 Testdateien mit 459 Testfällen (459 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -235,6 +235,54 @@ Testgruppen:
 | `TestStilvorlagen.test_fehlende_begleitdatei_wird_toleriert` | Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird. | grün |
 | `TestStilvorlagen.test_ohne_ordner_leere_liste` | Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt. | grün |
 
+### `Plugin_SN_Basis/tests/test_basemap_themen.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_themen.py und den Abschnitt „Basemap-Layer“ im Basemap-Bereich (Basis #100):
+
+Testgruppen:
+
+- `TestQuelleUndThema` (4 Testfälle): Layerquelle der Themen, Thema eines Layers und Erkennung über die Kachel-URL.
+- `TestPruefwert` (3 Testfälle): Prüfwert der Darstellung und Änderungsstand für ungespeicherte Änderungen.
+- `TestLayerUndThemenwechsel` (7 Testfälle): Basemap-Layer anlegen, in den Layerbaum einfügen und Thema wechseln.
+- `TestStilvorlageMitThema` (1 Testfälle): Stilvorlagen vermerken das Thema des Layers.
+- `TestAuswahlDialog` (1 Testfälle): Rückfrage mit frei benannten Knöpfen.
+- `TestBasemapHinzufuegen` (5 Testfälle): Knopf „Basemap hinzufügen“ mit Rückfrage bei vorhandenem Layer gleicher Quelle.
+- `TestThemaWechseln` (8 Testfälle): Themenauswahl mit Rückfrage bei geänderter oder unbekannter Darstellung.
+- `TestZielLayer` (1 Testfälle): Auswahl des Basemap-Layers, auf den Thema und Stil wirken.
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestQuelleUndThema.test_quelle_enthaelt_kachel_url_und_style_des_themas` | Prüft, dass die Layerquelle eines Themas die gemeinsame Kachel-URL und die Style-JSON des Themas enthält. | grün |
+| `TestQuelleUndThema.test_unbekannte_style_json_ergibt_kein_thema` | Prüft, dass ein Layer mit fremder oder fehlender Style-JSON keinem Thema zugeordnet wird. | grün |
+| `TestQuelleUndThema.test_unbekannter_schluessel_faellt_auf_farbe_zurueck` | Prüft, dass ein unbekannter Themenschlüssel auf das Standardthema „Farbe“ zurückfällt. | grün |
+| `TestQuelleUndThema.test_vorhandener_layer_wird_an_der_quell_url_erkannt` | Prüft, dass ein vorhandener Basemap-Layer an der Kachel-URL erkannt wird – unabhängig von Name, Thema und URL-Kodierung. | grün |
+| `TestPruefwert.test_gleiche_darstellung_gleicher_pruefwert_auch_bei_anderer_qgis_version` | Prüft, dass dieselbe Darstellung denselben Prüfwert ergibt, auch wenn die QGIS-Version in der QML abweicht. | grün |
+| `TestPruefwert.test_ohne_ausgabe_der_darstellung_kein_pruefwert` | Prüft, dass kein Prüfwert entsteht, wenn der Layer seine Darstellung nicht ausgeben kann. | grün |
+| `TestPruefwert.test_aenderungsstand_unbekannt_unveraendert_geaendert` | Prüft, dass der Änderungsstand ohne gemerkten Prüfwert „unbekannt“ ist, nach dem Merken „unverändert“ und nach einer Änderung „geändert“. | grün |
+| `TestLayerUndThemenwechsel.test_neuer_layer_hat_thema_namen_und_gemerkten_stand` | Prüft, dass ein neuer Basemap-Layer im gewählten Thema entsteht, den Themenstil lädt und als unverändert gilt. | grün |
+| `TestLayerUndThemenwechsel.test_ungueltiger_layer_oder_fehlender_themenstil_meldet_fehler` | Prüft, dass ein ungültiger Layer oder ein nicht ladbarer Themenstil als ValueError gemeldet wird. | grün |
+| `TestLayerUndThemenwechsel.test_ohne_vektorkacheln_kein_layer` | Prüft, dass ohne Vektorkachel-Unterstützung (Mock-Modus) kein Layer erzeugt, sondern ein Fehler gemeldet wird. | grün |
+| `TestLayerUndThemenwechsel.test_layer_kommt_ganz_unten_in_den_layerbaum` | Prüft, dass der Basemap-Layer ohne automatische Einordnung ins Projekt kommt und unten an den Layerbaum angehängt wird. | grün |
+| `TestLayerUndThemenwechsel.test_themenwechsel_stellt_quelle_um_und_merkt_neuen_stand` | Prüft, dass der Themenwechsel die Style-JSON der Quelle umstellt, den Themenstil lädt, den automatischen Namen anpasst und den neuen Stand merkt. | grün |
+| `TestLayerUndThemenwechsel.test_themenwechsel_behaelt_eigenen_layernamen` | Prüft, dass ein vom Nutzer vergebener Layername beim Themenwechsel erhalten bleibt. | grün |
+| `TestLayerUndThemenwechsel.test_themenwechsel_ohne_umstellbare_quelle_meldet_fehler` | Prüft, dass ein Layer ohne setDataSource den Themenwechsel mit einem ValueError ablehnt. | grün |
+| `TestStilvorlageMitThema.test_stilvorlage_vermerkt_das_thema` | Prüft, dass eine gespeicherte Stilvorlage das Thema des Layers in ihrer Begleitdatei und in der Liste vermerkt. | grün |
+| `TestAuswahlDialog.test_mock_modus_liefert_vorbelegung` | Prüft, dass die Rückfrage mit frei benannten Knöpfen im Mock-Modus den vorbelegten Schlüssel liefert. | grün |
+| `TestBasemapHinzufuegen.test_ohne_vorhandenen_layer_wird_ohne_rueckfrage_hinzugefuegt` | Prüft, dass ohne Basemap-Layer gleicher Quelle sofort ein Layer im gewählten Thema hinzugefügt wird. | grün |
+| `TestBasemapHinzufuegen.test_vorhandener_layer_wird_in_der_rueckfrage_genannt_abbrechen` | Prüft, dass ein vorhandener Layer gleicher Quelle in der Rückfrage genannt wird und „Abbrechen“ nichts verändert. | grün |
+| `TestBasemapHinzufuegen.test_zusaetzlich_laden_fuegt_zweiten_layer_hinzu` | Prüft, dass „Zusätzlich laden“ einen weiteren Basemap-Layer neben dem vorhandenen hinzufügt. | grün |
+| `TestBasemapHinzufuegen.test_ersetzen_laedt_vorhandenen_layer_im_gewaehlten_thema` | Prüft, dass „Ersetzen“ keinen neuen Layer anlegt, sondern den vorhandenen im gewählten Thema neu lädt. | grün |
+| `TestBasemapHinzufuegen.test_fehler_beim_anlegen_wird_gemeldet` | Prüft, dass ein nicht erreichbarer Dienst als Warnung gemeldet und kein Layer hinzugefügt wird. | grün |
+| `TestThemaWechseln.test_unveraenderter_layer_wechselt_ohne_rueckfrage` | Prüft, dass ein unveränderter Basemap-Layer ohne Rückfrage auf das gewählte Thema wechselt. | grün |
+| `TestThemaWechseln.test_gleiches_thema_aendert_nichts` | Prüft, dass die Wahl des Themas, das der Layer schon hat, nichts neu lädt. | grün |
+| `TestThemaWechseln.test_geaenderte_darstellung_abbrechen_behaelt_thema` | Prüft, dass bei geänderter Darstellung nachgefragt wird und „Abbrechen“ Thema und Darstellung unverändert lässt. | grün |
+| `TestThemaWechseln.test_geaenderte_darstellung_verwerfen_wechselt` | Prüft, dass „Verwerfen“ die geänderte Darstellung aufgibt und das Thema wechselt. | grün |
+| `TestThemaWechseln.test_als_stil_speichern_sichert_vor_dem_wechsel` | Prüft, dass „Als Stil speichern“ die Darstellung vor dem Themenwechsel als Stilvorlage sichert. | grün |
+| `TestThemaWechseln.test_abgebrochenes_speichern_verhindert_den_wechsel` | Prüft, dass das Thema nicht wechselt, wenn „Als Stil speichern“ gewählt, die Namensabfrage aber abgebrochen wird. | grün |
+| `TestThemaWechseln.test_unbekannter_stand_fragt_nach` | Prüft, dass bei einem Layer ohne gemerkten Prüfwert (vor dieser Funktion geladen) vorsichtshalber nachgefragt wird. | grün |
+| `TestThemaWechseln.test_ohne_basemap_layer_nur_vorauswahl` | Prüft, dass die Themenwahl ohne Basemap-Layer nur als Vorauswahl für „Basemap hinzufügen“ dient und nichts meldet. | grün |
+| `TestZielLayer.test_ausgewaehlter_basemap_layer_hat_vorrang` | Prüft, dass der im Layerbaum ausgewählte Basemap-Layer Ziel des Themenwechsels ist, sonst der erste Basemap-Layer. | grün |
+
 ### `Plugin_SN_Basis/tests/test_bootstrap.py`
 
 Worum es in der Datei geht: — (Docstring fehlt)
@@ -1459,7 +1507,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 429 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 459 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 31bb6ff..2008d3e 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -68,6 +68,20 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
   Der Legendenkatalog `sn_basis/data/basemap_legende.json` gehört zur Style-Version `bm_web_col` v2; nach einer
   neuen Version `python modules/basemap_katalog.py` laufen lassen und Meldungen prüfen. Eine QML-Stilvorlage des
   Layers ist rund 3,7 MB groß. (02.10.2026)
+- **basemap.de-Themen (Basis #100):** Angeboten werden Farbe (`bm_web_col`), Grau (`bm_web_gry`) und Relief
+  (`bm_web_top`); `bm_web_bin` liegt auf dem Server, steht aber nicht auf basemap.de. Farbe und Grau nutzen nur die
+  Kachelquelle `bm_web_de_3857`. **Relief braucht zusätzlich** die Höhenlinien-Kacheln `bm_web_hl_de_3857` und zwei
+  WMS-Schummerungen – ein einzelner QGIS-Vektorkachel-Layer hat nur eine Kachelquelle, Relief ist deshalb vorerst
+  nicht im Themen-Dropdown. Style-Version der JSONs am 05.10.2026: `basemapde:style-version` 5.1.0.
+- **QGIS kennt kein „Darstellung geändert“-Kennzeichen pro Layer.** Für ungespeicherte Änderungen am Thema wird
+  deshalb ein Prüfwert der QML (Symbologie + Beschriftung, ohne `version`-Attribut) als Layer-Eigenschaft gemerkt
+  und vor dem Themenwechsel verglichen (`basemap_themen.aenderungsstand`). Die Berechnung schreibt die rund 3,7 MB
+  große QML in eine temporäre Datei. Ob dieselbe Darstellung in QGIS stets dieselbe QML ergibt, ist im QGIS-Test zu
+  bestätigen. Auch „Punktlinien korrigieren“ zählt als Änderung. (05.10.2026)
+- **basemap.de P10 Vektor ist kein Kacheldienst.** Es ist ein bundesweites GeoPackage (rund 60 GB, 47 normale
+  Vektorlayer, Varianten col/gry, Beta vom 14.08.2026, optimiert für QGIS 3.44.8). Die Basemap-Werkzeuge in `sn_basis`
+  (Erkennung, `selectByGeometry`, Legendenkatalog nach Style-IDs, Stilvorlagen über `renderer().styles()`) setzen
+  Vektorkacheln voraus und greifen dort nicht. Eigenes Issue. (05.10.2026)
 - **WFS-Beschränkungen der sächsischen ALKIS-Dienste:** POST/GML-Filter werden blockiert, URL-Länge ist begrenzt, serverseitige Feature-Limits. Deshalb arbeiten Flurstücks- und Grenzpunktabrufe mit BBOX, gemarkungsweisen Batches und Nutzerdialogen. Nicht „vereinfachen“, ohne das gegen den Dienst zu testen. Vollständig mit erprobten Anfragen: `docs/wissen/wfs_dienste.md`, Objektarten/Attribute: `docs/wissen/alkis_objektarten.md`. (Verfahrensgebiet, Code)
 - **Zwei Linklisten.** Zentrale `Linkliste/linkliste.csv` (Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) und die vom Plugin genutzte `Plugin_SN_Plan41/assets/Linkliste.xlsx` (`ident, gruppe, kartenebene, inhalt, link, provider, stildatei`) sind nicht verbunden. Änderungen an Diensten in **beiden** nachziehen, bis die Zusammenführung entschieden ist. (09/2026)
 - **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)
diff --git a/docs/wissen/glossar.md b/docs/wissen/glossar.md
index cc41149..ae74472 100644
--- a/docs/wissen/glossar.md
+++ b/docs/wissen/glossar.md
@@ -36,4 +36,7 @@ Flurbereinigungs-Hintergrund. Ergänzen, wenn ein Begriff im Issue erklärt werd
 | **Atlas** | QGIS-Serienkarte über einen Abdeckungslayer (`sn_abdeckungslayer`) |
 | **Kanal** | Release-Kanal: stable, testing, unstable (QGIS-Plugin-Feeds in `Repository`) |
 | **Mock-Modus** | Betrieb der Wrapper ohne QGIS/Qt für Tests |
+| **basemap.de** | Bundesweite Hintergrundkarte der Vermessungsverwaltungen; in `sn_basis` als Vektorkachel-Layer genutzt (Basemap-Legende, Basemap-Bereich im Druck-Tab) |
+| **Thema (Basemap)** | Vom Dienst basemap.de als Style-JSON bereitgestellte Darstellung: Farbe, Grau (Relief vorerst nicht). Nicht verwechseln mit *Stil* und mit QGIS-Kartenthemen |
+| **Stil (Basemap)** | Lokale Anpassung der Darstellung durch den Nutzer, als Stilvorlage im QGIS-Profil gespeichert; vermerkt, zu welchem Thema sie gehört |
 | **AG QGIS** | Arbeitsgruppe QGIS der sächsischen Flurbereinigungsbehörden; Gitea-Organisation `AG_QGIS` |

From b275df770e00bda0beaa9ef68f230fc5de7552f8 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Mon, 5 Oct 2026 21:42:58 +0200
Subject: [PATCH 09/20] QGIS-Test zu Basemap-Themen (Basis #100) nachgetragen

Co-Authored-By: Claude Opus 5.5 
---
 docs/status/STATUS.md      | 4 ++--
 docs/wissen/fallstricke.md | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 7bec6af..2c86681 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -73,8 +73,8 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus.
 17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Umgesetzt auf
     `Plugin_SN_Basis:feature/100-basemap-themen` (Doku: `Projektwissen:docs/100-basemap-themen`), PR steht aus.
-    Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Offen: **QGIS-Test**
-    (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Stabilität des Prüfwerts). Folge-Issues: #101
+    Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test
+    am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101
     (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 2008d3e..159b27d 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -76,8 +76,8 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 - **QGIS kennt kein „Darstellung geändert“-Kennzeichen pro Layer.** Für ungespeicherte Änderungen am Thema wird
   deshalb ein Prüfwert der QML (Symbologie + Beschriftung, ohne `version`-Attribut) als Layer-Eigenschaft gemerkt
   und vor dem Themenwechsel verglichen (`basemap_themen.aenderungsstand`). Die Berechnung schreibt die rund 3,7 MB
-  große QML in eine temporäre Datei. Ob dieselbe Darstellung in QGIS stets dieselbe QML ergibt, ist im QGIS-Test zu
-  bestätigen. Auch „Punktlinien korrigieren“ zählt als Änderung. (05.10.2026)
+  große QML in eine temporäre Datei. Dieselbe Darstellung ergibt in QGIS dieselbe QML, der Prüfwert ist also stabil
+  (manueller QGIS-Test 05.10.2026). Auch „Punktlinien korrigieren“ zählt als Änderung. (05.10.2026)
 - **basemap.de P10 Vektor ist kein Kacheldienst.** Es ist ein bundesweites GeoPackage (rund 60 GB, 47 normale
   Vektorlayer, Varianten col/gry, Beta vom 14.08.2026, optimiert für QGIS 3.44.8). Die Basemap-Werkzeuge in `sn_basis`
   (Erkennung, `selectByGeometry`, Legendenkatalog nach Style-IDs, Stilvorlagen über `renderer().styles()`) setzen

From e5c7d17831f289a6076b1d9e355fe1d41cac82c1 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Mon, 5 Oct 2026 21:53:50 +0200
Subject: [PATCH 10/20] Scroll-Bereich der LNO-Docks (Basis #104) dokumentiert

Schnittstellen Abschnitt 2, Fallstricke, Modulkarte, Testkatalog und
STATUS (Punkt 18; #96 und #100 als gemergt vermerkt).

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |  6 +++---
 docs/architektur/schnittstellen.md |  5 +++++
 docs/status/STATUS.md              |  8 +++++---
 docs/tests/testkatalog.html        | 28 ++++++++++++++++++++--------
 docs/tests/testkatalog.md          | 20 +++++++++++++-------
 docs/wissen/fallstricke.md         |  5 +++++
 6 files changed, 51 insertions(+), 21 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 7b1f6d1..534de21 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -15,7 +15,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `main.py` | 64 | `BasisPlugin`: baut `Navigation`, startet Verfahrensgebiet-Listener, ruft `initGui()` aller `sn_*`-Plugins nach, räumt beim Unload auf |
 | `ui/navigation.py` | 127 | Menü und Toolbar „LNO Sachsen“; `add_action(text, callback, tooltip, priority)`, `finalize_menu_and_toolbar()`, `set_active_plugin()`. Enthält noch Debug-`print` und eine `TEST ACTION` |
 | `ui/dockmanager.py` | 86 | `DockManager.show(dock)`: entfernt alle anderen `sn_dock_*`-Docks, zeigt das neue rechts an |
-| `ui/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück |
+| `ui/base_dockwidget.py` | 172 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, jeder Tab in einem Scroll-Bereich (`setWidget`, #104), ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück |
 | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) |
 | `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` |
 | `ui/widgets/basemap_bereich.py` | 433 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen |
@@ -25,7 +25,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | Datei | Zeilen | Zweck |
 |---|---:|---|
 | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) |
-| `functions/qt_wrapper.py` ⚠ | 832 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` |
+| `functions/qt_wrapper.py` ⚠ | 873 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` |
 | `functions/qgiscore_wrapper.py` ⚠ | 1 428 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
 | `functions/qgisui_wrapper.py` | 271 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, aktiver Layer (`get_active_layer`); Mock-`iface` |
 | `functions/dialog_wrapper.py` | 570 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ask_auswahl` (frei benannte Knöpfe), `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) |
@@ -146,5 +146,5 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl
 | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 |
 | `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 |
 | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 |
-| `sn_basis/functions/qt_wrapper.py` | 832 |
+| `sn_basis/functions/qt_wrapper.py` | 873 |
 | `sn_basis/ui/tabs/print_tab.py` | 730 |
diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md
index 5c33dde..5b83751 100644
--- a/docs/architektur/schnittstellen.md
+++ b/docs/architektur/schnittstellen.md
@@ -40,6 +40,11 @@ Vertrag für ein Fachplugin (Muster: `Plugin_SN_Widmung/main.py`):
   Optional `set_services(pruefmanager=..., data_grabber=...)` (wird von Fachplugin-Docks aufgerufen) und `cleanup()` (wird beim Schließen des Docks aufgerufen – Signale trennen!).
 - Gemeinsame Tabs aus `sn_basis`: `ui.tabs.print_tab.PrintTab` (Druck), `ui.tabs.settings_tab.SettingsTab` (Projekteigenschaften).
 - `dock.action` = Referenz auf die Toolbar-Action; `BaseDockWidget.closeEvent` setzt sie zurück.
+- **Scroll-Bereich je Tab (Basis #104):** `BaseDockWidget.setWidget(widget)` packt jeden Tab eines `QTabWidget` in einen
+  `QScrollArea` (`widgetResizable`, ohne Rahmen; Funktion `base_dockwidget.tabs_mit_scrollbereich`). Das gilt auch für
+  Fachplugins, die ihre Tabs in `_init_tabs_with_services()` selbst aufbauen und per `setWidget` einsetzen. Folge:
+  `dock.widget().widget(i)` liefert den Scroll-Bereich, die Tab-Instanz ist `….widget(i).widget()`; das Eltern-Widget
+  eines Tabs ist der Viewport des Scroll-Bereichs, nicht mehr das `QTabWidget`. Tabs vor `setWidget` vollständig hinzufügen.
 
 ## 3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt)
 
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 2c86681..3492e19 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -70,12 +70,14 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
 16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Gemergt (PR #95). Mehrspaltige Legenden schmaler
     (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das
     Kartenschild) auf
-    `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus.
-17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Umgesetzt auf
-    `Plugin_SN_Basis:feature/100-basemap-themen` (Doku: `Projektwissen:docs/100-basemap-themen`), PR steht aus.
+    `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, gemergt (PR #97).
+17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Gemergt (PR #103, Doku PR #25).
     Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test
     am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101
     (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
+18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Behoben auf `Plugin_SN_Basis:bugfix/104-dock-scrollbereich`
+    (Doku: `Projektwissen:docs/104-dock-scrollbereich`), PR steht aus: jeder Tab im Scroll-Bereich über
+    `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet. Offen: QGIS-Test (3.44 und 4).
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index bbacea9..d241bb9 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,20 +119,20 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
711Testfälle
-
668grün
+
716Testfälle
+
673grün
35rot (Baseline)
80%mit Docstring
59Testdateien
Verteilung je Paket
-
sn_basis42 Dateien · 459 Testfälle
+
sn_basis42 Dateien · 464 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,13 +147,13 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 42 Testdateien · 459 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 42 Testdateien · 464 Testfälle

- -

Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).

CloseEventTests 5
flowchart TD
+  

Tests für sn_basis/ui/base_dockwidget.py: closeEvent (Issue #65) und Scroll-Bereich je Tab (Basis #104).

CloseEventTests 5ScrollbereichTests 5
flowchart TD
   root(["test_base_dockwidget.py"])
   class root rootnode
   root --> g1["CloseEventTests (5)"]
@@ -163,16 +163,28 @@
   g1 --> t3("fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter")
   g1 --> t4("setzt_toolbar_action_zurueck")
   g1 --> t5("ohne_action_wird_nichts_zurueckgesetzt")
+  root --> g2["ScrollbereichTests (5)"]
+  class g2 groupnode
+  g2 --> t6("jeder_tab_liegt_in_einem_scrollbereich")
+  g2 --> t7("fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich")
+  g2 --> t8("scrollbereich_wird_nicht_doppelt_angelegt")
+  g2 --> t9("setwidget_none_und_andere_widgets_bleiben_unveraendert")
+  g2 --> t10("cleanup_erreicht_tabs_im_scrollbereich")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 10
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün
ScrollbereichTests
test_jeder_tab_liegt_in_einem_scrollbereich
Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern.grün
ScrollbereichTests
test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich
Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt.grün
ScrollbereichTests
test_scrollbereich_wird_nicht_doppelt_angelegt
Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird.grün
ScrollbereichTests
test_setwidget_none_und_andere_widgets_bleiben_unveraendert
Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden.grün
ScrollbereichTests
test_cleanup_erreicht_tabs_im_scrollbereich
Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 10
flowchart TD
   root(["test_basemap_anbindung.py"])
   class root rootnode
   root --> g1["TestMehrfachauswahl (1)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 914c237..6af84ed 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 59 Testdateien, 711 Testfälle – 668 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 566 von 711 (80 %).
+Gesamt: 3 Pakete, 59 Testdateien, 716 Testfälle – 673 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 571 von 716 (80 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 42 | 459 | 459 | 0 | 0 | 459 von 459 (100 %) |
+| `sn_basis` | 42 | 464 | 464 | 0 | 0 | 464 von 464 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **59** | **711** | **668** | **35** | **8** | **566 von 711 (80 %)** |
+| **Summe** | **59** | **716** | **673** | **35** | **8** | **571 von 716 (80 %)** |
 
 ### Legende
 
@@ -31,15 +31,16 @@ Mit Beschreibung (Docstring): 566 von 711 (80 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 42 Testdateien mit 459 Testfällen (459 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 42 Testdateien mit 464 Testfällen (464 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
-Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).
+Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py: closeEvent (Issue #65) und Scroll-Bereich je Tab (Basis #104).
 
 Testgruppen:
 
 - `CloseEventTests` (5 Testfälle): — (Docstring fehlt)
+- `ScrollbereichTests` (5 Testfälle): Lange Tabs dürfen das QGIS-Fenster nicht über den Bildschirmrand schieben (Basis #104).
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -48,6 +49,11 @@ Testgruppen:
 | `CloseEventTests.test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter` | Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert. | grün |
 | `CloseEventTests.test_setzt_toolbar_action_zurueck` | Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt. | grün |
 | `CloseEventTests.test_ohne_action_wird_nichts_zurueckgesetzt` | Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft. | grün |
+| `ScrollbereichTests.test_jeder_tab_liegt_in_einem_scrollbereich` | Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern. | grün |
+| `ScrollbereichTests.test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich` | Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt. | grün |
+| `ScrollbereichTests.test_scrollbereich_wird_nicht_doppelt_angelegt` | Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird. | grün |
+| `ScrollbereichTests.test_setwidget_none_und_andere_widgets_bleiben_unveraendert` | Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden. | grün |
+| `ScrollbereichTests.test_cleanup_erreicht_tabs_im_scrollbereich` | Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen. | grün |
 
 ### `Plugin_SN_Basis/tests/test_basemap_anbindung.py`
 
@@ -1507,7 +1513,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 459 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 464 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 159b27d..09f5841 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -92,6 +92,11 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Code-Konventionen, die man kennen muss
 
+- **Dock-Inhalt ohne Scroll-Bereich verlängert das QGIS-Fenster.** Qt verlangt für ein Dock mindestens die Höhe seines
+  Inhalts (beim `QTabWidget`: des höchsten Tabs). Ist sie größer als der Bildschirm, wächst das Hauptfenster nach unten
+  aus dem Bild, statt dass ein Scrollbalken erscheint. Abhilfe: Inhalt in `QScrollArea` mit `setWidgetResizable(True)`;
+  in den LNO-Docks erledigt das `BaseDockWidget.setWidget` (Basis #104). Die VLN-Plugins („VLN Karte“ 2.0, „VLN Karten“
+  0.5) haben keine eigene Lösung, ihr Inhalt ist nur kurz genug. (05.10.2026)
 - **Legendenelemente im Layout (QGIS 3.44):** `QgsLayoutItemLegend.adjustBoxSize()` wirkt erst, nachdem das
   Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über
   `QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))`

From 7dd9b749611f2c4d77ef47b08d4a25ab34b917fb Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Mon, 5 Oct 2026 21:56:12 +0200
Subject: [PATCH 11/20] QGIS-Test zum Scroll-Bereich (Basis #104) nachgetragen

Co-Authored-By: Claude Opus 5.5 
---
 docs/status/STATUS.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 3492e19..01f4aa9 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -77,7 +77,7 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
 18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Behoben auf `Plugin_SN_Basis:bugfix/104-dock-scrollbereich`
     (Doku: `Projektwissen:docs/104-dock-scrollbereich`), PR steht aus: jeder Tab im Scroll-Bereich über
-    `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet. Offen: QGIS-Test (3.44 und 4).
+    `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet. Manueller QGIS-Test am 05.10.2026 bestanden.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.

From 05e36abe0d40fdeff17bcb35d91f2e1f7e5c4793 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Mon, 5 Oct 2026 22:15:35 +0200
Subject: [PATCH 12/20] KBS-Umrechnung bei Zoom und Druck (Basis #99)
 dokumentiert

Schnittstellen Abschnitt 2a (KBS), Fallstricke, Modulkarte, Testkatalog
und STATUS (Punkt 19; #104 als gemergt vermerkt).

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     | 20 ++++-----
 docs/architektur/schnittstellen.md | 12 +++++
 docs/status/STATUS.md              |  9 ++--
 docs/tests/testkatalog.html        | 71 ++++++++++++++++++++++++------
 docs/tests/testkatalog.md          | 48 +++++++++++++++-----
 docs/wissen/fallstricke.md         |  6 +++
 6 files changed, 129 insertions(+), 37 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 534de21..6633fbe 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -26,8 +26,8 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 |---|---:|---|
 | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) |
 | `functions/qt_wrapper.py` ⚠ | 873 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` |
-| `functions/qgiscore_wrapper.py` ⚠ | 1 428 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
-| `functions/qgisui_wrapper.py` | 271 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, aktiver Layer (`get_active_layer`); Mock-`iface` |
+| `functions/qgiscore_wrapper.py` ⚠ | 1 474 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
+| `functions/qgisui_wrapper.py` | 292 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, Zoom auf Layer (`zoom_auf_layer`), aktiver Layer (`get_active_layer`); Mock-`iface` |
 | `functions/dialog_wrapper.py` | 570 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ask_auswahl` (frei benannte Knöpfe), `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) |
 | `functions/message_wrapper.py` | 85 | MessageBar: `info`, `warning`, `error`, `success`, `push_message` |
 | `functions/variable_wrapper.py` | 120 | `get_variable(key, scope)`, `set_variable(...)` mit Präfix `sn_`; Mock-Speicher |
@@ -56,10 +56,10 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/Datenabruf.py` ⚠ | 636 | `Datenabruf.datenabruf(...)`: WFS, ArcGIS REST, OGR laden; Raumfilter Verfahrensgebiet/Puffer/ohne |
 | `modules/LayerLoader.py` | 422 | `create_layer(provider, link, thema)`, `apply_style`, `filter_by_extent`, `filter_by_layer`, `add_to_project` |
 | `modules/Datenschreiber.py` | 448 | `schreibe_Daten(...)` in GeoPackage, `lade_Layer(...)` ins Projekt, `schreibe_log(...)` HTML-Protokoll |
-| `modules/print_logic.py` ⚠ | 1 360 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf |
-| `modules/print_layout.py` ⚠ | 1 127 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) |
+| `modules/print_logic.py` ⚠ | 1 419 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf |
+| `modules/print_layout.py` ⚠ | 1 137 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) |
 | `modules/print_kartenrand.py` | 284 | Kartenrand (Legenden, Quellenangabe, Kartenschild) überlappungsfrei anordnen: 1–3 Legendenspalten, Rand breiter, Seite ggf. höher (#93); Legenden-Mindestbreite 180 mm samt Listener für das Neuladen (#96) |
-| `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) |
+| `modules/print_layout_baufreigabe.py` | 419 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) |
 | `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg |
 | `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer |
 | `modules/basemap_legende_layout.py` | 471 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
@@ -85,7 +85,7 @@ 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`, `PrintTab`, `SettingsTab`; injiziert Services per `set_services()` |
-| `ui/tabs/working_tab.py` ⚠ | 873 | Tab „Bearbeitung“: Buttons und Handler für ALKIS/Shape laden, Gemarkungen, Flurstücke, Grenzpunkte, Beteiligung, Statistik; hält viel Ablauflogik |
+| `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 |
 | `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 |
@@ -139,12 +139,12 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl
 | Datei | Zeilen |
 |---|---:|
 | `sn_verfahrensgebiet/functions/grenzpunkt_alkis.py` | 1 674 |
-| `sn_basis/modules/print_logic.py` | 1 360 |
+| `sn_basis/modules/print_logic.py` | 1 419 |
 | `sn_plan41/ui/tab_a_logic.py` | 1 239 |
-| `sn_basis/functions/qgiscore_wrapper.py` | 1 428 |
-| `sn_basis/modules/print_layout.py` | 1 127 |
+| `sn_basis/functions/qgiscore_wrapper.py` | 1 474 |
+| `sn_basis/modules/print_layout.py` | 1 137 |
 | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 |
-| `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 |
+| `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 820 |
 | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 |
 | `sn_basis/functions/qt_wrapper.py` | 873 |
 | `sn_basis/ui/tabs/print_tab.py` | 730 |
diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md
index 5b83751..b9a2c49 100644
--- a/docs/architektur/schnittstellen.md
+++ b/docs/architektur/schnittstellen.md
@@ -46,6 +46,18 @@ Vertrag für ein Fachplugin (Muster: `Plugin_SN_Widmung/main.py`):
   `dock.widget().widget(i)` liefert den Scroll-Bereich, die Tab-Instanz ist `….widget(i).widget()`; das Eltern-Widget
   eines Tabs ist der Viewport des Scroll-Bereichs, nicht mehr das `QTabWidget`. Tabs vor `setWidget` vollständig hinzufügen.
 
+## 2a. Koordinatenbezugssysteme (Basis #99)
+
+- **Ausdehnungen stehen im KBS ihrer Quelle:** `layer.extent()` im Layer-KBS, `canvas.extent()` im KBS der
+  Kartenansicht (`canvas.mapSettings().destinationCrs()`), Layoutkarten in `map_item.crs()`. Wer eine Ausdehnung
+  weitergibt, rechnet sie mit `qgiscore_wrapper.transformiere_rechteck(rechteck, quell_crs, ziel_crs)` um.
+- **Zoom auf einen Layer:** `qgisui_wrapper.zoom_auf_layer(layer, canvas=None)` – nie `canvas.setExtent(layer.extent())`.
+- **Druck-KBS:** Die Hauptkarte der Layouts (§ 38/§ 41, Baufreigabe) steht im KBS des Verfahrensgebiet-Layers, ohne
+  diesen im Projekt-KBS (`PrintLogic._druck_crs`); `create_single_page_layout`/`create_baufreigabe_layout` nehmen es
+  als Parameter `crs` und setzen es vor der Ausdehnung. Alle Druckbereiche werden in dieses KBS umgerechnet.
+- **Druckrechteck:** Der Layer „Druckrechteck“ hat das KBS des Verfahrensgebiets; das in der Kartenansicht
+  gezeichnete Rechteck wird vor dem Speichern umgerechnet. `druckrechteck_xmin/…` stehen im KBS dieses Layers.
+
 ## 3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt)
 
 Zugriff nur über `sn_basis.functions.variable_wrapper.get_variable(key, scope="project")` / `set_variable(...)`.
diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 01f4aa9..7b60c6e 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -75,9 +75,12 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
     Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test
     am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101
     (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
-18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Behoben auf `Plugin_SN_Basis:bugfix/104-dock-scrollbereich`
-    (Doku: `Projektwissen:docs/104-dock-scrollbereich`), PR steht aus: jeder Tab im Scroll-Bereich über
-    `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet. Manueller QGIS-Test am 05.10.2026 bestanden.
+18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Gemergt (PR #105, Doku PR #26): jeder Tab im
+    Scroll-Bereich über `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet.
+19. **Zoom und Drucklayout bei anderem Projekt-KBS (Basis #99).** Behoben auf `Plugin_SN_Basis:bugfix/99-druck-kbs`
+    und `Plugin_SN_Verfahrensgebiet:bugfix/99-zoom-kbs` (Doku: `Projektwissen:docs/99-druck-kbs`), PRs stehen aus;
+    **Basis zuerst mergen** (Verfahrensgebiet nutzt `zoom_auf_layer`). Layoutkarte im KBS des Verfahrensgebiets.
+    Offen: QGIS-Test. Folge-Issue (Entwurf): Projekt-KBS prüfen und amtliches KBS nach Länderkennung vorschlagen.
 
 **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
 und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index d241bb9..9bff7ed 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,21 +119,21 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
716Testfälle
-
673grün
+
729Testfälle
+
686grün
35rot (Baseline)
80%mit Docstring
-
59Testdateien
+
60Testdateien
Verteilung je Paket
-
sn_basis42 Dateien · 464 Testfälle
-
sn_verfahrensgebiet12 Dateien · 208 Testfälle
+
sn_basis43 Dateien · 475 Testfälle
+
sn_verfahrensgebiet12 Dateien · 210 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 42 Testdateien · 464 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 43 Testdateien · 475 Testfälle

- @@ -742,7 +742,44 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
DockManagerShowTests
test_none_dock_meldet_fehler_und_zeigt_nichts_an
Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt.grün
DockManagerShowTests
test_setzt_objectname_wenn_leer
Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält.grün
DockManagerShowTests
test_entfernt_andere_sn_docks_aber_nicht_sich_selbst
Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben.grün
DockManagerShowTests
test_zeigt_neues_dock_mit_default_area_an
Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_entfernen_wird_als_warnung_gemeldet
Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_anzeigen_wird_gemeldet
Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird.grün

Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).

ImportXlsxTests 5ExcelImporterKonstruktorTests 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
DockManagerShowTests
test_none_dock_meldet_fehler_und_zeigt_nichts_an
Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt.grün
DockManagerShowTests
test_setzt_objectname_wenn_leer
Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält.grün
DockManagerShowTests
test_entfernt_andere_sn_docks_aber_nicht_sich_selbst
Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben.grün
DockManagerShowTests
test_zeigt_neues_dock_mit_default_area_an
Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_entfernen_wird_als_warnung_gemeldet
Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_anzeigen_wird_gemeldet
Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird.grün

Tests zu Basis #99: Ist das Projekt-KBS ein anderes als das KBS des Verfahrensgebiets, müssen

TestTransformiereRechteck 3TestZoomAufLayer 2TestDruckKbs 5TestLayoutkarteImDruckKbs 1
flowchart TD
+  root(["test_druck_kbs.py"])
+  class root rootnode
+  root --> g1["TestTransformiereRechteck (3)"]
+  class g1 groupnode
+  g1 --> t1("verschiedene_kbs_werden_umgerechnet")
+  g1 --> t2("gleiches_fehlendes_oder_ungueltiges_kbs_laesst_rechteck_unveraendert")
+  g1 --> t3("fehler_bei_der_umrechnung_liefert_das_rechteck")
+  root --> g2["TestZoomAufLayer (2)"]
+  class g2 groupnode
+  g2 --> t4("ausdehnung_wird_ins_kbs_der_kartenansicht_umgerechnet")
+  g2 --> t5("ohne_layer_oder_kartenansicht_kein_zoom")
+  root --> g3["TestDruckKbs (5)"]
+  class g3 groupnode
+  g3 --> t6("druck_kbs_ist_das_kbs_des_verfahrensgebiets")
+  g3 --> t7("kartenfenster_wird_vom_kbs_der_kartenansicht_umgerechnet")
+  g3 --> t8("auswahl_wird_vor_dem_puffern_vom_layer_kbs_umgerechnet")
+  g3 --> t9("ohne_druck_kbs_wird_nicht_umgerechnet")
+  g3 --> t10("druckrechteck_wird_ins_kbs_seines_layers_umgerechnet")
+  root --> g4["TestLayoutkarteImDruckKbs (1)"]
+  class g4 groupnode
+  g4 --> t11("hauptkarte_bekommt_das_kbs_vor_der_ausdehnung")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestTransformiereRechteck
test_verschiedene_kbs_werden_umgerechnet
Prüft, dass ein Rechteck bei verschiedenen KBS mit QgsCoordinateTransform von Quell- nach Ziel-KBS umgerechnet wird.grün
TestTransformiereRechteck
test_gleiches_fehlendes_oder_ungueltiges_kbs_laesst_rechteck_unveraendert
Prüft, dass bei gleichem, fehlendem oder ungültigem KBS nicht umgerechnet und das Rechteck unverändert geliefert wird.grün
TestTransformiereRechteck
test_fehler_bei_der_umrechnung_liefert_das_rechteck
Prüft, dass ein Fehler bei der Umrechnung nicht durchschlägt, sondern das ursprüngliche Rechteck zurückkommt.grün
TestZoomAufLayer
test_ausdehnung_wird_ins_kbs_der_kartenansicht_umgerechnet
Prüft, dass zoom_auf_layer die Layer-Ausdehnung vom Layer-KBS in das KBS der Kartenansicht umrechnet, bevor gezoomt wird.grün
TestZoomAufLayer
test_ohne_layer_oder_kartenansicht_kein_zoom
Prüft, dass zoom_auf_layer ohne Layer oder ohne Kartenansicht nichts tut und False liefert.grün
TestDruckKbs
test_druck_kbs_ist_das_kbs_des_verfahrensgebiets
Prüft, dass die Layoutkarte im KBS des Verfahrensgebiet-Layers steht und ohne Verfahrensgebiet das Projekt-KBS gilt.grün
TestDruckKbs
test_kartenfenster_wird_vom_kbs_der_kartenansicht_umgerechnet
Prüft, dass der Druckbereich „wie Kartenfenster“ vom KBS der Kartenansicht in das Druck-KBS umgerechnet wird.grün
TestDruckKbs
test_auswahl_wird_vor_dem_puffern_vom_layer_kbs_umgerechnet
Prüft, dass der Druckbereich „Auswahl“ aus dem KBS des Auswahl-Layers umgerechnet und erst danach gepuffert wird.grün
TestDruckKbs
test_ohne_druck_kbs_wird_nicht_umgerechnet
Prüft, dass _resolve_extent ohne Druck-KBS wie bisher nicht umrechnet (Aufrufe ohne den neuen Parameter).grün
TestDruckKbs
test_druckrechteck_wird_ins_kbs_seines_layers_umgerechnet
Prüft, dass ein in der Kartenansicht gezeichnetes Druckrechteck vor dem Speichern in das KBS des Druckrechteck-Layers umgerechnet wird.grün
TestLayoutkarteImDruckKbs
test_hauptkarte_bekommt_das_kbs_vor_der_ausdehnung
Prüft, dass die Hauptkarte eines neuen Layouts das Druck-KBS erhält, bevor ihre Ausdehnung gesetzt wird.grün

Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).

ImportXlsxTests 5ExcelImporterKonstruktorTests 2
flowchart TD
   root(["test_excel_importer.py"])
   class root rootnode
   root --> g1["ImportXlsxTests (5)"]
@@ -1602,9 +1639,9 @@
   

sn_verfahrensgebiet

-

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 12 Testdateien · 208 Testfälle

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 12 Testdateien · 210 Testfälle

- @@ -2111,7 +2148,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFindShp
test_findet_shp_in_root
— (Docstring fehlt)grün
TestFindShp
test_findet_shp_in_unterordner
— (Docstring fehlt)grün
TestFindShp
test_kein_shp_gibt_none_zurueck
— (Docstring fehlt)grün
TestFindShp
test_case_insensitive_erkennung
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_leere_geometrien_geben_none
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_keine_features_gibt_none
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_attribute_von_erstem_feature_werden_gesetzt
Attributwerte des ersten Features werden auf das Union-Feature übertragen.grün
TestLadeVgAusZip
test_dialog_abgebrochen_gibt_none_zurueck
— (Docstring fehlt)rot
TestLadeVgAusZip
test_zip_fehler_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_kein_shp_in_zip_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_invalider_qgslayer_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_leere_geometrien_geben_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_erfolg_gibt_memory_layer_zurueck
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_lade_zip_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_shape_vg_sofort_ins_projekt_nach_laden
Shape-VG wird unmittelbar nach dem Laden ins Projekt eingefügt.rot
TestVerfahrensgebietShapeKomplett
test_shape_vg_wird_bei_abbruch_aus_projekt_entfernt
Shape-VG wird bei Fehler/Abbruch aus dem Projekt entfernt.rot
TestVerfahrensgebietShapeKomplett
test_shape_vg_bleibt_im_projekt_bei_erfolg
Bei erfolgreichem Abschluss wird shape_vg NICHT aus dem Projekt entfernt.grün
TestVerfahrensgebietShapeKomplett
test_flst_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_keine_flurstuecke_selektiert_gibt_warning
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_flaechen_uebereinstimmung_gibt_info_und_kein_dialog
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_abweichung_abbrechen_gibt_shape_vg_zurueck
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_abweichung_detailpruefung_ruft_knickpunkt_pruefung_auf
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_punktort_ta_none_gibt_shape_vg_zurueck_und_zeigt_fehler
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_temp_layer_wird_stets_entfernt
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_bror_layer_ist_stets_none
Dritter Rückgabewert ist im Shape-Workflow immer None (kein BROR).grün
TestVerfahrensgebietShapeKomplett
test_bror_geladen_und_zurueckgegeben_wenn_vkz_gesetzt
VKZ gesetzt → verfahrensgebiet_alkis aufgerufen, bror_layer zurückgegeben.rot
TestVerfahrensgebietShapeKomplett
test_bror_wird_vor_shape_vg_ins_projekt_eingefuegt
BROR wird VOR shape_vg ins Projekt eingefügt (kein Namenskonflikt).rot
TestVerfahrensgebietShapeKomplett
test_bror_wird_bei_abbruch_aus_projekt_entfernt
BROR und shape_vg werden bei Fehler aus dem Projekt entfernt.rot
TestVerfahrensgebietShapeKomplett
test_verfahrensgebiet_alkis_nicht_aufgerufen_wenn_vkz_leer
Leere VKZ → verfahrensgebiet_alkis wird NICHT aufgerufen.grün
TestVerfahrensgebietShapeKomplett
test_bror_als_erstes_argument_beim_flaechenvergleich
Mit BROR: _vergleiche_flaechen wird (bror, shape_vg, ...) aufgerufen.rot
TestVerfahrensgebietShapeKomplett
test_fallback_auf_temp_enriched_wenn_bror_ladeversagen
BROR-Ladeversagen → Fallback: _vergleiche_flaechen mit shape_vg vs temp_enriched.rot

— (Docstring fehlt)

TestWorkingTab 24TestHandleVerfShape 9
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFindShp
test_findet_shp_in_root
— (Docstring fehlt)grün
TestFindShp
test_findet_shp_in_unterordner
— (Docstring fehlt)grün
TestFindShp
test_kein_shp_gibt_none_zurueck
— (Docstring fehlt)grün
TestFindShp
test_case_insensitive_erkennung
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_leere_geometrien_geben_none
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_keine_features_gibt_none
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_attribute_von_erstem_feature_werden_gesetzt
Attributwerte des ersten Features werden auf das Union-Feature übertragen.grün
TestLadeVgAusZip
test_dialog_abgebrochen_gibt_none_zurueck
— (Docstring fehlt)rot
TestLadeVgAusZip
test_zip_fehler_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_kein_shp_in_zip_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_invalider_qgslayer_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_leere_geometrien_geben_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_erfolg_gibt_memory_layer_zurueck
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_lade_zip_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_shape_vg_sofort_ins_projekt_nach_laden
Shape-VG wird unmittelbar nach dem Laden ins Projekt eingefügt.rot
TestVerfahrensgebietShapeKomplett
test_shape_vg_wird_bei_abbruch_aus_projekt_entfernt
Shape-VG wird bei Fehler/Abbruch aus dem Projekt entfernt.rot
TestVerfahrensgebietShapeKomplett
test_shape_vg_bleibt_im_projekt_bei_erfolg
Bei erfolgreichem Abschluss wird shape_vg NICHT aus dem Projekt entfernt.grün
TestVerfahrensgebietShapeKomplett
test_flst_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_keine_flurstuecke_selektiert_gibt_warning
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_flaechen_uebereinstimmung_gibt_info_und_kein_dialog
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_abweichung_abbrechen_gibt_shape_vg_zurueck
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_abweichung_detailpruefung_ruft_knickpunkt_pruefung_auf
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_punktort_ta_none_gibt_shape_vg_zurueck_und_zeigt_fehler
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_temp_layer_wird_stets_entfernt
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_bror_layer_ist_stets_none
Dritter Rückgabewert ist im Shape-Workflow immer None (kein BROR).grün
TestVerfahrensgebietShapeKomplett
test_bror_geladen_und_zurueckgegeben_wenn_vkz_gesetzt
VKZ gesetzt → verfahrensgebiet_alkis aufgerufen, bror_layer zurückgegeben.rot
TestVerfahrensgebietShapeKomplett
test_bror_wird_vor_shape_vg_ins_projekt_eingefuegt
BROR wird VOR shape_vg ins Projekt eingefügt (kein Namenskonflikt).rot
TestVerfahrensgebietShapeKomplett
test_bror_wird_bei_abbruch_aus_projekt_entfernt
BROR und shape_vg werden bei Fehler aus dem Projekt entfernt.rot
TestVerfahrensgebietShapeKomplett
test_verfahrensgebiet_alkis_nicht_aufgerufen_wenn_vkz_leer
Leere VKZ → verfahrensgebiet_alkis wird NICHT aufgerufen.grün
TestVerfahrensgebietShapeKomplett
test_bror_als_erstes_argument_beim_flaechenvergleich
Mit BROR: _vergleiche_flaechen wird (bror, shape_vg, ...) aufgerufen.rot
TestVerfahrensgebietShapeKomplett
test_fallback_auf_temp_enriched_wenn_bror_ladeversagen
BROR-Ladeversagen → Fallback: _vergleiche_flaechen mit shape_vg vs temp_enriched.rot

— (Docstring fehlt)

TestWorkingTab 24TestHandleVerfShape 9TestZoomImProjektKbs 2
flowchart TD
   root(["test_working_tab.py"])
   class root rootnode
   root --> g1["TestWorkingTab (24)"]
@@ -2151,6 +2188,10 @@
   g2 --> t31("bror_layer_erhaelt_qml_stil")
   g2 --> t32("enriched_layer_already_in_project_not_added_again")
   g2 --> t33("busy_state_set_during_execution")
+  root --> g3["TestZoomImProjektKbs (2)"]
+  class g3 groupnode
+  g3 --> t34("alkis_zoom_ueber_zoom_auf_layer")
+  g3 --> t35("shape_zoom_ueber_zoom_auf_layer")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -2184,11 +2225,13 @@
   class t31 ok
   class t32 ok
   class t33 ok
+  class t34 ok
+  class t35 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestWorkingTab
test_set_busy_updates_button_state
— (Docstring fehlt)grün
TestWorkingTab
test_on_verf_layer_changed_valid_sets_variable
— (Docstring fehlt)grün
TestWorkingTab
test_on_verf_layer_changed_none_clears_variable
— (Docstring fehlt)grün
TestWorkingTab
test_handle_verf_alkis_success_path
verfahrensgebiet_alkis_komplett → 4-Tupel (enriched, flst, bror, FIRST).rot
TestWorkingTab
test_handle_verf_alkis_failure_clears_layer_var
Status NONE → Layer-Variable wird auf '' gesetzt.grün
TestWorkingTab
test_handle_flurst_alkis_first_load_uebernimmt_layer
Erstmaliges Laden über 'Flurstücke laden' fügt den Layer dem Projekt hinzu.grün
TestWorkingTab
test_handle_flurst_aus_gemarkungen_ruft_flurstueck_alkis_ohne_schluessel_abfrage
Der Gemarkungen-Auswahl-Button ruft dieselbe Ladefunktion mit ohne_schluessel_abfrage=True auf (#47).grün
TestWorkingTab
test_handle_flurst_aus_gemarkungen_abbruch_zeigt_keine_zusaetzliche_meldung
Bricht flurstueck_alkis() ab (None, NONE), zeigt der Button keine eigene Meldung – flurstueck_alkis() hat das schon getan (#47).grün
TestWorkingTab
test_handle_grenz_alkis_success_sets_haken
Grenzpunkte erfolgreich geladen → haken_grenz wird gesetzt.rot
TestWorkingTab
test_handle_grenz_alkis_none_result_sets_no_haken
grenzpunkt_alkis liefert (None, None) → kein Haken.rot
TestWorkingTab
test_handle_flurst_alkis_success_sets_haken_and_style
— (Docstring fehlt)grün
TestWorkingTab
test_handle_flurst_alkis_existing_layer_keep_path
— (Docstring fehlt)grün
TestWorkingTab
test_handle_flurst_alkis_invalid_input_returns_early
Status NONE (z. B. Nutzerabbruch) → kein Layer, kein Haken.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_active_layer
Kein aktiver Layer → warning, kein Layer wird erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_required_fields
Layer ohne Pflichtfelder → warning, kein Layer wird erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_selection
Keine Flurstück-Auswahl → warning, kein Layer erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_new_layer_success
Erstlauf ohne bestehenden Layer → Layer wird erstellt und hinzugefügt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ergaenzen_dispatch
Entscheidung 'ergaenzen' → ergaenze_verfahrensgebiet wird aufgerufen.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_entfernen_dispatch
Entscheidung 'entfernen' → entferne_aus_verfahrensgebiet wird aufgerufen.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ersetzen_dispatch
Entscheidung 'ersetzen' → alter Layer entfernt, neuer hinzugefügt.grün
TestWorkingTab
test_aus_liste_button_shows_not_implemented_hint
— (Docstring fehlt)grün
TestWorkingTab
test_handle_gem_alkis_service_reachable_adds_layer
Dienst erreichbar → Layer ins Projekt, Stil angewendet, Haken gesetzt.grün
TestWorkingTab
test_handle_gem_alkis_service_unreachable_shows_warning
Dienst nicht erreichbar (None) → warning, kein addMapLayer.grün
TestWorkingTab
test_handle_gem_alkis_layer_already_exists_shows_info
Layer 'Gemarkungen' bereits vorhanden → info, kein erneutes Laden.grün
TestHandleVerfShape
test_status_none_clears_layer_variable
Status NONE → Layer-Variable wird auf '' gesetzt.grün
TestHandleVerfShape
test_button_always_re_enabled_on_failure
Button muss nach Status NONE wieder freigegeben sein (finally).grün
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_nein_bricht_ab
Verfahrensnummer leer + Nutzer wählt 'Nein' → Vorgang abgebrochen.rot
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_ja_fuehrt_fort
Verfahrensnummer leer + Nutzer wählt 'Ja' → Pipeline wird aufgerufen.rot
TestHandleVerfShape
test_verfahrensnummer_gesetzt_kein_rueckfrage_dialog
Verfahrensnummer gesetzt → kein ask_yes_no-Dialog.grün
TestHandleVerfShape
test_success_path_applies_style_and_sets_haken
Erfolg → verfahrensgebiet.qml angewendet, haken_verf gesetzt, success aufgerufen.grün
TestHandleVerfShape
test_bror_layer_erhaelt_qml_stil
Wenn bror_layer zurückgegeben → BauRaumOderBodenordnungsrecht.qml angewendet.grün
TestHandleVerfShape
test_enriched_layer_already_in_project_not_added_again
Layer bereits im Projekt → kein erneutes addMapLayer für VG-Layer.grün
TestHandleVerfShape
test_busy_state_set_during_execution
Button wird während der Ausführung auf busy gesetzt.grün
+ classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestWorkingTab
test_set_busy_updates_button_state
— (Docstring fehlt)grün
TestWorkingTab
test_on_verf_layer_changed_valid_sets_variable
— (Docstring fehlt)grün
TestWorkingTab
test_on_verf_layer_changed_none_clears_variable
— (Docstring fehlt)grün
TestWorkingTab
test_handle_verf_alkis_success_path
verfahrensgebiet_alkis_komplett → 4-Tupel (enriched, flst, bror, FIRST).rot
TestWorkingTab
test_handle_verf_alkis_failure_clears_layer_var
Status NONE → Layer-Variable wird auf '' gesetzt.grün
TestWorkingTab
test_handle_flurst_alkis_first_load_uebernimmt_layer
Erstmaliges Laden über 'Flurstücke laden' fügt den Layer dem Projekt hinzu.grün
TestWorkingTab
test_handle_flurst_aus_gemarkungen_ruft_flurstueck_alkis_ohne_schluessel_abfrage
Der Gemarkungen-Auswahl-Button ruft dieselbe Ladefunktion mit ohne_schluessel_abfrage=True auf (#47).grün
TestWorkingTab
test_handle_flurst_aus_gemarkungen_abbruch_zeigt_keine_zusaetzliche_meldung
Bricht flurstueck_alkis() ab (None, NONE), zeigt der Button keine eigene Meldung – flurstueck_alkis() hat das schon getan (#47).grün
TestWorkingTab
test_handle_grenz_alkis_success_sets_haken
Grenzpunkte erfolgreich geladen → haken_grenz wird gesetzt.rot
TestWorkingTab
test_handle_grenz_alkis_none_result_sets_no_haken
grenzpunkt_alkis liefert (None, None) → kein Haken.rot
TestWorkingTab
test_handle_flurst_alkis_success_sets_haken_and_style
— (Docstring fehlt)grün
TestWorkingTab
test_handle_flurst_alkis_existing_layer_keep_path
— (Docstring fehlt)grün
TestWorkingTab
test_handle_flurst_alkis_invalid_input_returns_early
Status NONE (z. B. Nutzerabbruch) → kein Layer, kein Haken.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_active_layer
Kein aktiver Layer → warning, kein Layer wird erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_required_fields
Layer ohne Pflichtfelder → warning, kein Layer wird erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_selection
Keine Flurstück-Auswahl → warning, kein Layer erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_new_layer_success
Erstlauf ohne bestehenden Layer → Layer wird erstellt und hinzugefügt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ergaenzen_dispatch
Entscheidung 'ergaenzen' → ergaenze_verfahrensgebiet wird aufgerufen.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_entfernen_dispatch
Entscheidung 'entfernen' → entferne_aus_verfahrensgebiet wird aufgerufen.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ersetzen_dispatch
Entscheidung 'ersetzen' → alter Layer entfernt, neuer hinzugefügt.grün
TestWorkingTab
test_aus_liste_button_shows_not_implemented_hint
— (Docstring fehlt)grün
TestWorkingTab
test_handle_gem_alkis_service_reachable_adds_layer
Dienst erreichbar → Layer ins Projekt, Stil angewendet, Haken gesetzt.grün
TestWorkingTab
test_handle_gem_alkis_service_unreachable_shows_warning
Dienst nicht erreichbar (None) → warning, kein addMapLayer.grün
TestWorkingTab
test_handle_gem_alkis_layer_already_exists_shows_info
Layer 'Gemarkungen' bereits vorhanden → info, kein erneutes Laden.grün
TestHandleVerfShape
test_status_none_clears_layer_variable
Status NONE → Layer-Variable wird auf '' gesetzt.grün
TestHandleVerfShape
test_button_always_re_enabled_on_failure
Button muss nach Status NONE wieder freigegeben sein (finally).grün
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_nein_bricht_ab
Verfahrensnummer leer + Nutzer wählt 'Nein' → Vorgang abgebrochen.rot
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_ja_fuehrt_fort
Verfahrensnummer leer + Nutzer wählt 'Ja' → Pipeline wird aufgerufen.rot
TestHandleVerfShape
test_verfahrensnummer_gesetzt_kein_rueckfrage_dialog
Verfahrensnummer gesetzt → kein ask_yes_no-Dialog.grün
TestHandleVerfShape
test_success_path_applies_style_and_sets_haken
Erfolg → verfahrensgebiet.qml angewendet, haken_verf gesetzt, success aufgerufen.grün
TestHandleVerfShape
test_bror_layer_erhaelt_qml_stil
Wenn bror_layer zurückgegeben → BauRaumOderBodenordnungsrecht.qml angewendet.grün
TestHandleVerfShape
test_enriched_layer_already_in_project_not_added_again
Layer bereits im Projekt → kein erneutes addMapLayer für VG-Layer.grün
TestHandleVerfShape
test_busy_state_set_during_execution
Button wird während der Ausführung auf busy gesetzt.grün
TestZoomImProjektKbs
test_alkis_zoom_ueber_zoom_auf_layer
Prüft, dass nach dem Laden aus ALKIS über zoom_auf_layer auf das Verfahrensgebiet gezoomt wird und nicht mit der unumgerechneten Layer-Ausdehnung.grün
TestZoomImProjektKbs
test_shape_zoom_ueber_zoom_auf_layer
Prüft, dass nach dem Laden aus einem Shape über zoom_auf_layer auf das Verfahrensgebiet gezoomt wird und nicht mit der unumgerechneten Layer-Ausdehnung.grün
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md index 6af84ed..ace6f90 100644 --- a/docs/tests/testkatalog.md +++ b/docs/tests/testkatalog.md @@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline. Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`) -Gesamt: 3 Pakete, 59 Testdateien, 716 Testfälle – 673 grün, 35 rot (Baseline), 8 übersprungen. -Mit Beschreibung (Docstring): 571 von 716 (80 %). +Gesamt: 3 Pakete, 60 Testdateien, 729 Testfälle – 686 grün, 35 rot (Baseline), 8 übersprungen. +Mit Beschreibung (Docstring): 584 von 729 (80 %). ## Zusammenfassung | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung | | --- | ---: | ---: | ---: | ---: | ---: | ---: | -| `sn_basis` | 42 | 464 | 464 | 0 | 0 | 464 von 464 (100 %) | -| `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) | +| `sn_basis` | 43 | 475 | 475 | 0 | 0 | 475 von 475 (100 %) | +| `sn_verfahrensgebiet` | 12 | 210 | 168 | 34 | 8 | 108 von 210 (51 %) | | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) | -| **Summe** | **59** | **716** | **673** | **35** | **8** | **571 von 716 (80 %)** | +| **Summe** | **60** | **729** | **686** | **35** | **8** | **584 von 729 (80 %)** | ### Legende @@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 571 von 716 (80 %). ## Paket `sn_basis` -Plattform-Plugin, Ordner `Plugin_SN_Basis` – 42 Testdateien mit 464 Testfällen (464 grün, 0 rot, 0 übersprungen). +Plattform-Plugin, Ordner `Plugin_SN_Basis` – 43 Testdateien mit 475 Testfällen (475 grün, 0 rot, 0 übersprungen). ### `Plugin_SN_Basis/tests/test_base_dockwidget.py` @@ -431,6 +431,31 @@ Testgruppen: | `DockManagerShowTests.test_fehler_beim_entfernen_wird_als_warnung_gemeldet` | Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird. | grün | | `DockManagerShowTests.test_fehler_beim_anzeigen_wird_gemeldet` | Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird. | grün | +### `Plugin_SN_Basis/tests/test_druck_kbs.py` + +Worum es in der Datei geht: Tests zu Basis #99: Ist das Projekt-KBS ein anderes als das KBS des Verfahrensgebiets, müssen + +Testgruppen: + +- `TestTransformiereRechteck` (3 Testfälle): Umrechnung eines Rechtecks zwischen zwei KBS. +- `TestZoomAufLayer` (2 Testfälle): Zoom der Kartenansicht auf einen Layer in einem anderen KBS. +- `TestDruckKbs` (5 Testfälle): KBS der Layoutkarte und Umrechnung der Druckbereiche. +- `TestLayoutkarteImDruckKbs` (1 Testfälle): Die Layoutkarte bekommt das Druck-KBS, bevor die Ausdehnung gesetzt wird. + +| Testfall | prüft | Status | +| --- | --- | --- | +| `TestTransformiereRechteck.test_verschiedene_kbs_werden_umgerechnet` | Prüft, dass ein Rechteck bei verschiedenen KBS mit QgsCoordinateTransform von Quell- nach Ziel-KBS umgerechnet wird. | grün | +| `TestTransformiereRechteck.test_gleiches_fehlendes_oder_ungueltiges_kbs_laesst_rechteck_unveraendert` | Prüft, dass bei gleichem, fehlendem oder ungültigem KBS nicht umgerechnet und das Rechteck unverändert geliefert wird. | grün | +| `TestTransformiereRechteck.test_fehler_bei_der_umrechnung_liefert_das_rechteck` | Prüft, dass ein Fehler bei der Umrechnung nicht durchschlägt, sondern das ursprüngliche Rechteck zurückkommt. | grün | +| `TestZoomAufLayer.test_ausdehnung_wird_ins_kbs_der_kartenansicht_umgerechnet` | Prüft, dass zoom_auf_layer die Layer-Ausdehnung vom Layer-KBS in das KBS der Kartenansicht umrechnet, bevor gezoomt wird. | grün | +| `TestZoomAufLayer.test_ohne_layer_oder_kartenansicht_kein_zoom` | Prüft, dass zoom_auf_layer ohne Layer oder ohne Kartenansicht nichts tut und False liefert. | grün | +| `TestDruckKbs.test_druck_kbs_ist_das_kbs_des_verfahrensgebiets` | Prüft, dass die Layoutkarte im KBS des Verfahrensgebiet-Layers steht und ohne Verfahrensgebiet das Projekt-KBS gilt. | grün | +| `TestDruckKbs.test_kartenfenster_wird_vom_kbs_der_kartenansicht_umgerechnet` | Prüft, dass der Druckbereich „wie Kartenfenster“ vom KBS der Kartenansicht in das Druck-KBS umgerechnet wird. | grün | +| `TestDruckKbs.test_auswahl_wird_vor_dem_puffern_vom_layer_kbs_umgerechnet` | Prüft, dass der Druckbereich „Auswahl“ aus dem KBS des Auswahl-Layers umgerechnet und erst danach gepuffert wird. | grün | +| `TestDruckKbs.test_ohne_druck_kbs_wird_nicht_umgerechnet` | Prüft, dass _resolve_extent ohne Druck-KBS wie bisher nicht umrechnet (Aufrufe ohne den neuen Parameter). | grün | +| `TestDruckKbs.test_druckrechteck_wird_ins_kbs_seines_layers_umgerechnet` | Prüft, dass ein in der Kartenansicht gezeichnetes Druckrechteck vor dem Speichern in das KBS des Druckrechteck-Layers umgerechnet wird. | grün | +| `TestLayoutkarteImDruckKbs.test_hauptkarte_bekommt_das_kbs_vor_der_ausdehnung` | Prüft, dass die Hauptkarte eines neuen Layouts das Druck-KBS erhält, bevor ihre Ausdehnung gesetzt wird. | grün | + ### `Plugin_SN_Basis/tests/test_excel_importer.py` Worum es in der Datei geht: Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63). @@ -1028,7 +1053,7 @@ Testgruppen: ## Paket `sn_verfahrensgebiet` -Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 208 Testfällen (166 grün, 34 rot, 8 übersprungen). +Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 210 Testfällen (168 grün, 34 rot, 8 übersprungen). ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` @@ -1361,6 +1386,7 @@ Testgruppen: - `TestWorkingTab` (24 Testfälle): — (Docstring fehlt) - `TestHandleVerfShape` (9 Testfälle): — (Docstring fehlt) +- `TestZoomImProjektKbs` (2 Testfälle): Zoom auf das neue Verfahrensgebiet rechnet in das KBS der Kartenansicht um (Basis #99). | Testfall | prüft | Status | | --- | --- | --- | @@ -1397,6 +1423,8 @@ Testgruppen: | `TestHandleVerfShape.test_bror_layer_erhaelt_qml_stil` | Wenn bror_layer zurückgegeben → BauRaumOderBodenordnungsrecht.qml angewendet. | grün | | `TestHandleVerfShape.test_enriched_layer_already_in_project_not_added_again` | Layer bereits im Projekt → kein erneutes addMapLayer für VG-Layer. | grün | | `TestHandleVerfShape.test_busy_state_set_during_execution` | Button wird während der Ausführung auf busy gesetzt. | grün | +| `TestZoomImProjektKbs.test_alkis_zoom_ueber_zoom_auf_layer` | Prüft, dass nach dem Laden aus ALKIS über zoom_auf_layer auf das Verfahrensgebiet gezoomt wird und nicht mit der unumgerechneten Layer-Ausdehnung. | grün | +| `TestZoomImProjektKbs.test_shape_zoom_ueber_zoom_auf_layer` | Prüft, dass nach dem Laden aus einem Shape über zoom_auf_layer auf das Verfahrensgebiet gezoomt wird und nicht mit der unumgerechneten Layer-Ausdehnung. | grün | ## Paket `sn_plan41` @@ -1513,11 +1541,11 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen, aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall, dann verschwindet er hier von selbst. -### `sn_basis` – 0 von 464 Testfällen ohne Beschreibung +### `sn_basis` – 0 von 475 Testfällen ohne Beschreibung Keine Lücken. -### `sn_verfahrensgebiet` – 102 von 208 Testfällen ohne Beschreibung +### `sn_verfahrensgebiet` – 102 von 210 Testfällen ohne Beschreibung #### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` (25 von 25) @@ -1645,7 +1673,7 @@ Keine Lücken. - `TestVerfahrensgebietShapeKomplett.test_punktort_ta_none_gibt_shape_vg_zurueck_und_zeigt_fehler` - `TestVerfahrensgebietShapeKomplett.test_temp_layer_wird_stets_entfernt` -#### `Plugin_SN_Verfahrensgebiet/tests/test_working_tab.py` (6 von 33) – auch der Modul-Docstring fehlt +#### `Plugin_SN_Verfahrensgebiet/tests/test_working_tab.py` (6 von 35) – auch der Modul-Docstring fehlt - `TestWorkingTab.test_set_busy_updates_button_state` - `TestWorkingTab.test_on_verf_layer_changed_valid_sets_variable` diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md index 09f5841..9c55bff 100644 --- a/docs/wissen/fallstricke.md +++ b/docs/wissen/fallstricke.md @@ -92,6 +92,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik ## Code-Konventionen, die man kennen muss +- **`layer.extent()` steht im KBS des Layers, nicht im Projekt-KBS.** Solange das Projekt in EPSG:25833 steht wie + alle sächsischen Daten, fällt das nicht auf. Mit einem anderen Projekt-KBS landen `canvas.setExtent(layer.extent())` + und `map_item.setExtent(layer.extent())` an der falschen Stelle; die Layer selbst rendert QGIS trotzdem richtig + (Umprojektion beim Zeichnen). Immer `zoom_auf_layer` bzw. `transformiere_rechteck` verwenden (Basis #99). + Layoutkarten ohne `setCrs` folgen dem Projekt-KBS; Gitter (500 Karteneinheiten) und Maßstab setzen Meter voraus. + (05.10.2026) - **Dock-Inhalt ohne Scroll-Bereich verlängert das QGIS-Fenster.** Qt verlangt für ein Dock mindestens die Höhe seines Inhalts (beim `QTabWidget`: des höchsten Tabs). Ist sie größer als der Bildschirm, wächst das Hauptfenster nach unten aus dem Bild, statt dass ein Scrollbalken erscheint. Abhilfe: Inhalt in `QScrollArea` mit `setWidgetResizable(True)`; From 1b926b1b5ce15e718f2923011831c66a170fce68 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 6 Oct 2026 07:41:23 +0200 Subject: [PATCH 13/20] Autobahn-Symbol der Basemap-Legende (Basis #98) dokumentiert Fallstrick zu Zoombereichen der symbol_stile im Legendenkatalog, Testkatalog mit den drei neuen Tests. Co-Authored-By: Claude Opus 5.5 --- docs/tests/testkatalog.html | 48 +++++++++++++++++++++---------------- docs/tests/testkatalog.md | 16 ++++++++----- docs/wissen/fallstricke.md | 7 ++++++ 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index 9bff7ed..caec034 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,20 +119,20 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
729Testfälle
-
686grün
+
732Testfälle
+
689grün
35rot (Baseline)
80%mit Docstring
60Testdateien
Verteilung je Paket
-
sn_basis43 Dateien · 475 Testfälle
+
sn_basis43 Dateien · 478 Testfälle
sn_verfahrensgebiet12 Dateien · 210 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 43 Testdateien · 475 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 43 Testdateien · 478 Testfälle

- @@ -338,7 +338,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 7
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestAutobahnSymbol 3TestHilfslayer 3TestPlatzierung 7
flowchart TD
   root(["test_basemap_legende_layout.py"])
   class root rootnode
   root --> g1["TestKennzeichenUndKarte (2)"]
@@ -349,20 +349,25 @@
   class g2 groupnode
   g2 --> t3("symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge")
   g2 --> t4("ohne_aktive_symbol_stile_erster_getroffener_style")
-  root --> g3["TestHilfslayer (3)"]
+  root --> g3["TestAutobahnSymbol (3)"]
   class g3 groupnode
-  g3 --> t5("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
-  g3 --> t6("alte_hilfslayer_werden_entfernt")
-  g3 --> t7("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
-  root --> g4["TestPlatzierung (7)"]
+  g3 --> t5("druckmassstab_ab_zoom_14_blauer_decker_auf_grauer_kontur")
+  g3 --> t6("zoom_11_bis_13_bisherige_kontur_und_decker")
+  g3 --> t7("unter_zoom_11_blauer_decker")
+  root --> g4["TestHilfslayer (3)"]
   class g4 groupnode
-  g4 --> t8("einspaltig_ueber_der_quellenangabe")
-  g4 --> t9("mehrspaltig_wenn_hoehe_nicht_reicht")
-  g4 --> t10("spalten_ungleich_breit")
-  g4 --> t11("mindestgroesse_mit_ungleichen_spalten_und_umbruch")
-  g4 --> t12("neue_basemap_legende_mindestens_so_breit_wie_kartenschild")
-  g4 --> t13("titel_zweizeilig")
-  g4 --> t14("nie_breiter_als_der_seitenrand_mit_hinweis")
+  g4 --> t8("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
+  g4 --> t9("alte_hilfslayer_werden_entfernt")
+  g4 --> t10("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
+  root --> g5["TestPlatzierung (7)"]
+  class g5 groupnode
+  g5 --> t11("einspaltig_ueber_der_quellenangabe")
+  g5 --> t12("mehrspaltig_wenn_hoehe_nicht_reicht")
+  g5 --> t13("spalten_ungleich_breit")
+  g5 --> t14("mindestgroesse_mit_ungleichen_spalten_und_umbruch")
+  g5 --> t15("neue_basemap_legende_mindestens_so_breit_wie_kartenschild")
+  g5 --> t16("titel_zweizeilig")
+  g5 --> t17("nie_breiter_als_der_seitenrand_mit_hinweis")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -377,11 +382,14 @@
   class t12 ok
   class t13 ok
   class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_spalten_ungleich_breit
Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96).grün
TestPlatzierung
test_mindestgroesse_mit_ungleichen_spalten_und_umbruch
Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96).grün
TestPlatzierung
test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild
Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt.grün
TestPlatzierung
test_titel_zweizeilig
Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96).grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestAutobahnSymbol
test_druckmassstab_ab_zoom_14_blauer_decker_auf_grauer_kontur
Prüft, dass das Autobahn-Symbol ab Zoom 14 aus grauer Kontur und blauem Decker der Fahrbahnachse besteht statt nur aus einem grauen Style.grün
TestAutobahnSymbol
test_zoom_11_bis_13_bisherige_kontur_und_decker
Prüft, dass das Autobahn-Symbol bei Zoom 11 bis 13 weiterhin aus Kontur und Decker der getrennten Fahrbahn besteht.grün
TestAutobahnSymbol
test_unter_zoom_11_blauer_decker
Prüft, dass das Autobahn-Symbol unter Zoom 11 den blauen Decker der Übersichtsdarstellung nutzt.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_spalten_ungleich_breit
Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96).grün
TestPlatzierung
test_mindestgroesse_mit_ungleichen_spalten_und_umbruch
Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96).grün
TestPlatzierung
test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild
Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt.grün
TestPlatzierung
test_titel_zweizeilig
Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96).grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
   root(["test_basemap_legende_rand.py"])
   class root rootnode
   root --> g1["TestBrauchtLegendenrand (4)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index ace6f90..25e8d8c 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 60 Testdateien, 729 Testfälle – 686 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 584 von 729 (80 %).
+Gesamt: 3 Pakete, 60 Testdateien, 732 Testfälle – 689 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 587 von 732 (80 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 43 | 475 | 475 | 0 | 0 | 475 von 475 (100 %) |
+| `sn_basis` | 43 | 478 | 478 | 0 | 0 | 478 von 478 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 210 | 168 | 34 | 8 | 108 von 210 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **60** | **729** | **686** | **35** | **8** | **584 von 729 (80 %)** |
+| **Summe** | **60** | **732** | **689** | **35** | **8** | **587 von 732 (80 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 584 von 729 (80 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 43 Testdateien mit 475 Testfällen (475 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 43 Testdateien mit 478 Testfällen (478 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -166,6 +166,7 @@ Testgruppen:
 
 - `TestKennzeichenUndKarte` (2 Testfälle): — (Docstring fehlt)
 - `TestSymbolwahl` (2 Testfälle): — (Docstring fehlt)
+- `TestAutobahnSymbol` (3 Testfälle): Autobahn-Symbol aus dem ausgelieferten Katalog (Basis #98).
 - `TestHilfslayer` (3 Testfälle): — (Docstring fehlt)
 - `TestPlatzierung` (7 Testfälle): — (Docstring fehlt)
 
@@ -175,6 +176,9 @@ Testgruppen:
 | `TestKennzeichenUndKarte.test_hauptkarte_ueber_id` | Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird. | grün |
 | `TestSymbolwahl.test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge` | Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden. | grün |
 | `TestSymbolwahl.test_ohne_aktive_symbol_stile_erster_getroffener_style` | Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert. | grün |
+| `TestAutobahnSymbol.test_druckmassstab_ab_zoom_14_blauer_decker_auf_grauer_kontur` | Prüft, dass das Autobahn-Symbol ab Zoom 14 aus grauer Kontur und blauem Decker der Fahrbahnachse besteht statt nur aus einem grauen Style. | grün |
+| `TestAutobahnSymbol.test_zoom_11_bis_13_bisherige_kontur_und_decker` | Prüft, dass das Autobahn-Symbol bei Zoom 11 bis 13 weiterhin aus Kontur und Decker der getrennten Fahrbahn besteht. | grün |
+| `TestAutobahnSymbol.test_unter_zoom_11_blauer_decker` | Prüft, dass das Autobahn-Symbol unter Zoom 11 den blauen Decker der Übersichtsdarstellung nutzt. | grün |
 | `TestHilfslayer.test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer` | Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen. | grün |
 | `TestHilfslayer.test_alte_hilfslayer_werden_entfernt` | Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden. | grün |
 | `TestHilfslayer.test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt` | Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird. | grün |
@@ -1541,7 +1545,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 475 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 478 Testfällen ohne Beschreibung
 
 Keine Lücken.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 9c55bff..4a805e0 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -68,6 +68,13 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
   Der Legendenkatalog `sn_basis/data/basemap_legende.json` gehört zur Style-Version `bm_web_col` v2; nach einer
   neuen Version `python modules/basemap_katalog.py` laufen lassen und Meldungen prüfen. Eine QML-Stilvorlage des
   Layers ist rund 3,7 MB groß. (02.10.2026)
+- **`symbol_stile` im Basemap-Legendenkatalog müssen alle Zoomstufen abdecken (Basis #98).** Viele basemap.de-Styles
+  gelten nur in einem Zoombereich (z. B. `*_Autobahn_getrennt` bis Zoom 13, ab Zoom 14
+  `*_Autobahn_getrennt_Fahrbahnachse`). Ist bei der Druck-Zoomstufe kein `symbol_stil` aktiv, nimmt
+  `waehle_symbolnamen` den ersten getroffenen Style – oft nur die graue Kontur. Deshalb je Zoombereich ein
+  Kontur-/Decker-Paar eintragen; inaktive werden übergangen. Lücken findet ein Abgleich der `minzoom`/`maxzoom`
+  der Style-JSON mit dem Katalog. Weitere Lücken im Druckbereich (Stand 06.10.2026): „Straße im Tunnel“
+  ab Zoom 14, „Biosphärenreservat“ ab Zoom 11. (06.10.2026)
 - **basemap.de-Themen (Basis #100):** Angeboten werden Farbe (`bm_web_col`), Grau (`bm_web_gry`) und Relief
   (`bm_web_top`); `bm_web_bin` liegt auf dem Server, steht aber nicht auf basemap.de. Farbe und Grau nutzen nur die
   Kachelquelle `bm_web_de_3857`. **Relief braucht zusätzlich** die Höhenlinien-Kacheln `bm_web_hl_de_3857` und zwei

From 8d317d33d32d73e9267ddb1f19db5a5ebdc31036 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Tue, 6 Oct 2026 08:19:18 +0200
Subject: [PATCH 14/20] =?UTF-8?q?Pr=C3=BCfung=20basemap.de=20P10=20Vektor?=
 =?UTF-8?q?=20(Basis=20#102)=20dokumentiert?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Befunde zu KBS, eingebettetem QGIS-Projekt, Farbe/Grau über
Projektvariable und Projektfarben, Leistung beim Lesen aus dem Netz
und Beta-Lizenz in fallstricke.md; STATUS nachgezogen.

Co-Authored-By: Claude Opus 5.5 
---
 docs/status/STATUS.md      |  3 ++-
 docs/wissen/fallstricke.md | 14 +++++++++++---
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md
index 7b60c6e..4ced57a 100644
--- a/docs/status/STATUS.md
+++ b/docs/status/STATUS.md
@@ -74,7 +74,8 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
 17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Gemergt (PR #103, Doku PR #25).
     Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test
     am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101
-    (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
+    (Import/Export der Stile), P10 Vektor (#102: technisch geprüft 06.10.2026, fachliche Entscheidung offen –
+    Beta-Lizenz nur zu Testzwecken, siehe `fallstricke.md`), Relief.
 18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Gemergt (PR #105, Doku PR #26): jeder Tab im
     Scroll-Bereich über `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet.
 19. **Zoom und Drucklayout bei anderem Projekt-KBS (Basis #99).** Behoben auf `Plugin_SN_Basis:bugfix/99-druck-kbs`
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 4a805e0..6be7897 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -85,10 +85,18 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
   und vor dem Themenwechsel verglichen (`basemap_themen.aenderungsstand`). Die Berechnung schreibt die rund 3,7 MB
   große QML in eine temporäre Datei. Dieselbe Darstellung ergibt in QGIS dieselbe QML, der Prüfwert ist also stabil
   (manueller QGIS-Test 05.10.2026). Auch „Punktlinien korrigieren“ zählt als Änderung. (05.10.2026)
-- **basemap.de P10 Vektor ist kein Kacheldienst.** Es ist ein bundesweites GeoPackage (rund 60 GB, 47 normale
-  Vektorlayer, Varianten col/gry, Beta vom 14.08.2026, optimiert für QGIS 3.44.8). Die Basemap-Werkzeuge in `sn_basis`
+- **basemap.de P10 Vektor ist kein Kacheldienst (Basis #102).** Es ist ein bundesweites GeoPackage (60,3 GB, 40
+  Tabellen, **EPSG:25832**, Beta vom 14.08.2026, optimiert für QGIS 3.44.8). Die Basemap-Werkzeuge in `sn_basis`
   (Erkennung, `selectByGeometry`, Legendenkatalog nach Style-IDs, Stilvorlagen über `renderer().styles()`) setzen
-  Vektorkacheln voraus und greifen dort nicht. Eigenes Issue. (05.10.2026)
+  Vektorkacheln voraus und greifen dort nicht; die QGIS-Legende funktioniert dagegen selbst. Weitere Befunde der
+  Prüfung vom 06.10.2026:
+  - Stile gibt es nicht als `layer_styles`, sondern als ein in `qgis_projects` eingebettetes QGIS-Projekt (29 Layer).
+    Farbe/Grau steuert die Projektvariable `@project_theme` (`'gry'`) zusammen mit 73 benannten Projektfarben
+    (`project_color_object('Seeblau'|'Seeblau_gry')`). Wer nur die Layerstile übernimmt, verliert die Farben.
+  - Direktes Lesen aus dem Netz (`/vsicurl/`) ist zu langsam: Die Objekte liegen in der Datei nicht räumlich sortiert,
+    3 × 3 km und 5 Layer kosteten rund 3 900 Teilabrufe (etwa 8 min); nach einigen tausend Abrufen trennt der Server
+    die Verbindung. Nur nach vollständigem Download zuschneiden; `ogr2ogr` kopiert `qgis_projects` voraussichtlich nicht mit.
+  - Die Beta-Lizenz erlaubt die Nutzung nur „zu Testzwecken“ (Nr. 2.2). (06.10.2026)
 - **WFS-Beschränkungen der sächsischen ALKIS-Dienste:** POST/GML-Filter werden blockiert, URL-Länge ist begrenzt, serverseitige Feature-Limits. Deshalb arbeiten Flurstücks- und Grenzpunktabrufe mit BBOX, gemarkungsweisen Batches und Nutzerdialogen. Nicht „vereinfachen“, ohne das gegen den Dienst zu testen. Vollständig mit erprobten Anfragen: `docs/wissen/wfs_dienste.md`, Objektarten/Attribute: `docs/wissen/alkis_objektarten.md`. (Verfahrensgebiet, Code)
 - **Zwei Linklisten.** Zentrale `Linkliste/linkliste.csv` (Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) und die vom Plugin genutzte `Plugin_SN_Plan41/assets/Linkliste.xlsx` (`ident, gruppe, kartenebene, inhalt, link, provider, stildatei`) sind nicht verbunden. Änderungen an Diensten in **beiden** nachziehen, bis die Zusammenführung entschieden ist. (09/2026)
 - **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)

From 1d3c98be11d33fa937dfc3b0e51a25df918a21e5 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Tue, 6 Oct 2026 09:35:46 +0200
Subject: [PATCH 15/20] Export und Import der Basemap-Stilvorlagen (Basis #101)
 dokumentiert

Konzept 10 Abschnitt 5.3, Austauschformat in schnittstellen.md,
Modulkarte und Testkatalog mit den neuen Tests.

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md     |  4 +--
 docs/architektur/schnittstellen.md |  3 +-
 docs/konzept/10_basemap_legende.md | 12 ++++++-
 docs/tests/testkatalog.html        | 52 +++++++++++++++++++++++-------
 docs/tests/testkatalog.md          | 30 ++++++++++++-----
 5 files changed, 77 insertions(+), 24 deletions(-)

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 6633fbe..0942af2 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -18,7 +18,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `ui/base_dockwidget.py` | 172 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, jeder Tab in einem Scroll-Bereich (`setWidget`, #104), ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück |
 | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) |
 | `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` |
-| `ui/widgets/basemap_bereich.py` | 433 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen |
+| `ui/widgets/basemap_bereich.py` | 503 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen inkl. Export/Import (#101) |
 
 ### functions/ – Wrapper-Schicht (Qt/QGIS/OS mit Mock-Fallback)
 
@@ -65,7 +65,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `modules/basemap_legende_layout.py` | 471 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren |
 | `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) |
 | `modules/basemap_stilkorrektur.py` | 99 | Darstellungsfehler des Basemap-Layers korrigieren: Punktlinien mit Strichlänge 0 sichtbar machen (#90) |
-| `modules/basemap_stilvorlagen.py` | 188 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil, mit Thema), Standard in `QSettings` |
+| `modules/basemap_stilvorlagen.py` | 313 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil, mit Thema), Standard in `QSettings`; Export als ZIP, Import von ZIP oder QML, Themenprüfung beim Laden (#101) |
 | `modules/basemap_themen.py` | 245 | Themen des Basemap-Layers (Farbe, Grau): Layer anlegen, an der Quell-URL erkennen, Thema wechseln, Prüfwert der Darstellung für ungespeicherte Änderungen (#100) |
 
 ### Sonstiges
diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md
index b9a2c49..a376256 100644
--- a/docs/architektur/schnittstellen.md
+++ b/docs/architektur/schnittstellen.md
@@ -166,7 +166,8 @@ flowchart LR
   `python modules/basemap_katalog.py`; gehört zur Style-Version `bm_web_col` v2.
 - **Stilvorlagen:** lokal unter `/sn_basis/basemap_stile/`, Standard in `QSettings`
   `sn_basis/basemap_standardstil`. Die Begleit-JSON trägt seit #100 das Feld `thema` (Schlüssel aus
-  `basemap_themen.THEMEN`; fehlt bei älteren Vorlagen).
+  `basemap_themen.THEMEN`; fehlt bei älteren Vorlagen). Austauschformat seit #101: ZIP mit genau einer
+  `.qml` und optional `.json`; der Import nimmt auch eine reine QML-Datei an.
 - **Thema und Stil (Basis #100):** *Thema* = vom Dienst als Style-JSON gelieferte Darstellung (`farbe` →
   `bm_web_col.json`, `grau` → `bm_web_gry.json`); *Stil* = lokale Anpassung (Stilvorlage). Alle Themen nutzen
   dieselbe Kachel-URL `…/tiles/v2/bm_web_de_3857/{z}/{x}/{y}.pbf`; das Thema steht im Quellparameter `styleUrl`.
diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md
index 14ab406..d233514 100644
--- a/docs/konzept/10_basemap_legende.md
+++ b/docs/konzept/10_basemap_legende.md
@@ -142,9 +142,19 @@ Ohne Basemap-Layer im Projekt ist der Bereich deaktiviert und zeigt „Kein Base
 - **Laden:** Rückfrage vor dem Überschreiben der aktuellen Darstellung. Danach Prüfung, ob die `styleName`s der
   Vorlage zum Layer passen (Farbe vs. Grau, neue Basemap-Version); bei Abweichung Hinweis mit Anzahl unbekannter
   Styles. Laden geschieht **nur über den Knopf**, nicht automatisch beim Öffnen eines Projekts.
+  Seit #101 zusätzlich: Passt das `thema` der Vorlage (Farbe/Grau) nicht zum Thema des Layers, folgt eine
+  Rückfrage. Ohne Thema in Vorlage oder Layer (ältere oder als QML importierte Vorlage) gibt es keine Rückfrage.
 - **Speichern unter …:** Name abfragen, vorhandene Variante nur nach Rückfrage überschreiben.
+- **Exportieren … / Importieren … (#101):** Export schreibt die gewählte Variante als **eine ZIP-Datei**
+  (`.qml` + `.json`) zum Weitergeben per Mail oder Netzlaufwerk. Import nimmt eine solche ZIP-Datei
+  oder eine **reine QML-Datei** an (z. B. aus „Stil speichern“ in QGIS), schlägt den Anzeigenamen aus der
+  Begleitdatei bzw. dem Dateinamen vor und überschreibt eine gleichnamige Variante nur nach Rückfrage. Aus der
+  fremden Begleitdatei werden nur `erstellt`, `layer`, `stile`, `thema` und `legende_auswahl` übernommen, ergänzt
+  um `importiert` und `importiert_aus`; der Standard des anderen Rechners nicht. Geprüft wird, dass die QML eine
+  QGIS-Stildatei ist (`Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
732Testfälle
-
689grün
+
744Testfälle
+
701grün
35rot (Baseline)
-
80%mit Docstring
+
81%mit Docstring
60Testdateien
Verteilung je Paket
-
sn_basis43 Dateien · 478 Testfälle
+
sn_basis43 Dateien · 490 Testfälle
sn_verfahrensgebiet12 Dateien · 210 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,9 +147,9 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 43 Testdateien · 478 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 43 Testdateien · 490 Testfälle

- @@ -184,7 +184,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün
ScrollbereichTests
test_jeder_tab_liegt_in_einem_scrollbereich
Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern.grün
ScrollbereichTests
test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich
Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt.grün
ScrollbereichTests
test_scrollbereich_wird_nicht_doppelt_angelegt
Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird.grün
ScrollbereichTests
test_setwidget_none_und_andere_widgets_bleiben_unveraendert
Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden.grün
ScrollbereichTests
test_cleanup_erreicht_tabs_im_scrollbereich
Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 10
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün
ScrollbereichTests
test_jeder_tab_liegt_in_einem_scrollbereich
Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern.grün
ScrollbereichTests
test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich
Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt.grün
ScrollbereichTests
test_scrollbereich_wird_nicht_doppelt_angelegt
Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird.grün
ScrollbereichTests
test_setwidget_none_und_andere_widgets_bleiben_unveraendert
Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden.grün
ScrollbereichTests
test_cleanup_erreicht_tabs_im_scrollbereich
Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 11TestStilvorlagenAustausch 5
flowchart TD
   root(["test_basemap_anbindung.py"])
   class root rootnode
   root --> g1["TestMehrfachauswahl (1)"]
@@ -196,7 +196,7 @@
   g2 --> t3("layout_wird_markiert_und_legende_erzeugt")
   g2 --> t4("fehler_der_basemap_legende_verhindert_die_vorlage_nicht")
   g2 --> t5("druck_tab_aktualisiert_basemap_bereich_falls_vorhanden")
-  root --> g3["TestBasemapBereich (10)"]
+  root --> g3["TestBasemapBereich (11)"]
   class g3 groupnode
   g3 --> t6("auswertebereich_wird_gespeichert")
   g3 --> t7("aktualisieren_ohne_vorlage_warnt")
@@ -208,6 +208,14 @@
   g3 --> t13("punktlinien_korrigieren_meldet_anzahl")
   g3 --> t14("punktlinien_ohne_basemap_layer_warnt")
   g3 --> t15("stil_speichern_ohne_basemap_layer_warnt")
+  g3 --> t16("stil_laden_bricht_bei_fremdem_thema_ab")
+  root --> g4["TestStilvorlagenAustausch (5)"]
+  class g4 groupnode
+  g4 --> t17("export_schreibt_gewaehlte_vorlage")
+  g4 --> t18("export_abgebrochen_oder_ohne_vorlage")
+  g4 --> t19("import_uebernimmt_datei_unter_gewaehltem_namen")
+  g4 --> t20("import_ersetzt_vorhandene_nur_nach_rueckfrage")
+  g4 --> t21("import_ungeeigneter_datei_warnt")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -223,11 +231,17 @@
   class t13 ok
   class t14 ok
   class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grün
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grün
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grün
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grün
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grün
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grün
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grün
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grün
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grün
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grün
TestBasemapBereich
test_punktlinien_korrigieren_meldet_anzahl
Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet.grün
TestBasemapBereich
test_punktlinien_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird.grün
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grün

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grün
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grün
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grün
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grün
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grün
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grün
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grün
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grün
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grün
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grün
TestBasemapBereich
test_punktlinien_korrigieren_meldet_anzahl
Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet.grün
TestBasemapBereich
test_punktlinien_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird.grün
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremdem_thema_ab
Prüft, dass eine Vorlage eines anderen Themas (Grau auf Farbe) nur nach Bestätigung geladen wird.grün
TestStilvorlagenAustausch
test_export_schreibt_gewaehlte_vorlage
Prüft, dass „Exportieren …“ die gewählte Vorlage in die im Dateidialog gewählte Datei schreibt und den Pfad meldet.grün
TestStilvorlagenAustausch
test_export_abgebrochen_oder_ohne_vorlage
Prüft, dass ohne gewählte Vorlage oder bei abgebrochenem Dateidialog nichts exportiert wird.grün
TestStilvorlagenAustausch
test_import_uebernimmt_datei_unter_gewaehltem_namen
Prüft, dass „Importieren …“ den Namen aus der Datei vorschlägt, unter dem gewählten Namen importiert und die neue Vorlage auswählt.grün
TestStilvorlagenAustausch
test_import_ersetzt_vorhandene_nur_nach_rueckfrage
Prüft, dass ein Import eine gleichnamige Vorlage ohne Bestätigung nicht überschreibt.grün
TestStilvorlagenAustausch
test_import_ungeeigneter_datei_warnt
Prüft, dass eine ungeeignete Importdatei gemeldet wird, ohne nach einem Namen zu fragen oder etwas zu importieren.grün

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
   root(["test_basemap_kacheln.py"])
   class root rootnode
   root --> g1["TestSammleObjekte (4)"]
@@ -435,7 +449,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMusterUndAusdruck
test_strichlaenge_null_im_festen_muster
Prüft, dass Strichlängen 0 (gerade Positionen) im Strichmuster ersetzt und Lücken der Länge 0 nicht verändert werden.grün
TestMusterUndAusdruck
test_strichlaenge_null_im_zoomabhaengigen_ausdruck
Prüft, dass jede Strichlänge 0 in den array(...)-Mustern des Konverter-Ausdrucks ersetzt wird und der übrige Ausdruck gleich bleibt.grün
TestLayerkorrektur
test_punktlinien_werden_korrigiert_andere_styles_nicht
Prüft, dass nur Styles mit Strichlänge 0 korrigiert werden – Muster und Ausdruck – und die Anzahl zurückkommt.grün
TestLayerkorrektur
test_zweiter_lauf_korrigiert_nichts
Prüft, dass ein zweiter Lauf keine Styles mehr korrigiert und nicht neu zeichnen lässt.grün
TestLayerkorrektur
test_nur_ausdruck_ohne_festes_muster
Prüft, dass auch ein Style korrigiert wird, dessen Strichlänge 0 nur im zoomabhängigen Ausdruck steht.grün
TestLayerkorrektur
test_layer_ohne_renderer
Prüft, dass ein Layer ohne Renderer 0 korrigierte Styles liefert.grün

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).

TestStilvorlagen 8
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMusterUndAusdruck
test_strichlaenge_null_im_festen_muster
Prüft, dass Strichlängen 0 (gerade Positionen) im Strichmuster ersetzt und Lücken der Länge 0 nicht verändert werden.grün
TestMusterUndAusdruck
test_strichlaenge_null_im_zoomabhaengigen_ausdruck
Prüft, dass jede Strichlänge 0 in den array(...)-Mustern des Konverter-Ausdrucks ersetzt wird und der übrige Ausdruck gleich bleibt.grün
TestLayerkorrektur
test_punktlinien_werden_korrigiert_andere_styles_nicht
Prüft, dass nur Styles mit Strichlänge 0 korrigiert werden – Muster und Ausdruck – und die Anzahl zurückkommt.grün
TestLayerkorrektur
test_zweiter_lauf_korrigiert_nichts
Prüft, dass ein zweiter Lauf keine Styles mehr korrigiert und nicht neu zeichnen lässt.grün
TestLayerkorrektur
test_nur_ausdruck_ohne_festes_muster
Prüft, dass auch ein Style korrigiert wird, dessen Strichlänge 0 nur im zoomabhängigen Ausdruck steht.grün
TestLayerkorrektur
test_layer_ohne_renderer
Prüft, dass ein Layer ohne Renderer 0 korrigierte Styles liefert.grün

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88),

TestStilvorlagen 8TestExportImport 6
flowchart TD
   root(["test_basemap_stilvorlagen.py"])
   class root rootnode
   root --> g1["TestStilvorlagen (8)"]
@@ -448,6 +462,14 @@
   g1 --> t6("standard_setzen_und_beim_loeschen_entfernen")
   g1 --> t7("fehlende_begleitdatei_wird_toleriert")
   g1 --> t8("ohne_ordner_leere_liste")
+  root --> g2["TestExportImport (6)"]
+  class g2 groupnode
+  g2 --> t9("export_und_import_uebertragen_vorlage_mit_begleitdaten")
+  g2 --> t10("export_einer_fehlenden_vorlage")
+  g2 --> t11("import_einer_reinen_qml_datei")
+  g2 --> t12("import_lehnt_ungeeignete_dateien_ab")
+  g2 --> t13("import_uebernimmt_nur_bekannte_begleitdaten")
+  g2 --> t14("thema_der_vorlage_passt_nicht_zum_layer")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -456,11 +478,17 @@
   class t6 ok
   class t7 ok
   class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grün
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grün
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grün
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grün
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grün
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grün
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grün
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grün

Tests für modules/basemap_themen.py und den Abschnitt „Basemap-Layer“ im Basemap-Bereich (Basis #100):

TestQuelleUndThema 4TestPruefwert 3TestLayerUndThemenwechsel 7TestStilvorlageMitThema 1TestAuswahlDialog 1TestBasemapHinzufuegen 5TestThemaWechseln 8TestZielLayer 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grün
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grün
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grün
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grün
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grün
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grün
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grün
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grün
TestExportImport
test_export_und_import_uebertragen_vorlage_mit_begleitdaten
Prüft, dass eine exportierte Vorlage auf einem anderen Rechner mit Anzeigename, Thema, Style-Namen und Legendenauswahl ankommt.grün
TestExportImport
test_export_einer_fehlenden_vorlage
Prüft, dass der Export einer nicht vorhandenen Vorlage als ValueError gemeldet wird und keine Datei entsteht.grün
TestExportImport
test_import_einer_reinen_qml_datei
Prüft, dass eine QML-Datei ohne Begleitdatei mit dem Dateinamen als Namensvorschlag und ohne Thema übernommen wird.grün
TestExportImport
test_import_lehnt_ungeeignete_dateien_ab
Prüft, dass Dateien ohne QGIS-Stil und ZIP-Dateien ohne genau eine QML-Datei abgelehnt werden, ohne etwas anzulegen.grün
TestExportImport
test_import_uebernimmt_nur_bekannte_begleitdaten
Prüft, dass ein Import unbekannte oder falsch typisierte Angaben der Begleitdatei verwirft und den gewählten Namen nutzt.grün
TestExportImport
test_thema_der_vorlage_passt_nicht_zum_layer
Prüft, dass ein abweichendes Thema der Vorlage gemeldet wird und ohne Thema in Vorlage oder Layer keine Meldung kommt.grün

Tests für modules/basemap_themen.py und den Abschnitt „Basemap-Layer“ im Basemap-Bereich (Basis #100):

TestQuelleUndThema 4TestPruefwert 3TestLayerUndThemenwechsel 7TestStilvorlageMitThema 1TestAuswahlDialog 1TestBasemapHinzufuegen 5TestThemaWechseln 8TestZielLayer 1
flowchart TD
   root(["test_basemap_themen.py"])
   class root rootnode
   root --> g1["TestQuelleUndThema (4)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 25e8d8c..3174fa7 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 60 Testdateien, 732 Testfälle – 689 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 587 von 732 (80 %).
+Gesamt: 3 Pakete, 60 Testdateien, 744 Testfälle – 701 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 599 von 744 (81 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 43 | 478 | 478 | 0 | 0 | 478 von 478 (100 %) |
+| `sn_basis` | 43 | 490 | 490 | 0 | 0 | 490 von 490 (100 %) |
 | `sn_verfahrensgebiet` | 12 | 210 | 168 | 34 | 8 | 108 von 210 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **60** | **732** | **689** | **35** | **8** | **587 von 732 (80 %)** |
+| **Summe** | **60** | **744** | **701** | **35** | **8** | **599 von 744 (81 %)** |
 
 ### Legende
 
@@ -31,7 +31,7 @@ Mit Beschreibung (Docstring): 587 von 732 (80 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 43 Testdateien mit 478 Testfällen (478 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 43 Testdateien mit 490 Testfällen (490 grün, 0 rot, 0 übersprungen).
 
 ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
 
@@ -63,7 +63,8 @@ Testgruppen:
 
 - `TestMehrfachauswahl` (1 Testfälle): — (Docstring fehlt)
 - `TestDruckanbindung` (4 Testfälle): — (Docstring fehlt)
-- `TestBasemapBereich` (10 Testfälle): — (Docstring fehlt)
+- `TestBasemapBereich` (11 Testfälle): — (Docstring fehlt)
+- `TestStilvorlagenAustausch` (5 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -82,6 +83,12 @@ Testgruppen:
 | `TestBasemapBereich.test_punktlinien_korrigieren_meldet_anzahl` | Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet. | grün |
 | `TestBasemapBereich.test_punktlinien_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird. | grün |
 | `TestBasemapBereich.test_stil_speichern_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird. | grün |
+| `TestBasemapBereich.test_stil_laden_bricht_bei_fremdem_thema_ab` | Prüft, dass eine Vorlage eines anderen Themas (Grau auf Farbe) nur nach Bestätigung geladen wird. | grün |
+| `TestStilvorlagenAustausch.test_export_schreibt_gewaehlte_vorlage` | Prüft, dass „Exportieren …“ die gewählte Vorlage in die im Dateidialog gewählte Datei schreibt und den Pfad meldet. | grün |
+| `TestStilvorlagenAustausch.test_export_abgebrochen_oder_ohne_vorlage` | Prüft, dass ohne gewählte Vorlage oder bei abgebrochenem Dateidialog nichts exportiert wird. | grün |
+| `TestStilvorlagenAustausch.test_import_uebernimmt_datei_unter_gewaehltem_namen` | Prüft, dass „Importieren …“ den Namen aus der Datei vorschlägt, unter dem gewählten Namen importiert und die neue Vorlage auswählt. | grün |
+| `TestStilvorlagenAustausch.test_import_ersetzt_vorhandene_nur_nach_rueckfrage` | Prüft, dass ein Import eine gleichnamige Vorlage ohne Bestätigung nicht überschreibt. | grün |
+| `TestStilvorlagenAustausch.test_import_ungeeigneter_datei_warnt` | Prüft, dass eine ungeeignete Importdatei gemeldet wird, ohne nach einem Namen zu fragen oder etwas zu importieren. | grün |
 
 ### `Plugin_SN_Basis/tests/test_basemap_kacheln.py`
 
@@ -228,11 +235,12 @@ Testgruppen:
 
 ### `Plugin_SN_Basis/tests/test_basemap_stilvorlagen.py`
 
-Worum es in der Datei geht: Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).
+Worum es in der Datei geht: Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88),
 
 Testgruppen:
 
 - `TestStilvorlagen` (8 Testfälle): — (Docstring fehlt)
+- `TestExportImport` (6 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -244,6 +252,12 @@ Testgruppen:
 | `TestStilvorlagen.test_standard_setzen_und_beim_loeschen_entfernen` | Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt. | grün |
 | `TestStilvorlagen.test_fehlende_begleitdatei_wird_toleriert` | Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird. | grün |
 | `TestStilvorlagen.test_ohne_ordner_leere_liste` | Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt. | grün |
+| `TestExportImport.test_export_und_import_uebertragen_vorlage_mit_begleitdaten` | Prüft, dass eine exportierte Vorlage auf einem anderen Rechner mit Anzeigename, Thema, Style-Namen und Legendenauswahl ankommt. | grün |
+| `TestExportImport.test_export_einer_fehlenden_vorlage` | Prüft, dass der Export einer nicht vorhandenen Vorlage als ValueError gemeldet wird und keine Datei entsteht. | grün |
+| `TestExportImport.test_import_einer_reinen_qml_datei` | Prüft, dass eine QML-Datei ohne Begleitdatei mit dem Dateinamen als Namensvorschlag und ohne Thema übernommen wird. | grün |
+| `TestExportImport.test_import_lehnt_ungeeignete_dateien_ab` | Prüft, dass Dateien ohne QGIS-Stil und ZIP-Dateien ohne genau eine QML-Datei abgelehnt werden, ohne etwas anzulegen. | grün |
+| `TestExportImport.test_import_uebernimmt_nur_bekannte_begleitdaten` | Prüft, dass ein Import unbekannte oder falsch typisierte Angaben der Begleitdatei verwirft und den gewählten Namen nutzt. | grün |
+| `TestExportImport.test_thema_der_vorlage_passt_nicht_zum_layer` | Prüft, dass ein abweichendes Thema der Vorlage gemeldet wird und ohne Thema in Vorlage oder Layer keine Meldung kommt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_basemap_themen.py`
 
@@ -1545,7 +1559,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 0 von 478 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 490 Testfällen ohne Beschreibung
 
 Keine Lücken.
 

From 7b0b7ea6c44c9048d899d37988a55fe69b70fbf6 Mon Sep 17 00:00:00 2001
From: Daniel 
Date: Tue, 6 Oct 2026 12:44:55 +0200
Subject: [PATCH 16/20] =?UTF-8?q?Flurst=C3=BCckssuche=20im=20Tab=20?=
 =?UTF-8?q?=E2=80=9EEigent=C3=BCmer=E2=80=9C=20(Verfahrensgebiet=20#56)=20?=
 =?UTF-8?q?dokumentiert?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Testkonzept docs/tests/verfahrensgebiet-flurstueckssuche.md, Aufbau des
Flurstückskennzeichens und fehlende Eigentümerangaben im öffentlichen
ALKIS-WFS in alkis_objektarten.md, Modulkarte und Testkatalog.

Co-Authored-By: Claude Opus 5.5 
---
 docs/architektur/modulkarte.md                |  6 +-
 docs/tests/testkatalog.html                   | 78 ++++++++++++++---
 docs/tests/testkatalog.md                     | 54 ++++++++++--
 .../verfahrensgebiet-flurstueckssuche.md      | 84 +++++++++++++++++++
 docs/wissen/alkis_objektarten.md              |  8 ++
 5 files changed, 212 insertions(+), 18 deletions(-)
 create mode 100644 docs/tests/verfahrensgebiet-flurstueckssuche.md

diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md
index 0942af2..c0e2275 100644
--- a/docs/architektur/modulkarte.md
+++ b/docs/architektur/modulkarte.md
@@ -84,8 +84,9 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | Datei | Zeilen | Zweck |
 |---|---:|---|
 | `main.py` | 80 | `Verfahrensgebiet`: registriert Action (Priorität 10), erzeugt `DockWidget` mit `Pruefmanager` und `DataGrabber` |
-| `ui/dockwidget.py` | 60 | Tabs `WorkingTab`, `PrintTab`, `SettingsTab`; injiziert Services per `set_services()` |
+| `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 |
 | `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 |
@@ -93,12 +94,13 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
 | `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/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/` | 12 Dateien | Mock-Modus, aktuell 157/201 grün (Baseline) |
+| `tests/` | 14 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 |
 
diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html
index 7cb665a..d12f21b 100644
--- a/docs/tests/testkatalog.html
+++ b/docs/tests/testkatalog.html
@@ -119,21 +119,21 @@
 Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
744Testfälle
-
701grün
+
762Testfälle
+
719grün
35rot (Baseline)
81%mit Docstring
-
60Testdateien
+
62Testdateien
Verteilung je Paket
sn_basis43 Dateien · 490 Testfälle
-
sn_verfahrensgebiet12 Dateien · 210 Testfälle
+
sn_verfahrensgebiet14 Dateien · 228 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -1675,10 +1675,10 @@

sn_verfahrensgebiet

-

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 12 Testdateien · 210 Testfälle

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 14 Testdateien · 228 Testfälle

-

Unit-Tests für `sn_verfahrensgebiet.functions.beteiligung_pruefung`.

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
@@ -1765,7 +1765,30 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für den Tab „Eigentümer“ – Flurstückssuche (#56).

TestFlurstueckSuchen 5TestDockWidgetTabs 1
flowchart TD
+  root(["test_eigentuemer_tab.py"])
+  class root rootnode
+  root --> g1["TestFlurstueckSuchen (5)"]
+  class g1 groupnode
+  g1 --> t1("ohne_flurstueckslayer_erscheint_hinweisdialog")
+  g1 --> t2("eingabefehler_als_warnung")
+  g1 --> t3("kein_treffer")
+  g1 --> t4("treffer_werden_ausgewaehlt_und_angezoomt")
+  g1 --> t5("ohne_kartenansicht_bleibt_auswahl")
+  root --> g2["TestDockWidgetTabs (1)"]
+  class g2 groupnode
+  g2 --> t6("tab_ist_im_dockwidget_eingetragen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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
TestDockWidgetTabs
test_tab_ist_im_dockwidget_eingetragen
Prüft, dass der Tab „Eigentümer“ im DockWidget direkt nach dem Bearbeitungs-Tab steht.grün

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
   root --> g1["TestFlurstueckAlkis (11)"]
@@ -1804,7 +1827,42 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird.rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird.rot
TestFlurstueckAlkis
test_existing_layer_abbrechen_returns_none
Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird.grün
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird.rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird.rot
TestFlurstueckAlkis
test_existing_layer_abbrechen_returns_none
Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird.grün
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Tests für die Flurstückssuche im Layer „Flurstücke“ (#56).

TestEingabe 6TestSuche 6
flowchart TD
+  root(["test_flurstueck_suche.py"])
+  class root rootnode
+  root --> g1["TestEingabe (6)"]
+  class g1 groupnode
+  g1 --> t1("flurstuecksnummer_mit_und_ohne_nenner")
+  g1 --> t2("ungueltige_flurstuecksnummer")
+  g1 --> t3("kennzeichen_zerlegen")
+  g1 --> t4("gemarkungsnummer_aus_ziffern")
+  g1 --> t5("gemarkungsname_ueber_gemarkungslayer")
+  g1 --> t6("gemarkungsname_ohne_layer_oder_unbekannt")
+  root --> g2["TestSuche (6)"]
+  class g2 groupnode
+  g2 --> t7("mit_nenner_genau_ein_treffer")
+  g2 --> t8("ohne_nenner_alle_mit_diesem_zaehler")
+  g2 --> t9("kein_treffer")
+  g2 --> t10("layer_ohne_kennzeichenfeld")
+  g2 --> t11("fehlender_flurstueckslayer_bricht_ab")
+  g2 --> t12("suche_im_projekt_mit_gemarkungsname")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestEingabe
test_flurstuecksnummer_mit_und_ohne_nenner
Prüft, dass Zähler und Nenner getrennt und führende Nullen entfernt werden.grün
TestEingabe
test_ungueltige_flurstuecksnummer
Prüft, dass leere oder unzulässige Flurstücksnummern abgelehnt werden.grün
TestEingabe
test_kennzeichen_zerlegen
Prüft, dass Gemarkung, Zähler und Nenner aus dem ALKIS-Kennzeichen gelesen werden.grün
TestEingabe
test_gemarkungsnummer_aus_ziffern
Prüft, dass Ziffern als Gemarkungsnummer gelten, auch ohne führende Nullen oder als langer Schlüssel.grün
TestEingabe
test_gemarkungsname_ueber_gemarkungslayer
Prüft, dass ein Gemarkungsname ohne Rücksicht auf Großschreibung in alle passenden Nummern übersetzt wird.grün
TestEingabe
test_gemarkungsname_ohne_layer_oder_unbekannt
Prüft, dass ein Name ohne Gemarkungslayer oder ohne Treffer eine verständliche Meldung ergibt.grün
TestSuche
test_mit_nenner_genau_ein_treffer
Prüft, dass mit Nenner nur das passende Flurstück der Gemarkung gefunden wird.grün
TestSuche
test_ohne_nenner_alle_mit_diesem_zaehler
Prüft, dass ohne Nenner alle Flurstücke mit dem Zähler gefunden werden, aber nur in der gesuchten Gemarkung.grün
TestSuche
test_kein_treffer
Prüft, dass ein nicht vorhandenes Flurstück eine leere Trefferliste ergibt.grün
TestSuche
test_layer_ohne_kennzeichenfeld
Prüft, dass ein Flurstückslayer ohne Feld „flurstueckskennzeichen“ gemeldet wird.grün
TestSuche
test_fehlender_flurstueckslayer_bricht_ab
Prüft, dass die Suche ohne Layer „Flurstücke“ mit dem Hinweis abbricht, zuerst Verfahrensgebiet und Flurstücke zu laden.grün
TestSuche
test_suche_im_projekt_mit_gemarkungsname
Prüft, dass die Suche im Projekt den Gemarkungsnamen auflöst und Layer samt Treffern liefert.grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
   root(["test_gemarkung_alkis.py"])
   class root rootnode
   root --> g1["TestExtractSchluesselUndName (4)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 3174fa7..2490ce1 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 60 Testdateien, 744 Testfälle – 701 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 599 von 744 (81 %).
+Gesamt: 3 Pakete, 62 Testdateien, 762 Testfälle – 719 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 617 von 762 (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` | 12 | 210 | 168 | 34 | 8 | 108 von 210 (51 %) |
+| `sn_verfahrensgebiet` | 14 | 228 | 186 | 34 | 8 | 126 von 228 (55 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **60** | **744** | **701** | **35** | **8** | **599 von 744 (81 %)** |
+| **Summe** | **62** | **762** | **719** | **35** | **8** | **617 von 762 (81 %)** |
 
 ### Legende
 
@@ -1071,7 +1071,7 @@ Testgruppen:
 
 ## Paket `sn_verfahrensgebiet`
 
-Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 210 Testfällen (168 grün, 34 rot, 8 übersprungen).
+Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 14 Testdateien mit 228 Testfällen (186 grün, 34 rot, 8 übersprungen).
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
 
@@ -1128,6 +1128,24 @@ 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_tab.py`
+
+Worum es in der Datei geht: Tests für den Tab „Eigentümer“ – Flurstückssuche (#56).
+
+Testgruppen:
+
+- `TestFlurstueckSuchen` (5 Testfälle): — (Docstring fehlt)
+- `TestDockWidgetTabs` (1 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestFlurstueckSuchen.test_ohne_flurstueckslayer_erscheint_hinweisdialog` | Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint. | grün |
+| `TestFlurstueckSuchen.test_eingabefehler_als_warnung` | Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog. | grün |
+| `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 |
+| `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`
 
 Worum es in der Datei geht: Unit-Tests für das flurstueck_alkis-Modul.
@@ -1154,6 +1172,30 @@ Testgruppen:
 | `TestFlurstueckAlkisAnhaengenDedup.test_append_ohne_kennzeichen_feld_haengt_alles_an` | Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten). | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_ohne_schluessel_abfrage_append_dedup` | ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47). | grün |
 
+### `Plugin_SN_Verfahrensgebiet/tests/test_flurstueck_suche.py`
+
+Worum es in der Datei geht: Tests für die Flurstückssuche im Layer „Flurstücke“ (#56).
+
+Testgruppen:
+
+- `TestEingabe` (6 Testfälle): — (Docstring fehlt)
+- `TestSuche` (6 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestEingabe.test_flurstuecksnummer_mit_und_ohne_nenner` | Prüft, dass Zähler und Nenner getrennt und führende Nullen entfernt werden. | grün |
+| `TestEingabe.test_ungueltige_flurstuecksnummer` | Prüft, dass leere oder unzulässige Flurstücksnummern abgelehnt werden. | grün |
+| `TestEingabe.test_kennzeichen_zerlegen` | Prüft, dass Gemarkung, Zähler und Nenner aus dem ALKIS-Kennzeichen gelesen werden. | grün |
+| `TestEingabe.test_gemarkungsnummer_aus_ziffern` | Prüft, dass Ziffern als Gemarkungsnummer gelten, auch ohne führende Nullen oder als langer Schlüssel. | grün |
+| `TestEingabe.test_gemarkungsname_ueber_gemarkungslayer` | Prüft, dass ein Gemarkungsname ohne Rücksicht auf Großschreibung in alle passenden Nummern übersetzt wird. | grün |
+| `TestEingabe.test_gemarkungsname_ohne_layer_oder_unbekannt` | Prüft, dass ein Name ohne Gemarkungslayer oder ohne Treffer eine verständliche Meldung ergibt. | grün |
+| `TestSuche.test_mit_nenner_genau_ein_treffer` | Prüft, dass mit Nenner nur das passende Flurstück der Gemarkung gefunden wird. | grün |
+| `TestSuche.test_ohne_nenner_alle_mit_diesem_zaehler` | Prüft, dass ohne Nenner alle Flurstücke mit dem Zähler gefunden werden, aber nur in der gesuchten Gemarkung. | grün |
+| `TestSuche.test_kein_treffer` | Prüft, dass ein nicht vorhandenes Flurstück eine leere Trefferliste ergibt. | grün |
+| `TestSuche.test_layer_ohne_kennzeichenfeld` | Prüft, dass ein Flurstückslayer ohne Feld „flurstueckskennzeichen“ gemeldet wird. | grün |
+| `TestSuche.test_fehlender_flurstueckslayer_bricht_ab` | Prüft, dass die Suche ohne Layer „Flurstücke“ mit dem Hinweis abbricht, zuerst Verfahrensgebiet und Flurstücke zu laden. | grün |
+| `TestSuche.test_suche_im_projekt_mit_gemarkungsname` | Prüft, dass die Suche im Projekt den Gemarkungsnamen auflöst und Layer samt Treffern liefert. | grün |
+
 ### `Plugin_SN_Verfahrensgebiet/tests/test_gemarkung_alkis.py`
 
 Worum es in der Datei geht: Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.
@@ -1563,7 +1605,7 @@ dann verschwindet er hier von selbst.
 
 Keine Lücken.
 
-### `sn_verfahrensgebiet` – 102 von 210 Testfällen ohne Beschreibung
+### `sn_verfahrensgebiet` – 102 von 228 Testfällen ohne Beschreibung
 
 #### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` (25 von 25)
 
diff --git a/docs/tests/verfahrensgebiet-flurstueckssuche.md b/docs/tests/verfahrensgebiet-flurstueckssuche.md
new file mode 100644
index 0000000..63c05fb
--- /dev/null
+++ b/docs/tests/verfahrensgebiet-flurstueckssuche.md
@@ -0,0 +1,84 @@
+# Testkonzept: Flurstückssuche 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 einem Eigentümer soll die Sachbearbeitung ein Flurstück schnell auf der Karte zeigen können.
+Sie gibt Gemarkung und Flurstücksnummer ein, das Plugin wählt das Flurstück im geladenen Flurstückslayer aus
+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.
+
+## Der Ablauf
+
+```mermaid
+flowchart TD
+    A["1 Anwender gibt Gemarkung
und Flurstücksnummer ein"] --> B{"2 Flurstückslayer
im Projekt?"} + B -->|nein| C["Hinweis: zuerst Verfahrensgebiet
und Flurstücke aus ALKIS laden"] + B -->|ja| D{"3 Flurstücksnummer lesbar?
z. B. 562 oder 12/3"} + D -->|nein| E["Hinweis zur Schreibweise"] + D -->|ja| F{"4 Gemarkung als Nummer
oder als Name?"} + F -->|Nummer| H["5 Flurstücke vergleichen"] + F -->|Name| G{"Gemarkungslayer geladen
und Name darin?"} + G -->|nein| E2["Hinweis: Nummer eingeben
oder Gemarkungen laden"] + G -->|ja| H + H --> I{"6 Treffer?"} + I -->|nein| J["Hinweis: nicht gefunden"] + I -->|ja| K["7 Treffer auswählen,
hinzoomen, aufblinken"] + + style C fill:#fff4e6,stroke:#d9822b + style E fill:#fff4e6,stroke:#d9822b + style E2 fill:#fff4e6,stroke:#d9822b +``` + +## Was geprüft wird + +Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_flurstueck_suche.py` (Suchlogik) und +`tests/test_eigentuemer_tab.py` (Bedienung im Tab). + +| # | Schritt | Wird geprüft durch | Ebene | +|---|---|---|---| +| — | Tab erscheint nach „Bearbeitung“ | `test_tab_ist_im_dockwidget_eingetragen` | Mock | +| 2 | Kein Flurstückslayer: Suche bricht ab | `test_fehlender_flurstueckslayer_bricht_ab` | Mock | +| 2 | … und der Hinweis kommt als Dialog | `test_ohne_flurstueckslayer_erscheint_hinweisdialog` | Mock | +| 3 | Zähler und Nenner lesen, führende Nullen | `test_flurstuecksnummer_mit_und_ohne_nenner` | Mock | +| 3 | Unlesbare Nummer abweisen | `test_ungueltige_flurstuecksnummer`, `test_eingabefehler_als_warnung` | Mock | +| 4 | Gemarkungsnummer, auch ohne Nullen oder als langer Schlüssel | `test_gemarkungsnummer_aus_ziffern` | Mock | +| 4 | Gemarkungsname, auch mehrfach vorkommend | `test_gemarkungsname_ueber_gemarkungslayer` | Mock | +| 4 | Name ohne Gemarkungslayer oder unbekannt | `test_gemarkungsname_ohne_layer_oder_unbekannt` | Mock | +| 5 | Flurstückskennzeichen zerlegen | `test_kennzeichen_zerlegen` | Mock | +| 5 | Layer ohne Kennzeichenfeld | `test_layer_ohne_kennzeichenfeld` | Mock | +| 6 | Mit Nenner genau ein Treffer | `test_mit_nenner_genau_ein_treffer` | Mock | +| 6 | Ohne Nenner alle mit diesem Zähler, nur in der Gemarkung | `test_ohne_nenner_alle_mit_diesem_zaehler` | Mock | +| 6 | Kein Treffer | `test_kein_treffer` (Logik), `test_kein_treffer` (Tab) | Mock | +| 7 | Auswahl, Zoom, Aufblinken | `test_treffer_werden_ausgewaehlt_und_angezoomt` | Mock | +| 7 | Auswahl auch ohne Kartenansicht | `test_ohne_kartenansicht_bleibt_auswahl` | 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 | +|---|---|---| +| Buchstaben-Nenner (`12/a`) | ältere Flurstücke | `test_mit_nenner_genau_ein_treffer`, `test_kennzeichen_zerlegen` | +| Gleichnamige Gemarkungen | verbreitete Ortsnamen in mehreren Landkreisen | `test_gemarkungsname_ueber_gemarkungslayer` | +| Gemarkungsschlüssel mit Landesnummer (`140268`) | kopiert aus anderen Programmen | `test_gemarkungsnummer_aus_ziffern` | +| Flurstücke mit leerem Kennzeichen | unvollständige Daten | `test_ohne_nenner_alle_mit_diesem_zaehler` (Feature ohne Kennzeichen wird übersprungen) | +| Im Flurstückslayer ist schon etwas ausgewählt | Verfahrensgebiet aus der Flurstücksauswahl bilden | `test_treffer_werden_ausgewaehlt_und_angezoomt`: Die Suche ersetzt die Auswahl, wie die Suchen in QGIS selbst | + +## Bewusst nicht getestet + +| Was | Warum | Wie stattdessen abgesichert | +|---|---|---| +| Zoomen und Aufblinken in der Kartenansicht | QGIS-Funktionen (`zoomToSelected`, `flashFeatureIds`), im Mock-Modus gibt es keine Karte | manueller Test in QGIS | +| Aussehen des Tabs | nicht sinnvoll automatisiert prüfbar | manueller Test | +| Echter Flurstückslayer aus dem ALKIS-WFS | kein Netzwerk in Tests | Aufbau des Kennzeichens in `../wissen/alkis_objektarten.md`, manueller Test | + +## Bekannte Lücken + +| Lücke | Auswirkung | Issue | +|---|---|---| +| Der manuelle Test in QGIS steht aus | Zoom auf sehr kleine Flurstücke und das Aufblinken sind nicht gesehen | Verfahrensgebiet #56 | diff --git a/docs/wissen/alkis_objektarten.md b/docs/wissen/alkis_objektarten.md index 26b037b..45f011f 100644 --- a/docs/wissen/alkis_objektarten.md +++ b/docs/wissen/alkis_objektarten.md @@ -37,6 +37,14 @@ Versionsnummern unten nachziehen. - Praktisch relevant: das Flurstückskennzeichen ist objektbildend; jedes Flurstück gehört zu genau einer Gemarkung bzw. Flur/Gemarkungsteil. Das Plugin übernimmt das komplette Feldschema des WFS-Layers in den Memory-Layer, ohne Attribute fest zu benennen. +- **Aufbau von `flurstueckskennzeichen`** (20 Zeichen, unbesetzte Teile mit `_` gefüllt), am Dienst geprüft + am 06.10.2026: `14 0268 ___ 00562 ____ 00` = Land (2), Gemarkungsnummer (4), Flur (3, in Sachsen unbesetzt), + Zähler (5), Nenner (4, auch Buchstaben wie `000a`), Folge (2). Die Flurstückssuche (Verfahrensgebiet #56) + vergleicht deshalb dieses Kennzeichen und nicht die von QGIS abgeflachten Einzelfelder + (`flurstuecksnummer/…/zaehler`), deren Namen vom WFS-Provider abhängen. +- **Keine Eigentümer- oder Buchungsangaben:** Der öffentliche Dienst verweist nur per `istGebucht` auf ein + Buchungsblatt-Objekt, das er selbst nicht ausliefert. Eine Zuordnung Eigentümer ↔ Flurstück braucht eine eigene + Quelle (Verfahrensgebiet #56, dort zurückgestellt). ## `AX_BauRaumOderBodenordnungsrecht` — Bau-, Raum- oder Bodenordnungsrecht From 44cce10bda03e57ef99f2d2fcfee66282e0c3683 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 6 Oct 2026 13:04:16 +0200 Subject: [PATCH 17/20] =?UTF-8?q?Eigent=C3=BCmersuche=20(Verfahrensgebiet?= =?UTF-8?q?=20#56)=20dokumentiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/architektur/modulkarte.md | 8 +- docs/tests/testkatalog.html | 92 ++++++++++++++++--- docs/tests/testkatalog.md | 60 ++++++++++-- .../verfahrensgebiet-eigentuemersuche.md | 85 +++++++++++++++++ .../verfahrensgebiet-flurstueckssuche.md | 2 +- docs/wissen/fallstricke.md | 2 + 6 files changed, 225 insertions(+), 24 deletions(-) create mode 100644 docs/tests/verfahrensgebiet-eigentuemersuche.md diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md index c0e2275..ccc665a 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -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 | diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index d12f21b..3d1992c 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,21 +119,21 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
762Testfälle
-
719grün
+
782Testfälle
+
739grün
35rot (Baseline)
81%mit Docstring
-
62Testdateien
+
64Testdateien
Verteilung je Paket
sn_basis43 Dateien · 490 Testfälle
-
sn_verfahrensgebiet14 Dateien · 228 Testfälle
+
sn_verfahrensgebiet16 Dateien · 248 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -1675,10 +1675,10 @@

sn_verfahrensgebiet

-

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 14 Testdateien · 228 Testfälle

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 16 Testdateien · 248 Testfälle

-

Unit-Tests für `sn_verfahrensgebiet.functions.beteiligung_pruefung`.

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
@@ -1765,7 +1765,44 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für den Tab „Eigentümer“ – Flurstückssuche (#56).

TestFlurstueckSuchen 5TestDockWidgetTabs 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für die Eigentümersuche im Serienbriefexport (#56).

TestLayererkennung 2TestSuche 9TestAnzeigeformen 1
flowchart TD
+  root(["test_eigentuemer_suche.py"])
+  class root rootnode
+  root --> g1["TestLayererkennung (2)"]
+  class g1 groupnode
+  g1 --> t1("layer_mit_name_und_vorname_ist_eigentuemerlayer")
+  g1 --> t2("eigentuemerlayer_im_projekt")
+  root --> g2["TestSuche (9)"]
+  class g2 groupnode
+  g2 --> t3("ohne_eingabe_fehler")
+  g2 --> t4("layer_ohne_pflichtspalten")
+  g2 --> t5("teilstring_ohne_gross_kleinschreibung")
+  g2 --> t6("umlaute_und_eszett")
+  g2 --> t7("gleiche_person_gleiche_anschrift_zusammengefasst")
+  g2 --> t8("andere_anschrift_eigener_treffer")
+  g2 --> t9("kennzeichen_verstorben_und_rechtsperson")
+  g2 --> t10("plz_mit_fuehrender_null")
+  g2 --> t11("suche_gibt_nichts_aus")
+  root --> g3["TestAnzeigeformen (1)"]
+  class g3 groupnode
+  g3 --> t12("name_und_anschrift")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für den Tab „Eigentümer“ – Flurstückssuche und Eigentümersuche (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 2TestDockWidgetTabs 1
flowchart TD
   root(["test_eigentuemer_tab.py"])
   class root rootnode
   root --> g1["TestFlurstueckSuchen (5)"]
@@ -1775,20 +1812,36 @@
   g1 --> t3("kein_treffer")
   g1 --> t4("treffer_werden_ausgewaehlt_und_angezoomt")
   g1 --> t5("ohne_kartenansicht_bleibt_auswahl")
-  root --> g2["TestDockWidgetTabs (1)"]
+  root --> g2["TestEigentuemerSuchen (4)"]
   class g2 groupnode
-  g2 --> t6("tab_ist_im_dockwidget_eingetragen")
+  g2 --> t6("ohne_eigentuemerdatei_warnung")
+  g2 --> t7("suchfehler_als_warnung")
+  g2 --> t8("kein_treffer_ohne_namen_in_meldung")
+  g2 --> t9("treffer_oeffnen_trefferliste_und_ersetzen_die_alte")
+  root --> g3["TestTrefferliste (2)"]
+  class g3 groupnode
+  g3 --> t10("trefferzeile_mit_hinweis")
+  g3 --> t11("ausgewaehlte_eigentuemer")
+  root --> g4["TestDockWidgetTabs (1)"]
+  class g4 groupnode
+  g4 --> t12("tab_ist_im_dockwidget_eingetragen")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
   class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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
TestDockWidgetTabs
test_tab_ist_im_dockwidget_eingetragen
Prüft, dass der Tab „Eigentümer“ im DockWidget direkt nach dem Bearbeitungs-Tab steht.grün

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
   root --> g1["TestFlurstueckAlkis (11)"]
@@ -2102,7 +2155,20 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSelectByLocation
test_processing_not_available_returns_zero
— (Docstring fehlt)grün
TestSelectByLocation
test_processing_success_returns_count
— (Docstring fehlt)grün
TestSelectByLocation
test_processing_exception_returns_zero
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_empty_vkz_does_nothing
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_none_vkz_does_nothing
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_vkz_field_added
— (Docstring fehlt)grün
TestFlaechenvergleich
test_gleiche_flaeche_stimmt_ueberein
— (Docstring fehlt)grün
TestFlaechenvergleich
test_verschiedene_flaeche_stimmt_nicht_ueberein
— (Docstring fehlt)grün
TestFlaechenvergleich
test_differenz_innerhalb_toleranz
— (Docstring fehlt)grün
TestFlaechenvergleich
test_differenz_ausserhalb_toleranz
— (Docstring fehlt)grün
TestFlaechenvergleich
test_flaechen_werte_korrekt
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_default_ist_null
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_parst_string
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_ungueltig_fallback_null
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_vg_alkis_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_flst_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_no_selection_returns_none_enriched
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_invalid_enriched_layer_returns_none
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_progress_cancel_after_step1_returns_none
Abbruch nach Schritt 1 → sofortiger sauberer Rückgabestatus.grün
TestVerfahrensgebietAlkisKomplett
test_success_flaechen_match_zeigt_info
Flächenübereinstimmung → show_info_dialog mit erwartetem Text.grün
TestVerfahrensgebietAlkisKomplett
test_no_vkz_variable_passes_empty_string
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_abbrechen_kein_detailcheck
Abweichung + Abbrechen → knickpunkt_pruefung wird NICHT aufgerufen.grün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_als_vg_laden
Abweichung + Als VG laden → BROR-Layer wird umbenannt und alsgrün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_detailpruefung_startet_knickpruefung
Abweichung + Detailprüfung → lade_punktort_ta + knickpunkt_pruefunggrün
TestVerfahrensgebietAlkisKomplett
test_bror_layer_bleibt_bei_erfolg_im_projekt
Bei Erfolg darf der BROR-Layer NICHT entfernt werden.grün
TestVerfahrensgebietAlkisKomplett
test_bror_layer_wird_bei_fehler_entfernt
Bei Fehler (flst_alkis NONE) wird der BROR-Layer aus dem Projekt entfernt.grün
TestVerfahrensgebietAlkisKomplett
test_kein_vkz_kein_landkreis_dialog_abgebrochen
Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer bricht dengrün
TestVerfahrensgebietAlkisKomplett
test_kein_vkz_kein_landkreis_dialog_liefert_landkreis
Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer wählt einengrün
TestVerfahrensgebietAlkisKomplett
test_landkreis_modus_wird_bei_fehlender_vkz_verwendet
Keine Verfahrensnummer, aber Landkreis gesetzt → Landkreis-Suchpfadgrün
TestVerfahrensgebietAlkisKomplett
test_landkreis_modus_nur_laden_ueberspringt_dialog2
nur_laden=True → Funktion kehrt sofort zurück, Dialog 2 (Gemarkunggrün

Unit-Tests für das Verfahrensgebiet-Edit-Service-Modul.

TestPruefeFlurstueckLayer 3TestPruefeAuswahl 3TestGetKennzeichenMenge 3TestErstelle 3TestErgaenzeVerfahrensgebiet 3TestEntferneAusVerfahrensgebiet 5TestErsetze 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSelectByLocation
test_processing_not_available_returns_zero
— (Docstring fehlt)grün
TestSelectByLocation
test_processing_success_returns_count
— (Docstring fehlt)grün
TestSelectByLocation
test_processing_exception_returns_zero
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_empty_vkz_does_nothing
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_none_vkz_does_nothing
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_vkz_field_added
— (Docstring fehlt)grün
TestFlaechenvergleich
test_gleiche_flaeche_stimmt_ueberein
— (Docstring fehlt)grün
TestFlaechenvergleich
test_verschiedene_flaeche_stimmt_nicht_ueberein
— (Docstring fehlt)grün
TestFlaechenvergleich
test_differenz_innerhalb_toleranz
— (Docstring fehlt)grün
TestFlaechenvergleich
test_differenz_ausserhalb_toleranz
— (Docstring fehlt)grün
TestFlaechenvergleich
test_flaechen_werte_korrekt
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_default_ist_null
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_parst_string
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_ungueltig_fallback_null
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_vg_alkis_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_flst_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_no_selection_returns_none_enriched
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_invalid_enriched_layer_returns_none
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_progress_cancel_after_step1_returns_none
Abbruch nach Schritt 1 → sofortiger sauberer Rückgabestatus.grün
TestVerfahrensgebietAlkisKomplett
test_success_flaechen_match_zeigt_info
Flächenübereinstimmung → show_info_dialog mit erwartetem Text.grün
TestVerfahrensgebietAlkisKomplett
test_no_vkz_variable_passes_empty_string
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_abbrechen_kein_detailcheck
Abweichung + Abbrechen → knickpunkt_pruefung wird NICHT aufgerufen.grün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_als_vg_laden
Abweichung + Als VG laden → BROR-Layer wird umbenannt und alsgrün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_detailpruefung_startet_knickpruefung
Abweichung + Detailprüfung → lade_punktort_ta + knickpunkt_pruefunggrün
TestVerfahrensgebietAlkisKomplett
test_bror_layer_bleibt_bei_erfolg_im_projekt
Bei Erfolg darf der BROR-Layer NICHT entfernt werden.grün
TestVerfahrensgebietAlkisKomplett
test_bror_layer_wird_bei_fehler_entfernt
Bei Fehler (flst_alkis NONE) wird der BROR-Layer aus dem Projekt entfernt.grün
TestVerfahrensgebietAlkisKomplett
test_kein_vkz_kein_landkreis_dialog_abgebrochen
Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer bricht dengrün
TestVerfahrensgebietAlkisKomplett
test_kein_vkz_kein_landkreis_dialog_liefert_landkreis
Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer wählt einengrün
TestVerfahrensgebietAlkisKomplett
test_landkreis_modus_wird_bei_fehlender_vkz_verwendet
Keine Verfahrensnummer, aber Landkreis gesetzt → Landkreis-Suchpfadgrün
TestVerfahrensgebietAlkisKomplett
test_landkreis_modus_nur_laden_ueberspringt_dialog2
nur_laden=True → Funktion kehrt sofort zurück, Dialog 2 (Gemarkunggrün

Tests für die gemeinsame Auswahltabelle in verfahrensgebiet_dialoge (#56).

TestAuswahltabelle 2
flowchart TD
+  root(["test_verfahrensgebiet_dialoge.py"])
+  class root rootnode
+  root --> g1["TestAuswahltabelle (2)"]
+  class g1 groupnode
+  g1 --> t1("suchfilter_ueber_alle_spalten")
+  g1 --> t2("auswahl_nach_umsortieren")
+  class t1 ok
+  class t2 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Unit-Tests für das Verfahrensgebiet-Edit-Service-Modul.

TestPruefeFlurstueckLayer 3TestPruefeAuswahl 3TestGetKennzeichenMenge 3TestErstelle 3TestErgaenzeVerfahrensgebiet 3TestEntferneAusVerfahrensgebiet 5TestErsetze 1
flowchart TD
   root(["test_verfahrensgebiet_edit_service.py"])
   class root rootnode
   root --> g1["TestPruefeFlurstueckLayer (3)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 2490ce1..24cdf23 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -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)
 
diff --git a/docs/tests/verfahrensgebiet-eigentuemersuche.md b/docs/tests/verfahrensgebiet-eigentuemersuche.md
new file mode 100644
index 0000000..9808069
--- /dev/null
+++ b/docs/tests/verfahrensgebiet-eigentuemersuche.md
@@ -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
als CSV-Layer ins Projekt"] --> B["2 Eigentümerdatei wählen
(nur Layer mit Nachname und Vorname)"] + B --> C{"3 Eigentümerdatei
gewählt?"} + C -->|nein| D["Hinweis: CSV laden und wählen"] + C -->|ja| E{"4 Name oder Vorname
eingegeben?"} + E -->|nein| F["Hinweis: Namen eingeben"] + E -->|ja| G["5 Einträge suchen
(Teilwort, Groß-/Kleinschreibung
und Umlaute egal)"] + G --> H["6 Gleiche Person mit gleicher
Anschrift zusammenfassen"] + H --> I{"7 Treffer?"} + I -->|nein| J["Hinweis: keine Einträge
(ohne den Namen zu wiederholen)"] + I -->|ja| K["8 Trefferliste mit Anschrift, E-Mail,
verstorben / Rechtsperson;
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 | diff --git a/docs/tests/verfahrensgebiet-flurstueckssuche.md b/docs/tests/verfahrensgebiet-flurstueckssuche.md index 63c05fb..3eaf71a 100644 --- a/docs/tests/verfahrensgebiet-flurstueckssuche.md +++ b/docs/tests/verfahrensgebiet-flurstueckssuche.md @@ -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 diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md index 6be7897..eafcf18 100644 --- a/docs/wissen/fallstricke.md +++ b/docs/wissen/fallstricke.md @@ -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. From aa7fe5b2e40ea96763cdef18ebd855ee632b3554 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 6 Oct 2026 13:13:50 +0200 Subject: [PATCH 18/20] =?UTF-8?q?Testkonzept=20Eigent=C3=BCmersuche:=20PER?= =?UTF-8?q?=5FRECHTSPERSON=20als=20Rohwert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/tests/testkatalog.html | 30 ++++++++++--------- docs/tests/testkatalog.md | 19 ++++++------ .../verfahrensgebiet-eigentuemersuche.md | 15 ++++++---- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index 3d1992c..ddcf98e 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,8 +119,8 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
782Testfälle
-
739grün
+
783Testfälle
+
740grün
35rot (Baseline)
81%mit Docstring
@@ -128,12 +128,12 @@
Verteilung je Paket
sn_basis43 Dateien · 490 Testfälle
-
sn_verfahrensgebiet16 Dateien · 248 Testfälle
+
sn_verfahrensgebiet16 Dateien · 249 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -1675,9 +1675,9 @@

sn_verfahrensgebiet

-

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 16 Testdateien · 248 Testfälle

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 16 Testdateien · 249 Testfälle

- @@ -1765,14 +1765,14 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für die Eigentümersuche im Serienbriefexport (#56).

TestLayererkennung 2TestSuche 9TestAnzeigeformen 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für die Eigentümersuche im Serienbriefexport (#56).

TestLayererkennung 2TestSuche 10TestAnzeigeformen 1
flowchart TD
   root(["test_eigentuemer_suche.py"])
   class root rootnode
   root --> g1["TestLayererkennung (2)"]
   class g1 groupnode
   g1 --> t1("layer_mit_name_und_vorname_ist_eigentuemerlayer")
   g1 --> t2("eigentuemerlayer_im_projekt")
-  root --> g2["TestSuche (9)"]
+  root --> g2["TestSuche (10)"]
   class g2 groupnode
   g2 --> t3("ohne_eingabe_fehler")
   g2 --> t4("layer_ohne_pflichtspalten")
@@ -1780,12 +1780,13 @@
   g2 --> t6("umlaute_und_eszett")
   g2 --> t7("gleiche_person_gleiche_anschrift_zusammengefasst")
   g2 --> t8("andere_anschrift_eigener_treffer")
-  g2 --> t9("kennzeichen_verstorben_und_rechtsperson")
-  g2 --> t10("plz_mit_fuehrender_null")
-  g2 --> t11("suche_gibt_nichts_aus")
+  g2 --> t9("kennzeichen_verstorben")
+  g2 --> t10("rechtsperson_als_rohwert")
+  g2 --> t11("plz_mit_fuehrender_null")
+  g2 --> t12("suche_gibt_nichts_aus")
   root --> g3["TestAnzeigeformen (1)"]
   class g3 groupnode
-  g3 --> t12("name_und_anschrift")
+  g3 --> t13("name_und_anschrift")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -1798,11 +1799,12 @@
   class t10 ok
   class t11 ok
   class t12 ok
+  class t13 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für den Tab „Eigentümer“ – Flurstückssuche und Eigentümersuche (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 2TestDockWidgetTabs 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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
Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht.grün
TestSuche
test_rechtsperson_als_rohwert
Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden.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

Tests für den Tab „Eigentümer“ – Flurstückssuche und Eigentümersuche (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 2TestDockWidgetTabs 1
flowchart TD
   root(["test_eigentuemer_tab.py"])
   class root rootnode
   root --> g1["TestFlurstueckSuchen (5)"]
@@ -1841,7 +1843,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ 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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
   root --> g1["TestFlurstueckAlkis (11)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 24cdf23..ecde425 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 64 Testdateien, 782 Testfälle – 739 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 637 von 782 (81 %).
+Gesamt: 3 Pakete, 64 Testdateien, 783 Testfälle – 740 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 638 von 783 (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` | 16 | 248 | 206 | 34 | 8 | 146 von 248 (59 %) |
+| `sn_verfahrensgebiet` | 16 | 249 | 207 | 34 | 8 | 147 von 249 (59 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **64** | **782** | **739** | **35** | **8** | **637 von 782 (81 %)** |
+| **Summe** | **64** | **783** | **740** | **35** | **8** | **638 von 783 (81 %)** |
 
 ### Legende
 
@@ -1071,7 +1071,7 @@ Testgruppen:
 
 ## Paket `sn_verfahrensgebiet`
 
-Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 16 Testdateien mit 248 Testfällen (206 grün, 34 rot, 8 übersprungen).
+Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 16 Testdateien mit 249 Testfällen (207 grün, 34 rot, 8 übersprungen).
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
 
@@ -1135,7 +1135,7 @@ Worum es in der Datei geht: Tests für die Eigentümersuche im Serienbriefexport
 Testgruppen:
 
 - `TestLayererkennung` (2 Testfälle): — (Docstring fehlt)
-- `TestSuche` (9 Testfälle): — (Docstring fehlt)
+- `TestSuche` (10 Testfälle): — (Docstring fehlt)
 - `TestAnzeigeformen` (1 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
@@ -1148,7 +1148,8 @@ Testgruppen:
 | `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_kennzeichen_verstorben` | Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht. | grün |
+| `TestSuche.test_rechtsperson_als_rohwert` | Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden. | 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 |
@@ -1175,7 +1176,7 @@ Testgruppen:
 | `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_trefferzeile_mit_hinweis` | Prüft, dass die Trefferliste Name, Anschrift, E-Mail, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ 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 |
 
@@ -1651,7 +1652,7 @@ dann verschwindet er hier von selbst.
 
 Keine Lücken.
 
-### `sn_verfahrensgebiet` – 102 von 248 Testfällen ohne Beschreibung
+### `sn_verfahrensgebiet` – 102 von 249 Testfällen ohne Beschreibung
 
 #### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` (25 von 25)
 
diff --git a/docs/tests/verfahrensgebiet-eigentuemersuche.md b/docs/tests/verfahrensgebiet-eigentuemersuche.md
index 9808069..8bf5ac0 100644
--- a/docs/tests/verfahrensgebiet-eigentuemersuche.md
+++ b/docs/tests/verfahrensgebiet-eigentuemersuche.md
@@ -9,7 +9,9 @@ E-Mail. Grundlage ist der Serienbriefexport als CSV-Datei, den die Sachbearbeitu
 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.
+Rechtspersonen sind in der Liste gekennzeichnet:
+verstorben als Hinweis (`PER_VERSTORBEN` = 1), die Art der Person als Rohwert von `PER_RECHTSPERSON`
+(0, 1, 5 oder 9; die Bedeutung der Werte wird später entschlüsselt).
 
 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.
@@ -27,7 +29,7 @@ flowchart TD
     G --> H["6 Gleiche Person mit gleicher
Anschrift zusammenfassen"] H --> I{"7 Treffer?"} I -->|nein| J["Hinweis: keine Einträge
(ohne den Namen zu wiederholen)"] - I -->|ja| K["8 Trefferliste mit Anschrift, E-Mail,
verstorben / Rechtsperson;
Mehrfachauswahl"] + I -->|ja| K["8 Trefferliste mit Anschrift, E-Mail,
Rechtsperson-Wert, verstorben;
Mehrfachauswahl"] style D fill:#fff4e6,stroke:#d9822b style F fill:#fff4e6,stroke:#d9822b @@ -51,7 +53,7 @@ Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_suche.py` | 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 | Spalten der Trefferliste, Kennzeichen | `test_trefferzeile_mit_hinweis`, `test_kennzeichen_verstorben`, `test_rechtsperson_als_rohwert` | 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 | @@ -67,9 +69,10 @@ Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` ( | 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` | +| `PER_VERSTORBEN` als Text oder als Zahl gelesen | QGIS erkennt beim CSV-Laden die Spalte als Zahl | `test_kennzeichen_verstorben` | +| Eine Person mit verschiedenen `PER_RECHTSPERSON`-Werten | mehrere Konten mit unterschiedlicher Erfassung | `test_rechtsperson_als_rohwert` (Werte werden gesammelt) | +| Leere Felder bzw. QGIS-NULL | unvollständige Datensätze | `test_kennzeichen_verstorben`, `test_rechtsperson_als_rohwert` | +| Rechtsperson ohne Vornamen (z. B. Gemeinde) | Körperschaften als Eigentümer | `test_name_und_anschrift`, `test_rechtsperson_als_rohwert` | ## Bewusst nicht getestet From 5e87f2367e4d6f53954fab4f6b67c1f44145e7c1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 6 Oct 2026 13:27:15 +0200 Subject: [PATCH 19/20] =?UTF-8?q?Anzeige=20f=C3=BCr=20den=20Eigent=C3=BCme?= =?UTF-8?q?r=20und=20Korrekturen=20(Verfahrensgebiet=20#56)=20dokumentiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testkonzept docs/tests/verfahrensgebiet-eigentuemer-anzeige.md, Fallstricke (Korrektur-CSV mit Personendaten, Fenster auf bestimmtem Bildschirm öffnen), Modulkarte und Testkatalog. Co-Authored-By: Claude Opus 5.5 --- docs/architektur/modulkarte.md | 9 +- docs/tests/testkatalog.html | 138 ++++++++++++++++-- docs/tests/testkatalog.md | 85 +++++++++-- .../verfahrensgebiet-eigentuemer-anzeige.md | 93 ++++++++++++ .../verfahrensgebiet-eigentuemersuche.md | 4 +- docs/wissen/fallstricke.md | 3 +- 6 files changed, 302 insertions(+), 30 deletions(-) create mode 100644 docs/tests/verfahrensgebiet-eigentuemer-anzeige.md diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md index ccc665a..fbdc1c9 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -86,8 +86,10 @@ 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` | 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) | +| `ui/tabs/eigentuemer_tab.py` | 411 | Tab „Eigentümer“ (#56): Flurstückssuche, Eigentümersuche, Anzeige für den Eigentümer (Bildschirm, Sitzung, „Anzeige leeren“) und Korrekturen | +| `ui/eigentuemer_treffer.py` | 83 | Nicht modale Trefferliste der Eigentümersuche mit Mehrfachauswahl und „Daten anzeigen“ (#56) | +| `ui/eigentuemer_anzeige.py` | 178 | Anzeigefenster für den Eigentümer (nur Name, Anschrift, E-Mail; maskiertes HTML), Bildschirmwahl, Vollbild auf dem zweiten Bildschirm (#56) | +| `ui/eigentuemer_korrektur_dialog.py` | 79 | Korrekturdialog für den Bearbeiter (#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 | @@ -96,13 +98,14 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `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_korrektur.py` | 128 | Korrekturen ermitteln und an `_korrekturen.csv` anhängen; Pfad aus der Layerquelle; Qt-frei (#56) | | `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/` | 16 Dateien | Mock-Modus, aktuell 157/201 grün (Baseline) | +| `tests/` | 18 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 | diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index ddcf98e..16d2eda 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,21 +119,21 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
783Testfälle
-
740grün
+
822Testfälle
+
779grün
35rot (Baseline)
-
81%mit Docstring
-
64Testdateien
+
82%mit Docstring
+
66Testdateien
Verteilung je Paket
sn_basis43 Dateien · 490 Testfälle
-
sn_verfahrensgebiet16 Dateien · 249 Testfälle
+
sn_verfahrensgebiet18 Dateien · 288 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -1675,10 +1675,10 @@

sn_verfahrensgebiet

-

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 16 Testdateien · 249 Testfälle

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 18 Testdateien · 288 Testfälle

-

Unit-Tests für `sn_verfahrensgebiet.functions.beteiligung_pruefung`.

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
@@ -1765,7 +1765,79 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für die Eigentümersuche im Serienbriefexport (#56).

TestLayererkennung 2TestSuche 10TestAnzeigeformen 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für das Anzeigefenster des Eigentümer-Datenabgleichs (#56).

TestAnzeigetext 6TestBildschirme 6
flowchart TD
+  root(["test_eigentuemer_anzeige.py"])
+  class root rootnode
+  root --> g1["TestAnzeigetext (6)"]
+  class g1 groupnode
+  g1 --> t1("name_anschrift_email")
+  g1 --> t2("keine_internen_angaben")
+  g1 --> t3("fehlende_email_wird_genannt")
+  g1 --> t4("werte_werden_maskiert")
+  g1 --> t5("mehrere_eintraege_getrennt")
+  g1 --> t6("leer_nur_platzhalter")
+  root --> g2["TestBildschirme (6)"]
+  class g2 groupnode
+  g2 --> t7("vorgabe_ist_nicht_der_qgis_bildschirm")
+  g2 --> t8("bildschirm_text")
+  g2 --> t9("ohne_qt_anwendung_keine_bildschirme")
+  g2 --> t10("vollbild_auf_gewaehltem_bildschirm")
+  g2 --> t11("normales_fenster_mittig")
+  g2 --> t12("ohne_bildschirm_einfach_zeigen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestAnzeigetext
test_name_anschrift_email
Prüft, dass die Anzeige Name mit Anrede und Titel, die mehrzeilige Anschrift mit Ortsteil und die E-Mail zeigt.grün
TestAnzeigetext
test_keine_internen_angaben
Prüft, dass der Eigentümer weder Konten, Personen-IDs, Rechtsperson-Wert noch das Kennzeichen „verstorben“ sieht.grün
TestAnzeigetext
test_fehlende_email_wird_genannt
Prüft, dass eine fehlende E-Mail ausdrücklich als „keine angegeben“ erscheint.grün
TestAnzeigetext
test_werte_werden_maskiert
Prüft, dass Sonderzeichen aus den Daten maskiert werden und kein HTML einschleusen.grün
TestAnzeigetext
test_mehrere_eintraege_getrennt
Prüft, dass mehrere Einträge untereinander, durch eine Linie getrennt, erscheinen.grün
TestAnzeigetext
test_leer_nur_platzhalter
Prüft, dass die leere Anzeige nur den neutralen Platzhalter zeigt.grün
TestBildschirme
test_vorgabe_ist_nicht_der_qgis_bildschirm
Prüft, dass als Vorgabe der erste Bildschirm ohne QGIS gewählt wird, bei nur einem Bildschirm dieser.grün
TestBildschirme
test_bildschirm_text
Prüft, dass ein Bildschirm mit Nummer und Auflösung beschriftet wird.grün
TestBildschirme
test_ohne_qt_anwendung_keine_bildschirme
Prüft, dass ohne Qt-Anwendung (Mock-Modus) eine leere Bildschirmliste kommt statt eines Fehlers.grün
TestBildschirme
test_vollbild_auf_gewaehltem_bildschirm
Prüft, dass das Fenster im Vollbild dem gewählten Bildschirm zugeordnet und auf dessen Fläche gelegt wird.grün
TestBildschirme
test_normales_fenster_mittig
Prüft, dass auf dem QGIS-Bildschirm ein normales Fenster mittig im freien Bereich öffnet.grün
TestBildschirme
test_ohne_bildschirm_einfach_zeigen
Prüft, dass ohne bekannten Bildschirm das Fenster einfach gezeigt wird.grün

Tests für die Korrekturen beim Eigentümer-Datenabgleich (#56).

TestAenderungen 4TestKorrekturPfad 3TestSchreiben 5
flowchart TD
+  root(["test_eigentuemer_korrektur.py"])
+  class root rootnode
+  root --> g1["TestAenderungen (4)"]
+  class g1 groupnode
+  g1 --> t1("feldwerte")
+  g1 --> t2("nur_echte_aenderungen")
+  g1 --> t3("feld_leeren_ist_aenderung")
+  g1 --> t4("wende_an")
+  root --> g2["TestKorrekturPfad (3)"]
+  class g2 groupnode
+  g2 --> t5("textdatei_provider")
+  g2 --> t6("ogr_provider")
+  g2 --> t7("keine_csv")
+  root --> g3["TestSchreiben (5)"]
+  class g3 groupnode
+  g3 --> t8("neue_datei_mit_kopfzeile_und_bom")
+  g3 --> t9("anhaengen_ohne_zweite_kopfzeile")
+  g3 --> t10("ohne_aenderung_keine_datei")
+  g3 --> t11("schreibfehler_wird_weitergereicht")
+  g3 --> t12("schreibt_nichts_auf_die_konsole")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestAenderungen
test_feldwerte
Prüft, dass alle korrigierbaren Felder vorbelegt werden, mehrere E-Mails durch Komma getrennt.grün
TestAenderungen
test_nur_echte_aenderungen
Prüft, dass nur geänderte Felder zählen und Leerraum am Rand oder doppelte Leerzeichen keine Änderung sind.grün
TestAenderungen
test_feld_leeren_ist_aenderung
Prüft, dass das Leeren eines Feldes als Änderung zählt.grün
TestAenderungen
test_wende_an
Prüft, dass die Änderungen im angezeigten Eintrag landen und E-Mails wieder getrennt werden; Konten bleiben.grün
TestKorrekturPfad
test_textdatei_provider
Prüft, dass aus der Quelle eines Textdatei-Layers (file-URI, Leerzeichen kodiert) der Pfad neben der CSV wird.grün
TestKorrekturPfad
test_ogr_provider
Prüft, dass auch eine OGR-Quelle mit Layernamen den Pfad neben der CSV ergibt.grün
TestKorrekturPfad
test_keine_csv
Prüft, dass für Quellen ohne CSV-Datei kein Pfad vorgeschlagen wird.grün
TestSchreiben
test_neue_datei_mit_kopfzeile_und_bom
Prüft, dass eine neue Korrektur-CSV mit BOM, Kopfzeile und je Änderung einer Zeile mit Exportspaltennamen entsteht.grün
TestSchreiben
test_anhaengen_ohne_zweite_kopfzeile
Prüft, dass weitere Korrekturen angehängt werden, ohne Kopfzeile oder BOM zu wiederholen.grün
TestSchreiben
test_ohne_aenderung_keine_datei
Prüft, dass ohne Änderung keine Datei angelegt wird.grün
TestSchreiben
test_schreibfehler_wird_weitergereicht
Prüft, dass ein nicht schreibbarer Pfad als OSError beim Aufrufer ankommt.grün
TestSchreiben
test_schreibt_nichts_auf_die_konsole
Prüft, dass das Festhalten von Korrekturen keine Personendaten auf die Konsole schreibt (Datenschutz).grün

Tests für die Eigentümersuche im Serienbriefexport (#56).

TestLayererkennung 2TestSuche 10TestAnzeigeformen 1
flowchart TD
   root(["test_eigentuemer_suche.py"])
   class root rootnode
   root --> g1["TestLayererkennung (2)"]
@@ -1804,7 +1876,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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
Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht.grün
TestSuche
test_rechtsperson_als_rohwert
Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden.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

Tests für den Tab „Eigentümer“ – Flurstückssuche und Eigentümersuche (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 2TestDockWidgetTabs 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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
Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht.grün
TestSuche
test_rechtsperson_als_rohwert
Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden.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

Tests für den Tab „Eigentümer“ – Flurstückssuche, Eigentümersuche, Anzeige und Korrektur (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 3TestSitzung 7TestKorrektur 5TestKorrekturDatei 2TestDockWidgetTabs 1
flowchart TD
   root(["test_eigentuemer_tab.py"])
   class root rootnode
   root --> g1["TestFlurstueckSuchen (5)"]
@@ -1820,13 +1892,34 @@
   g2 --> t7("suchfehler_als_warnung")
   g2 --> t8("kein_treffer_ohne_namen_in_meldung")
   g2 --> t9("treffer_oeffnen_trefferliste_und_ersetzen_die_alte")
-  root --> g3["TestTrefferliste (2)"]
+  root --> g3["TestTrefferliste (3)"]
   class g3 groupnode
   g3 --> t10("trefferzeile_mit_hinweis")
   g3 --> t11("ausgewaehlte_eigentuemer")
-  root --> g4["TestDockWidgetTabs (1)"]
+  g3 --> t12("daten_anzeigen_gibt_auswahl_weiter")
+  root --> g4["TestSitzung (7)"]
   class g4 groupnode
-  g4 --> t12("tab_ist_im_dockwidget_eingetragen")
+  g4 --> t13("sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm")
+  g4 --> t14("auf_qgis_bildschirm_kein_vollbild")
+  g4 --> t15("daten_anzeigen_beginnt_sitzung_bei_bedarf")
+  g4 --> t16("daten_anzeigen_ohne_auswahl")
+  g4 --> t17("anzeige_leeren_laesst_fenster_offen")
+  g4 --> t18("sitzung_beenden_schliesst_fenster")
+  g4 --> t19("von_aussen_geschlossen_beendet_sitzung")
+  root --> g5["TestKorrektur (5)"]
+  class g5 groupnode
+  g5 --> t20("korrektur_wird_festgehalten_und_angezeigt")
+  g5 --> t21("ohne_aenderung_nichts_schreiben")
+  g5 --> t22("abbruch_nichts_schreiben")
+  g5 --> t23("schreibfehler_warnung_und_anzeige_unveraendert")
+  g5 --> t24("ohne_anzeige_keine_korrektur")
+  root --> g6["TestKorrekturDatei (2)"]
+  class g6 groupnode
+  g6 --> t25("vorschlag_neben_der_eigentuemerdatei")
+  g6 --> t26("ohne_csv_quelle_wird_gefragt")
+  root --> g7["TestDockWidgetTabs (1)"]
+  class g7 groupnode
+  g7 --> t27("tab_ist_im_dockwidget_eingetragen")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -1839,11 +1932,26 @@
   class t10 ok
   class t11 ok
   class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ 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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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 mit Anzeige-Rückruf 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, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ zeigt.grün
TestTrefferliste
test_ausgewaehlte_eigentuemer
Prüft, dass das Trefferfenster die in der Tabelle gewählten Einträge zurückgibt.grün
TestTrefferliste
test_daten_anzeigen_gibt_auswahl_weiter
Prüft, dass „Daten anzeigen“ die Auswahl der Trefferliste an den Tab übergibt.grün
TestSitzung
test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm
Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster auf dem gewählten Bildschirm öffnet, im Vollbild, wenn QGIS woanders liegt.grün
TestSitzung
test_auf_qgis_bildschirm_kein_vollbild
Prüft, dass das Anzeigefenster auf dem QGIS-Bildschirm als normales Fenster öffnet.grün
TestSitzung
test_daten_anzeigen_beginnt_sitzung_bei_bedarf
Prüft, dass „Daten anzeigen“ ohne laufende Sitzung diese beginnt und die Einträge zeigt; die Statuszeile nennt nur die Anzahl.grün
TestSitzung
test_daten_anzeigen_ohne_auswahl
Prüft, dass „Daten anzeigen“ ohne Auswahl nur einen Hinweis gibt und keine Sitzung beginnt.grün
TestSitzung
test_anzeige_leeren_laesst_fenster_offen
Prüft, dass „Anzeige leeren“ die Daten entfernt, das Fenster aber nicht schließt.grün
TestSitzung
test_sitzung_beenden_schliesst_fenster
Prüft, dass „Sitzung beenden“ das Anzeigefenster schließt und die angezeigten Daten vergisst.grün
TestSitzung
test_von_aussen_geschlossen_beendet_sitzung
Prüft, dass ein von außen geschlossenes Anzeigefenster (z. B. Alt+F4) die Sitzung im Tab beendet.grün
TestKorrektur
test_korrektur_wird_festgehalten_und_angezeigt
Prüft, dass eine Korrektur in die Korrektur-CSV geschrieben, sofort angezeigt und ohne Namen gemeldet wird.grün
TestKorrektur
test_ohne_aenderung_nichts_schreiben
Prüft, dass ein unverändert gespeicherter Dialog nichts schreibt.grün
TestKorrektur
test_abbruch_nichts_schreiben
Prüft, dass ein abgebrochener Korrekturdialog nichts schreibt.grün
TestKorrektur
test_schreibfehler_warnung_und_anzeige_unveraendert
Prüft, dass bei einem Schreibfehler gewarnt, die Anzeige nicht geändert und der Pfad beim nächsten Mal neu bestimmt wird.grün
TestKorrektur
test_ohne_anzeige_keine_korrektur
Prüft, dass ohne angezeigte Daten kein Korrekturdialog erscheint.grün
TestKorrekturDatei
test_vorschlag_neben_der_eigentuemerdatei
Prüft, dass die Korrektur-CSV ohne Rückfrage neben der Eigentümerdatei liegt, wenn der Ordner beschreibbar ist, und gemerkt wird.grün
TestKorrekturDatei
test_ohne_csv_quelle_wird_gefragt
Prüft, dass ohne verwertbare CSV-Quelle nach dem Speicherort gefragt wird; Abbrechen liefert keinen Pfad.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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
   root --> g1["TestFlurstueckAlkis (11)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index ecde425..0fbd313 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 64 Testdateien, 783 Testfälle – 740 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 638 von 783 (81 %).
+Gesamt: 3 Pakete, 66 Testdateien, 822 Testfälle – 779 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 677 von 822 (82 %).
 
 ## 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` | 16 | 249 | 207 | 34 | 8 | 147 von 249 (59 %) |
+| `sn_verfahrensgebiet` | 18 | 288 | 246 | 34 | 8 | 186 von 288 (65 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **64** | **783** | **740** | **35** | **8** | **638 von 783 (81 %)** |
+| **Summe** | **66** | **822** | **779** | **35** | **8** | **677 von 822 (82 %)** |
 
 ### Legende
 
@@ -1071,7 +1071,7 @@ Testgruppen:
 
 ## Paket `sn_verfahrensgebiet`
 
-Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 16 Testdateien mit 249 Testfällen (207 grün, 34 rot, 8 übersprungen).
+Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 18 Testdateien mit 288 Testfällen (246 grün, 34 rot, 8 übersprungen).
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
 
@@ -1128,6 +1128,55 @@ 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_anzeige.py`
+
+Worum es in der Datei geht: Tests für das Anzeigefenster des Eigentümer-Datenabgleichs (#56).
+
+Testgruppen:
+
+- `TestAnzeigetext` (6 Testfälle): — (Docstring fehlt)
+- `TestBildschirme` (6 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestAnzeigetext.test_name_anschrift_email` | Prüft, dass die Anzeige Name mit Anrede und Titel, die mehrzeilige Anschrift mit Ortsteil und die E-Mail zeigt. | grün |
+| `TestAnzeigetext.test_keine_internen_angaben` | Prüft, dass der Eigentümer weder Konten, Personen-IDs, Rechtsperson-Wert noch das Kennzeichen „verstorben“ sieht. | grün |
+| `TestAnzeigetext.test_fehlende_email_wird_genannt` | Prüft, dass eine fehlende E-Mail ausdrücklich als „keine angegeben“ erscheint. | grün |
+| `TestAnzeigetext.test_werte_werden_maskiert` | Prüft, dass Sonderzeichen aus den Daten maskiert werden und kein HTML einschleusen. | grün |
+| `TestAnzeigetext.test_mehrere_eintraege_getrennt` | Prüft, dass mehrere Einträge untereinander, durch eine Linie getrennt, erscheinen. | grün |
+| `TestAnzeigetext.test_leer_nur_platzhalter` | Prüft, dass die leere Anzeige nur den neutralen Platzhalter zeigt. | grün |
+| `TestBildschirme.test_vorgabe_ist_nicht_der_qgis_bildschirm` | Prüft, dass als Vorgabe der erste Bildschirm ohne QGIS gewählt wird, bei nur einem Bildschirm dieser. | grün |
+| `TestBildschirme.test_bildschirm_text` | Prüft, dass ein Bildschirm mit Nummer und Auflösung beschriftet wird. | grün |
+| `TestBildschirme.test_ohne_qt_anwendung_keine_bildschirme` | Prüft, dass ohne Qt-Anwendung (Mock-Modus) eine leere Bildschirmliste kommt statt eines Fehlers. | grün |
+| `TestBildschirme.test_vollbild_auf_gewaehltem_bildschirm` | Prüft, dass das Fenster im Vollbild dem gewählten Bildschirm zugeordnet und auf dessen Fläche gelegt wird. | grün |
+| `TestBildschirme.test_normales_fenster_mittig` | Prüft, dass auf dem QGIS-Bildschirm ein normales Fenster mittig im freien Bereich öffnet. | grün |
+| `TestBildschirme.test_ohne_bildschirm_einfach_zeigen` | Prüft, dass ohne bekannten Bildschirm das Fenster einfach gezeigt wird. | grün |
+
+### `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_korrektur.py`
+
+Worum es in der Datei geht: Tests für die Korrekturen beim Eigentümer-Datenabgleich (#56).
+
+Testgruppen:
+
+- `TestAenderungen` (4 Testfälle): — (Docstring fehlt)
+- `TestKorrekturPfad` (3 Testfälle): — (Docstring fehlt)
+- `TestSchreiben` (5 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestAenderungen.test_feldwerte` | Prüft, dass alle korrigierbaren Felder vorbelegt werden, mehrere E-Mails durch Komma getrennt. | grün |
+| `TestAenderungen.test_nur_echte_aenderungen` | Prüft, dass nur geänderte Felder zählen und Leerraum am Rand oder doppelte Leerzeichen keine Änderung sind. | grün |
+| `TestAenderungen.test_feld_leeren_ist_aenderung` | Prüft, dass das Leeren eines Feldes als Änderung zählt. | grün |
+| `TestAenderungen.test_wende_an` | Prüft, dass die Änderungen im angezeigten Eintrag landen und E-Mails wieder getrennt werden; Konten bleiben. | grün |
+| `TestKorrekturPfad.test_textdatei_provider` | Prüft, dass aus der Quelle eines Textdatei-Layers (file-URI, Leerzeichen kodiert) der Pfad neben der CSV wird. | grün |
+| `TestKorrekturPfad.test_ogr_provider` | Prüft, dass auch eine OGR-Quelle mit Layernamen den Pfad neben der CSV ergibt. | grün |
+| `TestKorrekturPfad.test_keine_csv` | Prüft, dass für Quellen ohne CSV-Datei kein Pfad vorgeschlagen wird. | grün |
+| `TestSchreiben.test_neue_datei_mit_kopfzeile_und_bom` | Prüft, dass eine neue Korrektur-CSV mit BOM, Kopfzeile und je Änderung einer Zeile mit Exportspaltennamen entsteht. | grün |
+| `TestSchreiben.test_anhaengen_ohne_zweite_kopfzeile` | Prüft, dass weitere Korrekturen angehängt werden, ohne Kopfzeile oder BOM zu wiederholen. | grün |
+| `TestSchreiben.test_ohne_aenderung_keine_datei` | Prüft, dass ohne Änderung keine Datei angelegt wird. | grün |
+| `TestSchreiben.test_schreibfehler_wird_weitergereicht` | Prüft, dass ein nicht schreibbarer Pfad als OSError beim Aufrufer ankommt. | grün |
+| `TestSchreiben.test_schreibt_nichts_auf_die_konsole` | Prüft, dass das Festhalten von Korrekturen keine Personendaten auf die Konsole schreibt (Datenschutz). | 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).
@@ -1156,13 +1205,16 @@ Testgruppen:
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_tab.py`
 
-Worum es in der Datei geht: Tests für den Tab „Eigentümer“ – Flurstückssuche und Eigentümersuche (#56).
+Worum es in der Datei geht: Tests für den Tab „Eigentümer“ – Flurstückssuche, Eigentümersuche, Anzeige und Korrektur (#56).
 
 Testgruppen:
 
 - `TestFlurstueckSuchen` (5 Testfälle): — (Docstring fehlt)
 - `TestEigentuemerSuchen` (4 Testfälle): — (Docstring fehlt)
-- `TestTrefferliste` (2 Testfälle): — (Docstring fehlt)
+- `TestTrefferliste` (3 Testfälle): — (Docstring fehlt)
+- `TestSitzung` (7 Testfälle): — (Docstring fehlt)
+- `TestKorrektur` (5 Testfälle): — (Docstring fehlt)
+- `TestKorrekturDatei` (2 Testfälle): — (Docstring fehlt)
 - `TestDockWidgetTabs` (1 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
@@ -1175,9 +1227,24 @@ Testgruppen:
 | `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 |
+| `TestEigentuemerSuchen.test_treffer_oeffnen_trefferliste_und_ersetzen_die_alte` | Prüft, dass Treffer in einer neuen Trefferliste mit Anzeige-Rückruf 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, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ zeigt. | grün |
 | `TestTrefferliste.test_ausgewaehlte_eigentuemer` | Prüft, dass das Trefferfenster die in der Tabelle gewählten Einträge zurückgibt. | grün |
+| `TestTrefferliste.test_daten_anzeigen_gibt_auswahl_weiter` | Prüft, dass „Daten anzeigen“ die Auswahl der Trefferliste an den Tab übergibt. | grün |
+| `TestSitzung.test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm` | Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster auf dem gewählten Bildschirm öffnet, im Vollbild, wenn QGIS woanders liegt. | grün |
+| `TestSitzung.test_auf_qgis_bildschirm_kein_vollbild` | Prüft, dass das Anzeigefenster auf dem QGIS-Bildschirm als normales Fenster öffnet. | grün |
+| `TestSitzung.test_daten_anzeigen_beginnt_sitzung_bei_bedarf` | Prüft, dass „Daten anzeigen“ ohne laufende Sitzung diese beginnt und die Einträge zeigt; die Statuszeile nennt nur die Anzahl. | grün |
+| `TestSitzung.test_daten_anzeigen_ohne_auswahl` | Prüft, dass „Daten anzeigen“ ohne Auswahl nur einen Hinweis gibt und keine Sitzung beginnt. | grün |
+| `TestSitzung.test_anzeige_leeren_laesst_fenster_offen` | Prüft, dass „Anzeige leeren“ die Daten entfernt, das Fenster aber nicht schließt. | grün |
+| `TestSitzung.test_sitzung_beenden_schliesst_fenster` | Prüft, dass „Sitzung beenden“ das Anzeigefenster schließt und die angezeigten Daten vergisst. | grün |
+| `TestSitzung.test_von_aussen_geschlossen_beendet_sitzung` | Prüft, dass ein von außen geschlossenes Anzeigefenster (z. B. Alt+F4) die Sitzung im Tab beendet. | grün |
+| `TestKorrektur.test_korrektur_wird_festgehalten_und_angezeigt` | Prüft, dass eine Korrektur in die Korrektur-CSV geschrieben, sofort angezeigt und ohne Namen gemeldet wird. | grün |
+| `TestKorrektur.test_ohne_aenderung_nichts_schreiben` | Prüft, dass ein unverändert gespeicherter Dialog nichts schreibt. | grün |
+| `TestKorrektur.test_abbruch_nichts_schreiben` | Prüft, dass ein abgebrochener Korrekturdialog nichts schreibt. | grün |
+| `TestKorrektur.test_schreibfehler_warnung_und_anzeige_unveraendert` | Prüft, dass bei einem Schreibfehler gewarnt, die Anzeige nicht geändert und der Pfad beim nächsten Mal neu bestimmt wird. | grün |
+| `TestKorrektur.test_ohne_anzeige_keine_korrektur` | Prüft, dass ohne angezeigte Daten kein Korrekturdialog erscheint. | grün |
+| `TestKorrekturDatei.test_vorschlag_neben_der_eigentuemerdatei` | Prüft, dass die Korrektur-CSV ohne Rückfrage neben der Eigentümerdatei liegt, wenn der Ordner beschreibbar ist, und gemerkt wird. | grün |
+| `TestKorrekturDatei.test_ohne_csv_quelle_wird_gefragt` | Prüft, dass ohne verwertbare CSV-Quelle nach dem Speicherort gefragt wird; Abbrechen liefert keinen Pfad. | 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`
@@ -1652,7 +1719,7 @@ dann verschwindet er hier von selbst.
 
 Keine Lücken.
 
-### `sn_verfahrensgebiet` – 102 von 249 Testfällen ohne Beschreibung
+### `sn_verfahrensgebiet` – 102 von 288 Testfällen ohne Beschreibung
 
 #### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` (25 von 25)
 
diff --git a/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md b/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md
new file mode 100644
index 0000000..245af2f
--- /dev/null
+++ b/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md
@@ -0,0 +1,93 @@
+# Testkonzept: Anzeige für den Eigentümer und Korrekturen
+
+Plugin: `sn_verfahrensgebiet` · Stand: 06.10.2026 · Pflege: bei jeder Änderung an diesem Ablauf
+
+## Worum es fachlich geht
+
+Im Termin sitzt der Eigentümer vor einem zweiten Bildschirm. Dort sieht er nur seine eigenen gespeicherten Daten:
+Name, Anschrift und E-Mail. Den QGIS-Bildschirm mit Trefferliste, Konten und anderen Personen sieht er nicht.
+Die Sachbearbeitung beginnt zu Beginn der Termine eine Sitzung. Für jeden Eigentümer wählt sie in der Trefferliste
+die passenden Einträge und zeigt sie an. Stimmt etwas nicht, korrigiert sie es im Plugin. Der Eigentümer sieht die
+Korrektur sofort. Vor dem nächsten Eigentümer leert sie die Anzeige, ohne das Fenster zu schließen. Am Ende beendet
+sie die Sitzung.
+
+Die Eigentümerdatei selbst wird nie verändert. Jede Korrektur landet als eigene Zeile in einer Korrektur-CSV neben
+der Eigentümerdatei und kann später in das Fachverfahren übernommen werden.
+
+Die Suche bis zur Trefferliste beschreibt [`verfahrensgebiet-eigentuemersuche.md`](verfahrensgebiet-eigentuemersuche.md).
+
+## Der Ablauf
+
+```mermaid
+flowchart TD
+    A["1 Bildschirm wählen,
Sitzung beginnen"] --> B["2 Anzeigefenster öffnet leer
(zweiter Bildschirm: Vollbild)"] + T["Trefferliste: Einträge wählen,
„Daten anzeigen“"] --> C{"Sitzung läuft?"} + C -->|nein| A + C -->|ja| D["3 Eigentümer sieht Name,
Anschrift, E-Mail"] + B --> D + D --> E{"4 Daten richtig?"} + E -->|nein| F["5 Korrigieren: Änderung
in Korrektur-CSV, sofort angezeigt"] + F --> E + E -->|ja| G["6 Anzeige leeren
(Fenster bleibt offen)"] + G --> T + G --> H["7 Sitzung beenden:
Fenster schließt"] + + style G fill:#e9f5e9,stroke:#2b8a3e +``` + +## Was geprüft wird + +Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_anzeige.py` (Anzeigetext, Bildschirme), +`tests/test_eigentuemer_korrektur.py` (Korrekturlogik, Korrektur-CSV) und `tests/test_eigentuemer_tab.py` +(Sitzung, Korrektur im Tab). + +| # | Schritt | Wird geprüft durch | Ebene | +|---|---|---|---| +| 1 | Vorgabe: erster Bildschirm ohne QGIS | `test_vorgabe_ist_nicht_der_qgis_bildschirm`, `test_bildschirm_text` | Mock | +| 1 | Ohne Qt-Anwendung keine Bildschirme | `test_ohne_qt_anwendung_keine_bildschirme` | Mock | +| 2 | Sitzung beginnen: leer, Vollbild auf zweitem Bildschirm | `test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm`, `test_vollbild_auf_gewaehltem_bildschirm` | Mock | +| 2 | Auf dem QGIS-Bildschirm normales Fenster | `test_auf_qgis_bildschirm_kein_vollbild`, `test_normales_fenster_mittig`, `test_ohne_bildschirm_einfach_zeigen` | Mock | +| 3 | „Daten anzeigen“ beginnt bei Bedarf die Sitzung | `test_daten_anzeigen_beginnt_sitzung_bei_bedarf`, `test_daten_anzeigen_gibt_auswahl_weiter` | Mock | +| 3 | Ohne Auswahl nur ein Hinweis | `test_daten_anzeigen_ohne_auswahl` | Mock | +| 3 | Inhalt: Name, Anschrift, E-Mail; mehrere Einträge | `test_name_anschrift_email`, `test_mehrere_eintraege_getrennt` | Mock | +| 3 | Keine internen Angaben für den Eigentümer | `test_keine_internen_angaben` | Mock | +| 3 | Fehlende E-Mail ausdrücklich nennen | `test_fehlende_email_wird_genannt` | Mock | +| 5 | Nur echte Änderungen, Leeren zählt | `test_nur_echte_aenderungen`, `test_feld_leeren_ist_aenderung`, `test_feldwerte` | Mock | +| 5 | Korrektur festhalten und sofort anzeigen | `test_korrektur_wird_festgehalten_und_angezeigt`, `test_wende_an` | Mock | +| 5 | Unverändert oder abgebrochen: nichts schreiben | `test_ohne_aenderung_nichts_schreiben`, `test_abbruch_nichts_schreiben` | Mock | +| 5 | Korrektur-CSV: Kopfzeile, BOM, anhängen | `test_neue_datei_mit_kopfzeile_und_bom`, `test_anhaengen_ohne_zweite_kopfzeile`, `test_ohne_aenderung_keine_datei` | Mock | +| 5 | Pfad neben der Eigentümerdatei, sonst Rückfrage | `test_textdatei_provider`, `test_ogr_provider`, `test_keine_csv`, `test_vorschlag_neben_der_eigentuemerdatei`, `test_ohne_csv_quelle_wird_gefragt` | Mock | +| 5 | Schreibfehler | `test_schreibfehler_wird_weitergereicht`, `test_schreibfehler_warnung_und_anzeige_unveraendert` | Mock | +| 5 | Ohne Anzeige keine Korrektur | `test_ohne_anzeige_keine_korrektur` | Mock | +| 6 | Anzeige leeren, Fenster bleibt offen | `test_anzeige_leeren_laesst_fenster_offen`, `test_leer_nur_platzhalter` | Mock | +| 7 | Sitzung beenden bzw. Fenster von außen geschlossen | `test_sitzung_beenden_schliesst_fenster`, `test_von_aussen_geschlossen_beendet_sitzung` | Mock | +| — | Keine Personendaten auf der Konsole | `test_schreibt_nichts_auf_die_konsole` | Mock | +| — | Sieht das in QGIS richtig aus, auf zwei Bildschirmen? | 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 | +|---|---|---| +| Nur ein Bildschirm (Notebook) | Termin ohne zweiten Monitor, Ausprobieren | `test_auf_qgis_bildschirm_kein_vollbild` | +| Eheleute oder Miteigentümer erscheinen gemeinsam | mehrere Einträge auf einmal anzeigen und einzeln korrigieren | `test_mehrere_eintraege_getrennt`, `test_korrektur_wird_festgehalten_und_angezeigt` | +| Eigentümer hat keine E-Mail hinterlegt | häufig; soll beim Termin ergänzt werden | `test_fehlende_email_wird_genannt` | +| Neue Anschrift mit Komma („Am Anger 3, Haus B“) | Zusatz zur Hausnummer | `test_neue_datei_mit_kopfzeile_und_bom` | +| Eigentümerdatei liegt in einem schreibgeschützten Ordner | Export auf einem Netzlaufwerk ohne Schreibrecht | `test_ohne_csv_quelle_wird_gefragt` (Rückfrage), `test_schreibfehler_warnung_und_anzeige_unveraendert` | +| Sonderzeichen oder spitze Klammern in den Daten | Firmennamen | `test_werte_werden_maskiert` | + +## Bewusst nicht getestet + +| Was | Warum | Wie stattdessen abgesichert | +|---|---|---| +| Fenster tatsächlich auf dem zweiten Bildschirm im Vollbild | braucht echte Bildschirme; im Mock-Modus gibt es keine | Aufrufreihenfolge mit Attrappe geprüft, manueller Test mit zwei Bildschirmen | +| Korrekturdialog mit echten Eingabefeldern | im Mock-Modus gibt es keine Widgets; die Vorbelegung (`feldwerte`) und die Auswertung sind als reine Funktionen getestet | manueller Test | +| Schriftgröße und Lesbarkeit der Anzeige | Geschmack und Bildschirmgröße | manueller Test im Termin | + +## Bekannte Lücken + +| Lücke | Auswirkung | Issue | +|---|---|---| +| Der manuelle Test mit zwei Bildschirmen steht aus, auch mit unterschiedlicher Skalierung (z. B. Notebook 150 %, Monitor 100 %) | Das Fenster könnte auf dem falschen Bildschirm oder nicht ganz im Vollbild erscheinen | Verfahrensgebiet #56 | +| Eine neue Suche leert die Anzeige nicht von selbst | Wer „Anzeige leeren“ vergisst, zeigt dem nächsten Eigentümer noch die vorherigen Daten | bewusst so (Bearbeiter entscheidet); bei Bedarf eigenes Issue | diff --git a/docs/tests/verfahrensgebiet-eigentuemersuche.md b/docs/tests/verfahrensgebiet-eigentuemersuche.md index 8bf5ac0..2db9cd4 100644 --- a/docs/tests/verfahrensgebiet-eigentuemersuche.md +++ b/docs/tests/verfahrensgebiet-eigentuemersuche.md @@ -13,8 +13,8 @@ Rechtspersonen sind in der Liste gekennzeichnet: verstorben als Hinweis (`PER_VERSTORBEN` = 1), die Art der Person als Rohwert von `PER_RECHTSPERSON` (0, 1, 5 oder 9; die Bedeutung der Werte wird später entschlüsselt). -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. +Die Trefferliste sieht nur die Sachbearbeitung. Wie die gewählten Einträge dem Eigentümer angezeigt und korrigiert +werden, beschreibt [`verfahrensgebiet-eigentuemer-anzeige.md`](verfahrensgebiet-eigentuemer-anzeige.md). ## Der Ablauf diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md index eafcf18..0f96f93 100644 --- a/docs/wissen/fallstricke.md +++ b/docs/wissen/fallstricke.md @@ -102,7 +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) +- **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. Die Korrektur-CSV (`_korrekturen.csv`) enthält ebenfalls Personendaten und liegt deshalb neben dem Export, nicht im Projekt- oder Plugin-Ordner. Das Anzeigefenster für den Eigentümer zeigt nur Name, Anschrift und E-Mail; Konten, Personen-IDs und Kennzeichen bleiben im Tab. (06.10.2026) +- **Fenster auf einem bestimmten Bildschirm öffnen (Qt):** Erst `winId()` aufrufen, damit es ein natives Fenster gibt, dann `windowHandle().setScreen(bildschirm)`, `setGeometry(bildschirm.geometry())` und `showFullScreen()`. Nur `move()` auf die Koordinaten reicht unter Windows nicht zuverlässig, wenn die Bildschirme unterschiedlich skaliert sind. Die Liste der Bildschirme liefert `QCoreApplication.instance().screens()` (die QGIS-Anwendung ist eine `QApplication`); im Mock-Modus gibt es sie nicht. Umgesetzt in `sn_verfahrensgebiet/ui/eigentuemer_anzeige.py` (Verfahrensgebiet #56), in QGIS noch zu bestätigen. (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. From abac5a015cf4de56191f4e619ff7941a9081dccb Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 6 Oct 2026 13:44:18 +0200 Subject: [PATCH 20/20] Anzeigefenster ohne Vollbild: Testkonzept und Fallstrick (Verfahrensgebiet #56) Befund aus dem QGIS-Test (Fenster immer auf dem Hauptbildschirm, im Vollbild nicht zu verlassen) in fallstricke.md festgehalten; Testkonzept, Modulkarte und Testkatalog an das Fenster mit Rahmen und die Pfadanzeige der Korrektur-Datei angepasst. Co-Authored-By: Claude Opus 5.5 --- docs/architektur/modulkarte.md | 4 +- docs/tests/testkatalog.html | 70 ++++++++++--------- docs/tests/testkatalog.md | 34 ++++----- .../verfahrensgebiet-eigentuemer-anzeige.md | 20 +++--- docs/wissen/fallstricke.md | 2 +- 5 files changed, 68 insertions(+), 62 deletions(-) diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md index fbdc1c9..c9f43b0 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -86,9 +86,9 @@ 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` | 411 | Tab „Eigentümer“ (#56): Flurstückssuche, Eigentümersuche, Anzeige für den Eigentümer (Bildschirm, Sitzung, „Anzeige leeren“) und Korrekturen | +| `ui/tabs/eigentuemer_tab.py` | 440 | Tab „Eigentümer“ (#56): Flurstückssuche, Eigentümersuche, Anzeige für den Eigentümer (Sitzung, „Anzeige leeren“, Lage des Fensters merken) und Korrekturen mit Pfadanzeige | | `ui/eigentuemer_treffer.py` | 83 | Nicht modale Trefferliste der Eigentümersuche mit Mehrfachauswahl und „Daten anzeigen“ (#56) | -| `ui/eigentuemer_anzeige.py` | 178 | Anzeigefenster für den Eigentümer (nur Name, Anschrift, E-Mail; maskiertes HTML), Bildschirmwahl, Vollbild auf dem zweiten Bildschirm (#56) | +| `ui/eigentuemer_anzeige.py` | 112 | Anzeigefenster für den Eigentümer (nur Name, Anschrift, E-Mail; maskiertes HTML), immer mit Rahmen, nie Vollbild (#56) | | `ui/eigentuemer_korrektur_dialog.py` | 79 | Korrekturdialog für den Bearbeiter (#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 | diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index 16d2eda..8915aab 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,8 +119,8 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
822Testfälle
-
779grün
+
823Testfälle
+
780grün
35rot (Baseline)
82%mit Docstring
@@ -128,12 +128,12 @@
Verteilung je Paket
sn_basis43 Dateien · 490 Testfälle
-
sn_verfahrensgebiet18 Dateien · 288 Testfälle
+
sn_verfahrensgebiet18 Dateien · 289 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -1675,9 +1675,9 @@

sn_verfahrensgebiet

-

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 18 Testdateien · 288 Testfälle

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 18 Testdateien · 289 Testfälle

- @@ -1765,7 +1765,7 @@ classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420; classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20; classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08; - classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für das Anzeigefenster des Eigentümer-Datenabgleichs (#56).

TestAnzeigetext 6TestBildschirme 6
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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

Tests für das Anzeigefenster des Eigentümer-Datenabgleichs (#56).

TestAnzeigetext 6TestFensterOeffnen 3
flowchart TD
   root(["test_eigentuemer_anzeige.py"])
   class root rootnode
   root --> g1["TestAnzeigetext (6)"]
@@ -1776,14 +1776,11 @@
   g1 --> t4("werte_werden_maskiert")
   g1 --> t5("mehrere_eintraege_getrennt")
   g1 --> t6("leer_nur_platzhalter")
-  root --> g2["TestBildschirme (6)"]
+  root --> g2["TestFensterOeffnen (3)"]
   class g2 groupnode
-  g2 --> t7("vorgabe_ist_nicht_der_qgis_bildschirm")
-  g2 --> t8("bildschirm_text")
-  g2 --> t9("ohne_qt_anwendung_keine_bildschirme")
-  g2 --> t10("vollbild_auf_gewaehltem_bildschirm")
-  g2 --> t11("normales_fenster_mittig")
-  g2 --> t12("ohne_bildschirm_einfach_zeigen")
+  g2 --> t7("normales_fenster_in_standardgroesse")
+  g2 --> t8("gemerkte_lage_wird_wiederhergestellt")
+  g2 --> t9("unbrauchbare_lage_ergibt_standardgroesse")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -1793,14 +1790,11 @@
   class t7 ok
   class t8 ok
   class t9 ok
-  class t10 ok
-  class t11 ok
-  class t12 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestAnzeigetext
test_name_anschrift_email
Prüft, dass die Anzeige Name mit Anrede und Titel, die mehrzeilige Anschrift mit Ortsteil und die E-Mail zeigt.grün
TestAnzeigetext
test_keine_internen_angaben
Prüft, dass der Eigentümer weder Konten, Personen-IDs, Rechtsperson-Wert noch das Kennzeichen „verstorben“ sieht.grün
TestAnzeigetext
test_fehlende_email_wird_genannt
Prüft, dass eine fehlende E-Mail ausdrücklich als „keine angegeben“ erscheint.grün
TestAnzeigetext
test_werte_werden_maskiert
Prüft, dass Sonderzeichen aus den Daten maskiert werden und kein HTML einschleusen.grün
TestAnzeigetext
test_mehrere_eintraege_getrennt
Prüft, dass mehrere Einträge untereinander, durch eine Linie getrennt, erscheinen.grün
TestAnzeigetext
test_leer_nur_platzhalter
Prüft, dass die leere Anzeige nur den neutralen Platzhalter zeigt.grün
TestBildschirme
test_vorgabe_ist_nicht_der_qgis_bildschirm
Prüft, dass als Vorgabe der erste Bildschirm ohne QGIS gewählt wird, bei nur einem Bildschirm dieser.grün
TestBildschirme
test_bildschirm_text
Prüft, dass ein Bildschirm mit Nummer und Auflösung beschriftet wird.grün
TestBildschirme
test_ohne_qt_anwendung_keine_bildschirme
Prüft, dass ohne Qt-Anwendung (Mock-Modus) eine leere Bildschirmliste kommt statt eines Fehlers.grün
TestBildschirme
test_vollbild_auf_gewaehltem_bildschirm
Prüft, dass das Fenster im Vollbild dem gewählten Bildschirm zugeordnet und auf dessen Fläche gelegt wird.grün
TestBildschirme
test_normales_fenster_mittig
Prüft, dass auf dem QGIS-Bildschirm ein normales Fenster mittig im freien Bereich öffnet.grün
TestBildschirme
test_ohne_bildschirm_einfach_zeigen
Prüft, dass ohne bekannten Bildschirm das Fenster einfach gezeigt wird.grün

Tests für die Korrekturen beim Eigentümer-Datenabgleich (#56).

TestAenderungen 4TestKorrekturPfad 3TestSchreiben 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestAnzeigetext
test_name_anschrift_email
Prüft, dass die Anzeige Name mit Anrede und Titel, die mehrzeilige Anschrift mit Ortsteil und die E-Mail zeigt.grün
TestAnzeigetext
test_keine_internen_angaben
Prüft, dass der Eigentümer weder Konten, Personen-IDs, Rechtsperson-Wert noch das Kennzeichen „verstorben“ sieht.grün
TestAnzeigetext
test_fehlende_email_wird_genannt
Prüft, dass eine fehlende E-Mail ausdrücklich als „keine angegeben“ erscheint.grün
TestAnzeigetext
test_werte_werden_maskiert
Prüft, dass Sonderzeichen aus den Daten maskiert werden und kein HTML einschleusen.grün
TestAnzeigetext
test_mehrere_eintraege_getrennt
Prüft, dass mehrere Einträge untereinander, durch eine Linie getrennt, erscheinen.grün
TestAnzeigetext
test_leer_nur_platzhalter
Prüft, dass die leere Anzeige nur den neutralen Platzhalter zeigt.grün
TestFensterOeffnen
test_normales_fenster_in_standardgroesse
Prüft, dass das Anzeigefenster ohne gemerkte Lage als normales Fenster mit Rahmen in Standardgröße öffnet, nie im Vollbild.grün
TestFensterOeffnen
test_gemerkte_lage_wird_wiederhergestellt
Prüft, dass eine gemerkte Größe und Lage der letzten Sitzung wiederhergestellt wird.grün
TestFensterOeffnen
test_unbrauchbare_lage_ergibt_standardgroesse
Prüft, dass eine nicht wiederherstellbare Lage auf die Standardgröße zurückfällt.grün

Tests für die Korrekturen beim Eigentümer-Datenabgleich (#56).

TestAenderungen 4TestKorrekturPfad 3TestSchreiben 5
flowchart TD
   root(["test_eigentuemer_korrektur.py"])
   class root rootnode
   root --> g1["TestAenderungen (4)"]
@@ -1876,7 +1870,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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
Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht.grün
TestSuche
test_rechtsperson_als_rohwert
Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden.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

Tests für den Tab „Eigentümer“ – Flurstückssuche, Eigentümersuche, Anzeige und Korrektur (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 3TestSitzung 7TestKorrektur 5TestKorrekturDatei 2TestDockWidgetTabs 1
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
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
Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht.grün
TestSuche
test_rechtsperson_als_rohwert
Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden.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

Tests für den Tab „Eigentümer“ – Flurstückssuche, Eigentümersuche, Anzeige und Korrektur (#56).

TestFlurstueckSuchen 5TestEigentuemerSuchen 4TestTrefferliste 3TestSitzung 7TestKorrekturLabel 4TestKorrektur 5TestKorrekturDatei 2TestDockWidgetTabs 1
flowchart TD
   root(["test_eigentuemer_tab.py"])
   class root rootnode
   root --> g1["TestFlurstueckSuchen (5)"]
@@ -1899,27 +1893,33 @@
   g3 --> t12("daten_anzeigen_gibt_auswahl_weiter")
   root --> g4["TestSitzung (7)"]
   class g4 groupnode
-  g4 --> t13("sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm")
-  g4 --> t14("auf_qgis_bildschirm_kein_vollbild")
+  g4 --> t13("sitzung_beginnen_oeffnet_leeres_fenster_mit_rahmen")
+  g4 --> t14("lage_gilt_fuer_naechste_sitzung")
   g4 --> t15("daten_anzeigen_beginnt_sitzung_bei_bedarf")
   g4 --> t16("daten_anzeigen_ohne_auswahl")
   g4 --> t17("anzeige_leeren_laesst_fenster_offen")
   g4 --> t18("sitzung_beenden_schliesst_fenster")
   g4 --> t19("von_aussen_geschlossen_beendet_sitzung")
-  root --> g5["TestKorrektur (5)"]
+  root --> g5["TestKorrekturLabel (4)"]
   class g5 groupnode
-  g5 --> t20("korrektur_wird_festgehalten_und_angezeigt")
-  g5 --> t21("ohne_aenderung_nichts_schreiben")
-  g5 --> t22("abbruch_nichts_schreiben")
-  g5 --> t23("schreibfehler_warnung_und_anzeige_unveraendert")
-  g5 --> t24("ohne_anzeige_keine_korrektur")
-  root --> g6["TestKorrekturDatei (2)"]
+  g5 --> t20("ohne_eigentuemerdatei")
+  g5 --> t21("pfad_neben_der_eigentuemerdatei")
+  g5 --> t22("ohne_csv_quelle_wird_abgefragt")
+  g5 --> t23("gewaehlter_pfad_wird_angezeigt")
+  root --> g6["TestKorrektur (5)"]
   class g6 groupnode
-  g6 --> t25("vorschlag_neben_der_eigentuemerdatei")
-  g6 --> t26("ohne_csv_quelle_wird_gefragt")
-  root --> g7["TestDockWidgetTabs (1)"]
+  g6 --> t24("korrektur_wird_festgehalten_und_angezeigt")
+  g6 --> t25("ohne_aenderung_nichts_schreiben")
+  g6 --> t26("abbruch_nichts_schreiben")
+  g6 --> t27("schreibfehler_warnung_und_anzeige_unveraendert")
+  g6 --> t28("ohne_anzeige_keine_korrektur")
+  root --> g7["TestKorrekturDatei (2)"]
   class g7 groupnode
-  g7 --> t27("tab_ist_im_dockwidget_eingetragen")
+  g7 --> t29("vorschlag_neben_der_eigentuemerdatei")
+  g7 --> t30("ohne_csv_quelle_wird_gefragt")
+  root --> g8["TestDockWidgetTabs (1)"]
+  class g8 groupnode
+  g8 --> t31("tab_ist_im_dockwidget_eingetragen")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -1947,11 +1947,15 @@
   class t25 ok
   class t26 ok
   class t27 ok
+  class t28 ok
+  class t29 ok
+  class t30 ok
+  class t31 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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 mit Anzeige-Rückruf 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, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ zeigt.grün
TestTrefferliste
test_ausgewaehlte_eigentuemer
Prüft, dass das Trefferfenster die in der Tabelle gewählten Einträge zurückgibt.grün
TestTrefferliste
test_daten_anzeigen_gibt_auswahl_weiter
Prüft, dass „Daten anzeigen“ die Auswahl der Trefferliste an den Tab übergibt.grün
TestSitzung
test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm
Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster auf dem gewählten Bildschirm öffnet, im Vollbild, wenn QGIS woanders liegt.grün
TestSitzung
test_auf_qgis_bildschirm_kein_vollbild
Prüft, dass das Anzeigefenster auf dem QGIS-Bildschirm als normales Fenster öffnet.grün
TestSitzung
test_daten_anzeigen_beginnt_sitzung_bei_bedarf
Prüft, dass „Daten anzeigen“ ohne laufende Sitzung diese beginnt und die Einträge zeigt; die Statuszeile nennt nur die Anzahl.grün
TestSitzung
test_daten_anzeigen_ohne_auswahl
Prüft, dass „Daten anzeigen“ ohne Auswahl nur einen Hinweis gibt und keine Sitzung beginnt.grün
TestSitzung
test_anzeige_leeren_laesst_fenster_offen
Prüft, dass „Anzeige leeren“ die Daten entfernt, das Fenster aber nicht schließt.grün
TestSitzung
test_sitzung_beenden_schliesst_fenster
Prüft, dass „Sitzung beenden“ das Anzeigefenster schließt und die angezeigten Daten vergisst.grün
TestSitzung
test_von_aussen_geschlossen_beendet_sitzung
Prüft, dass ein von außen geschlossenes Anzeigefenster (z. B. Alt+F4) die Sitzung im Tab beendet.grün
TestKorrektur
test_korrektur_wird_festgehalten_und_angezeigt
Prüft, dass eine Korrektur in die Korrektur-CSV geschrieben, sofort angezeigt und ohne Namen gemeldet wird.grün
TestKorrektur
test_ohne_aenderung_nichts_schreiben
Prüft, dass ein unverändert gespeicherter Dialog nichts schreibt.grün
TestKorrektur
test_abbruch_nichts_schreiben
Prüft, dass ein abgebrochener Korrekturdialog nichts schreibt.grün
TestKorrektur
test_schreibfehler_warnung_und_anzeige_unveraendert
Prüft, dass bei einem Schreibfehler gewarnt, die Anzeige nicht geändert und der Pfad beim nächsten Mal neu bestimmt wird.grün
TestKorrektur
test_ohne_anzeige_keine_korrektur
Prüft, dass ohne angezeigte Daten kein Korrekturdialog erscheint.grün
TestKorrekturDatei
test_vorschlag_neben_der_eigentuemerdatei
Prüft, dass die Korrektur-CSV ohne Rückfrage neben der Eigentümerdatei liegt, wenn der Ordner beschreibbar ist, und gemerkt wird.grün
TestKorrekturDatei
test_ohne_csv_quelle_wird_gefragt
Prüft, dass ohne verwertbare CSV-Quelle nach dem Speicherort gefragt wird; Abbrechen liefert keinen Pfad.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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckSuchen
test_ohne_flurstueckslayer_erscheint_hinweisdialog
Prüft, dass ohne Layer „Flurstücke“ ein Dialog zum Laden von Verfahrensgebiet und Flurstücken erscheint.grün
TestFlurstueckSuchen
test_eingabefehler_als_warnung
Prüft, dass eine ungültige Eingabe als Warnung in der Meldungsleiste erscheint, nicht als Dialog.grün
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 mit Anzeige-Rückruf 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, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ zeigt.grün
TestTrefferliste
test_ausgewaehlte_eigentuemer
Prüft, dass das Trefferfenster die in der Tabelle gewählten Einträge zurückgibt.grün
TestTrefferliste
test_daten_anzeigen_gibt_auswahl_weiter
Prüft, dass „Daten anzeigen“ die Auswahl der Trefferliste an den Tab übergibt.grün
TestSitzung
test_sitzung_beginnen_oeffnet_leeres_fenster_mit_rahmen
Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster als normales Fenster öffnet (ohne gemerkte Lage beim ersten Mal).grün
TestSitzung
test_lage_gilt_fuer_naechste_sitzung
Prüft, dass Größe und Lage des Fensters beim Beenden gemerkt und in der nächsten Sitzung wiederverwendet werden.grün
TestSitzung
test_daten_anzeigen_beginnt_sitzung_bei_bedarf
Prüft, dass „Daten anzeigen“ ohne laufende Sitzung diese beginnt und die Einträge zeigt; die Statuszeile nennt nur die Anzahl.grün
TestSitzung
test_daten_anzeigen_ohne_auswahl
Prüft, dass „Daten anzeigen“ ohne Auswahl nur einen Hinweis gibt und keine Sitzung beginnt.grün
TestSitzung
test_anzeige_leeren_laesst_fenster_offen
Prüft, dass „Anzeige leeren“ die Daten entfernt, das Fenster aber nicht schließt.grün
TestSitzung
test_sitzung_beenden_schliesst_fenster
Prüft, dass „Sitzung beenden“ das Anzeigefenster schließt und die angezeigten Daten vergisst.grün
TestSitzung
test_von_aussen_geschlossen_beendet_sitzung
Prüft, dass ein über den Fensterrahmen geschlossenes Anzeigefenster die Sitzung beendet und seine Lage gemerkt wird.grün
TestKorrekturLabel
test_ohne_eigentuemerdatei
Prüft, dass ohne Eigentümerdatei unter den Knöpfen steht, dass keine gewählt ist.grün
TestKorrekturLabel
test_pfad_neben_der_eigentuemerdatei
Prüft, dass der Pfad der Korrektur-CSV angezeigt wird, mit Hinweis, solange sie noch nicht angelegt ist.grün
TestKorrekturLabel
test_ohne_csv_quelle_wird_abgefragt
Prüft, dass bei einer Quelle ohne CSV angekündigt wird, dass der Speicherort beim ersten Speichern abgefragt wird.grün
TestKorrekturLabel
test_gewaehlter_pfad_wird_angezeigt
Prüft, dass ein einmal gewählter Speicherort angezeigt wird.grün
TestKorrektur
test_korrektur_wird_festgehalten_und_angezeigt
Prüft, dass eine Korrektur in die Korrektur-CSV geschrieben, sofort angezeigt und ohne Namen gemeldet wird.grün
TestKorrektur
test_ohne_aenderung_nichts_schreiben
Prüft, dass ein unverändert gespeicherter Dialog nichts schreibt.grün
TestKorrektur
test_abbruch_nichts_schreiben
Prüft, dass ein abgebrochener Korrekturdialog nichts schreibt.grün
TestKorrektur
test_schreibfehler_warnung_und_anzeige_unveraendert
Prüft, dass bei einem Schreibfehler gewarnt, die Anzeige nicht geändert und der Pfad beim nächsten Mal neu bestimmt wird.grün
TestKorrektur
test_ohne_anzeige_keine_korrektur
Prüft, dass ohne angezeigte Daten kein Korrekturdialog erscheint.grün
TestKorrekturDatei
test_vorschlag_neben_der_eigentuemerdatei
Prüft, dass die Korrektur-CSV ohne Rückfrage neben der Eigentümerdatei liegt, wenn der Ordner beschreibbar ist, und gemerkt wird.grün
TestKorrekturDatei
test_ohne_csv_quelle_wird_gefragt
Prüft, dass ohne verwertbare CSV-Quelle nach dem Speicherort gefragt wird; Abbrechen liefert keinen Pfad.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

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
   root --> g1["TestFlurstueckAlkis (11)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 0fbd313..4b806ef 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 66 Testdateien, 822 Testfälle – 779 grün, 35 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 677 von 822 (82 %).
+Gesamt: 3 Pakete, 66 Testdateien, 823 Testfälle – 780 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 678 von 823 (82 %).
 
 ## 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` | 18 | 288 | 246 | 34 | 8 | 186 von 288 (65 %) |
+| `sn_verfahrensgebiet` | 18 | 289 | 247 | 34 | 8 | 187 von 289 (65 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **66** | **822** | **779** | **35** | **8** | **677 von 822 (82 %)** |
+| **Summe** | **66** | **823** | **780** | **35** | **8** | **678 von 823 (82 %)** |
 
 ### Legende
 
@@ -1071,7 +1071,7 @@ Testgruppen:
 
 ## Paket `sn_verfahrensgebiet`
 
-Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 18 Testdateien mit 288 Testfällen (246 grün, 34 rot, 8 übersprungen).
+Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 18 Testdateien mit 289 Testfällen (247 grün, 34 rot, 8 übersprungen).
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
 
@@ -1135,7 +1135,7 @@ Worum es in der Datei geht: Tests für das Anzeigefenster des Eigentümer-Datena
 Testgruppen:
 
 - `TestAnzeigetext` (6 Testfälle): — (Docstring fehlt)
-- `TestBildschirme` (6 Testfälle): — (Docstring fehlt)
+- `TestFensterOeffnen` (3 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -1145,12 +1145,9 @@ Testgruppen:
 | `TestAnzeigetext.test_werte_werden_maskiert` | Prüft, dass Sonderzeichen aus den Daten maskiert werden und kein HTML einschleusen. | grün |
 | `TestAnzeigetext.test_mehrere_eintraege_getrennt` | Prüft, dass mehrere Einträge untereinander, durch eine Linie getrennt, erscheinen. | grün |
 | `TestAnzeigetext.test_leer_nur_platzhalter` | Prüft, dass die leere Anzeige nur den neutralen Platzhalter zeigt. | grün |
-| `TestBildschirme.test_vorgabe_ist_nicht_der_qgis_bildschirm` | Prüft, dass als Vorgabe der erste Bildschirm ohne QGIS gewählt wird, bei nur einem Bildschirm dieser. | grün |
-| `TestBildschirme.test_bildschirm_text` | Prüft, dass ein Bildschirm mit Nummer und Auflösung beschriftet wird. | grün |
-| `TestBildschirme.test_ohne_qt_anwendung_keine_bildschirme` | Prüft, dass ohne Qt-Anwendung (Mock-Modus) eine leere Bildschirmliste kommt statt eines Fehlers. | grün |
-| `TestBildschirme.test_vollbild_auf_gewaehltem_bildschirm` | Prüft, dass das Fenster im Vollbild dem gewählten Bildschirm zugeordnet und auf dessen Fläche gelegt wird. | grün |
-| `TestBildschirme.test_normales_fenster_mittig` | Prüft, dass auf dem QGIS-Bildschirm ein normales Fenster mittig im freien Bereich öffnet. | grün |
-| `TestBildschirme.test_ohne_bildschirm_einfach_zeigen` | Prüft, dass ohne bekannten Bildschirm das Fenster einfach gezeigt wird. | grün |
+| `TestFensterOeffnen.test_normales_fenster_in_standardgroesse` | Prüft, dass das Anzeigefenster ohne gemerkte Lage als normales Fenster mit Rahmen in Standardgröße öffnet, nie im Vollbild. | grün |
+| `TestFensterOeffnen.test_gemerkte_lage_wird_wiederhergestellt` | Prüft, dass eine gemerkte Größe und Lage der letzten Sitzung wiederhergestellt wird. | grün |
+| `TestFensterOeffnen.test_unbrauchbare_lage_ergibt_standardgroesse` | Prüft, dass eine nicht wiederherstellbare Lage auf die Standardgröße zurückfällt. | grün |
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_korrektur.py`
 
@@ -1213,6 +1210,7 @@ Testgruppen:
 - `TestEigentuemerSuchen` (4 Testfälle): — (Docstring fehlt)
 - `TestTrefferliste` (3 Testfälle): — (Docstring fehlt)
 - `TestSitzung` (7 Testfälle): — (Docstring fehlt)
+- `TestKorrekturLabel` (4 Testfälle): — (Docstring fehlt)
 - `TestKorrektur` (5 Testfälle): — (Docstring fehlt)
 - `TestKorrekturDatei` (2 Testfälle): — (Docstring fehlt)
 - `TestDockWidgetTabs` (1 Testfälle): — (Docstring fehlt)
@@ -1231,13 +1229,17 @@ Testgruppen:
 | `TestTrefferliste.test_trefferzeile_mit_hinweis` | Prüft, dass die Trefferliste Name, Anschrift, E-Mail, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ zeigt. | grün |
 | `TestTrefferliste.test_ausgewaehlte_eigentuemer` | Prüft, dass das Trefferfenster die in der Tabelle gewählten Einträge zurückgibt. | grün |
 | `TestTrefferliste.test_daten_anzeigen_gibt_auswahl_weiter` | Prüft, dass „Daten anzeigen“ die Auswahl der Trefferliste an den Tab übergibt. | grün |
-| `TestSitzung.test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm` | Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster auf dem gewählten Bildschirm öffnet, im Vollbild, wenn QGIS woanders liegt. | grün |
-| `TestSitzung.test_auf_qgis_bildschirm_kein_vollbild` | Prüft, dass das Anzeigefenster auf dem QGIS-Bildschirm als normales Fenster öffnet. | grün |
+| `TestSitzung.test_sitzung_beginnen_oeffnet_leeres_fenster_mit_rahmen` | Prüft, dass „Sitzung beginnen“ ein leeres Anzeigefenster als normales Fenster öffnet (ohne gemerkte Lage beim ersten Mal). | grün |
+| `TestSitzung.test_lage_gilt_fuer_naechste_sitzung` | Prüft, dass Größe und Lage des Fensters beim Beenden gemerkt und in der nächsten Sitzung wiederverwendet werden. | grün |
 | `TestSitzung.test_daten_anzeigen_beginnt_sitzung_bei_bedarf` | Prüft, dass „Daten anzeigen“ ohne laufende Sitzung diese beginnt und die Einträge zeigt; die Statuszeile nennt nur die Anzahl. | grün |
 | `TestSitzung.test_daten_anzeigen_ohne_auswahl` | Prüft, dass „Daten anzeigen“ ohne Auswahl nur einen Hinweis gibt und keine Sitzung beginnt. | grün |
 | `TestSitzung.test_anzeige_leeren_laesst_fenster_offen` | Prüft, dass „Anzeige leeren“ die Daten entfernt, das Fenster aber nicht schließt. | grün |
 | `TestSitzung.test_sitzung_beenden_schliesst_fenster` | Prüft, dass „Sitzung beenden“ das Anzeigefenster schließt und die angezeigten Daten vergisst. | grün |
-| `TestSitzung.test_von_aussen_geschlossen_beendet_sitzung` | Prüft, dass ein von außen geschlossenes Anzeigefenster (z. B. Alt+F4) die Sitzung im Tab beendet. | grün |
+| `TestSitzung.test_von_aussen_geschlossen_beendet_sitzung` | Prüft, dass ein über den Fensterrahmen geschlossenes Anzeigefenster die Sitzung beendet und seine Lage gemerkt wird. | grün |
+| `TestKorrekturLabel.test_ohne_eigentuemerdatei` | Prüft, dass ohne Eigentümerdatei unter den Knöpfen steht, dass keine gewählt ist. | grün |
+| `TestKorrekturLabel.test_pfad_neben_der_eigentuemerdatei` | Prüft, dass der Pfad der Korrektur-CSV angezeigt wird, mit Hinweis, solange sie noch nicht angelegt ist. | grün |
+| `TestKorrekturLabel.test_ohne_csv_quelle_wird_abgefragt` | Prüft, dass bei einer Quelle ohne CSV angekündigt wird, dass der Speicherort beim ersten Speichern abgefragt wird. | grün |
+| `TestKorrekturLabel.test_gewaehlter_pfad_wird_angezeigt` | Prüft, dass ein einmal gewählter Speicherort angezeigt wird. | grün |
 | `TestKorrektur.test_korrektur_wird_festgehalten_und_angezeigt` | Prüft, dass eine Korrektur in die Korrektur-CSV geschrieben, sofort angezeigt und ohne Namen gemeldet wird. | grün |
 | `TestKorrektur.test_ohne_aenderung_nichts_schreiben` | Prüft, dass ein unverändert gespeicherter Dialog nichts schreibt. | grün |
 | `TestKorrektur.test_abbruch_nichts_schreiben` | Prüft, dass ein abgebrochener Korrekturdialog nichts schreibt. | grün |
@@ -1719,7 +1721,7 @@ dann verschwindet er hier von selbst.
 
 Keine Lücken.
 
-### `sn_verfahrensgebiet` – 102 von 288 Testfällen ohne Beschreibung
+### `sn_verfahrensgebiet` – 102 von 289 Testfällen ohne Beschreibung
 
 #### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py` (25 von 25)
 
diff --git a/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md b/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md
index 245af2f..a49de90 100644
--- a/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md
+++ b/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md
@@ -6,7 +6,7 @@ Plugin: `sn_verfahrensgebiet` · Stand: 06.10.2026 · Pflege: bei jeder Änderun
 
 Im Termin sitzt der Eigentümer vor einem zweiten Bildschirm. Dort sieht er nur seine eigenen gespeicherten Daten:
 Name, Anschrift und E-Mail. Den QGIS-Bildschirm mit Trefferliste, Konten und anderen Personen sieht er nicht.
-Die Sachbearbeitung beginnt zu Beginn der Termine eine Sitzung. Für jeden Eigentümer wählt sie in der Trefferliste
+Die Sachbearbeitung beginnt zu Beginn der Termine eine Sitzung und schiebt das Anzeigefenster auf den Bildschirm des Eigentümers; beim nächsten Mal erscheint es wieder dort. Für jeden Eigentümer wählt sie in der Trefferliste
 die passenden Einträge und zeigt sie an. Stimmt etwas nicht, korrigiert sie es im Plugin. Der Eigentümer sieht die
 Korrektur sofort. Vor dem nächsten Eigentümer leert sie die Anzeige, ohne das Fenster zu schließen. Am Ende beendet
 sie die Sitzung.
@@ -20,7 +20,7 @@ Die Suche bis zur Trefferliste beschreibt [`verfahrensgebiet-eigentuemersuche.md
 
 ```mermaid
 flowchart TD
-    A["1 Bildschirm wählen,
Sitzung beginnen"] --> B["2 Anzeigefenster öffnet leer
(zweiter Bildschirm: Vollbild)"] + A["1 Sitzung beginnen"] --> B["2 Anzeigefenster öffnet leer, mit Rahmen;
Bearbeiter schiebt es auf den zweiten Bildschirm"] T["Trefferliste: Einträge wählen,
„Daten anzeigen“"] --> C{"Sitzung läuft?"} C -->|nein| A C -->|ja| D["3 Eigentümer sieht Name,
Anschrift, E-Mail"] @@ -37,16 +37,14 @@ flowchart TD ## Was geprüft wird -Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_anzeige.py` (Anzeigetext, Bildschirme), +Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_anzeige.py` (Anzeigetext, Fenster öffnen), `tests/test_eigentuemer_korrektur.py` (Korrekturlogik, Korrektur-CSV) und `tests/test_eigentuemer_tab.py` (Sitzung, Korrektur im Tab). | # | Schritt | Wird geprüft durch | Ebene | |---|---|---|---| -| 1 | Vorgabe: erster Bildschirm ohne QGIS | `test_vorgabe_ist_nicht_der_qgis_bildschirm`, `test_bildschirm_text` | Mock | -| 1 | Ohne Qt-Anwendung keine Bildschirme | `test_ohne_qt_anwendung_keine_bildschirme` | Mock | -| 2 | Sitzung beginnen: leer, Vollbild auf zweitem Bildschirm | `test_sitzung_beginnen_im_vollbild_auf_zweitem_bildschirm`, `test_vollbild_auf_gewaehltem_bildschirm` | Mock | -| 2 | Auf dem QGIS-Bildschirm normales Fenster | `test_auf_qgis_bildschirm_kein_vollbild`, `test_normales_fenster_mittig`, `test_ohne_bildschirm_einfach_zeigen` | Mock | +| 2 | Sitzung beginnen: leeres Fenster mit Rahmen, nie Vollbild | `test_sitzung_beginnen_oeffnet_leeres_fenster_mit_rahmen`, `test_normales_fenster_in_standardgroesse` | Mock | +| 2 | Lage des Fensters gilt für die nächste Sitzung | `test_lage_gilt_fuer_naechste_sitzung`, `test_gemerkte_lage_wird_wiederhergestellt`, `test_unbrauchbare_lage_ergibt_standardgroesse` | Mock | | 3 | „Daten anzeigen“ beginnt bei Bedarf die Sitzung | `test_daten_anzeigen_beginnt_sitzung_bei_bedarf`, `test_daten_anzeigen_gibt_auswahl_weiter` | Mock | | 3 | Ohne Auswahl nur ein Hinweis | `test_daten_anzeigen_ohne_auswahl` | Mock | | 3 | Inhalt: Name, Anschrift, E-Mail; mehrere Einträge | `test_name_anschrift_email`, `test_mehrere_eintraege_getrennt` | Mock | @@ -59,6 +57,7 @@ Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_anzeige.p | 5 | Pfad neben der Eigentümerdatei, sonst Rückfrage | `test_textdatei_provider`, `test_ogr_provider`, `test_keine_csv`, `test_vorschlag_neben_der_eigentuemerdatei`, `test_ohne_csv_quelle_wird_gefragt` | Mock | | 5 | Schreibfehler | `test_schreibfehler_wird_weitergereicht`, `test_schreibfehler_warnung_und_anzeige_unveraendert` | Mock | | 5 | Ohne Anzeige keine Korrektur | `test_ohne_anzeige_keine_korrektur` | Mock | +| 5 | Pfad der Korrektur-Datei unter den Knöpfen | `test_ohne_eigentuemerdatei`, `test_pfad_neben_der_eigentuemerdatei`, `test_ohne_csv_quelle_wird_abgefragt`, `test_gewaehlter_pfad_wird_angezeigt` | Mock | | 6 | Anzeige leeren, Fenster bleibt offen | `test_anzeige_leeren_laesst_fenster_offen`, `test_leer_nur_platzhalter` | Mock | | 7 | Sitzung beenden bzw. Fenster von außen geschlossen | `test_sitzung_beenden_schliesst_fenster`, `test_von_aussen_geschlossen_beendet_sitzung` | Mock | | — | Keine Personendaten auf der Konsole | `test_schreibt_nichts_auf_die_konsole` | Mock | @@ -70,7 +69,7 @@ Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` ( | Sonderfall | Kommt vor bei | Wird geprüft durch | |---|---|---| -| Nur ein Bildschirm (Notebook) | Termin ohne zweiten Monitor, Ausprobieren | `test_auf_qgis_bildschirm_kein_vollbild` | +| Nur ein Bildschirm (Notebook) | Termin ohne zweiten Monitor, Ausprobieren | `test_normales_fenster_in_standardgroesse` (immer mit Rahmen, verdeckt QGIS nie ausweglos) | | Eheleute oder Miteigentümer erscheinen gemeinsam | mehrere Einträge auf einmal anzeigen und einzeln korrigieren | `test_mehrere_eintraege_getrennt`, `test_korrektur_wird_festgehalten_und_angezeigt` | | Eigentümer hat keine E-Mail hinterlegt | häufig; soll beim Termin ergänzt werden | `test_fehlende_email_wird_genannt` | | Neue Anschrift mit Komma („Am Anger 3, Haus B“) | Zusatz zur Hausnummer | `test_neue_datei_mit_kopfzeile_und_bom` | @@ -81,7 +80,7 @@ Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` ( | Was | Warum | Wie stattdessen abgesichert | |---|---|---| -| Fenster tatsächlich auf dem zweiten Bildschirm im Vollbild | braucht echte Bildschirme; im Mock-Modus gibt es keine | Aufrufreihenfolge mit Attrappe geprüft, manueller Test mit zwei Bildschirmen | +| Wiederherstellen der Lage auf dem zweiten Bildschirm | braucht echte Bildschirme; im Mock-Modus gibt es keine | Aufruf mit Attrappe geprüft, manueller Test mit zwei Bildschirmen | | Korrekturdialog mit echten Eingabefeldern | im Mock-Modus gibt es keine Widgets; die Vorbelegung (`feldwerte`) und die Auswertung sind als reine Funktionen getestet | manueller Test | | Schriftgröße und Lesbarkeit der Anzeige | Geschmack und Bildschirmgröße | manueller Test im Termin | @@ -89,5 +88,6 @@ Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` ( | Lücke | Auswirkung | Issue | |---|---|---| -| Der manuelle Test mit zwei Bildschirmen steht aus, auch mit unterschiedlicher Skalierung (z. B. Notebook 150 %, Monitor 100 %) | Das Fenster könnte auf dem falschen Bildschirm oder nicht ganz im Vollbild erscheinen | Verfahrensgebiet #56 | +| Wiederherstellen der Lage in der nächsten Sitzung ist in QGIS noch nicht bestätigt | Das Fenster erscheint dann wieder auf dem Hauptbildschirm und muss erneut verschoben werden – lästig, aber gefahrlos | Verfahrensgebiet #56 | +| Die Lage wird nur bis zum Neustart von QGIS gemerkt | nach jedem Start einmal verschieben | bei Bedarf eigenes Issue (QSettings) | | Eine neue Suche leert die Anzeige nicht von selbst | Wer „Anzeige leeren“ vergisst, zeigt dem nächsten Eigentümer noch die vorherigen Daten | bewusst so (Bearbeiter entscheidet); bei Bedarf eigenes Issue | diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md index 0f96f93..e5a4479 100644 --- a/docs/wissen/fallstricke.md +++ b/docs/wissen/fallstricke.md @@ -103,7 +103,7 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik - **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. Die Korrektur-CSV (`_korrekturen.csv`) enthält ebenfalls Personendaten und liegt deshalb neben dem Export, nicht im Projekt- oder Plugin-Ordner. Das Anzeigefenster für den Eigentümer zeigt nur Name, Anschrift und E-Mail; Konten, Personen-IDs und Kennzeichen bleiben im Tab. (06.10.2026) -- **Fenster auf einem bestimmten Bildschirm öffnen (Qt):** Erst `winId()` aufrufen, damit es ein natives Fenster gibt, dann `windowHandle().setScreen(bildschirm)`, `setGeometry(bildschirm.geometry())` und `showFullScreen()`. Nur `move()` auf die Koordinaten reicht unter Windows nicht zuverlässig, wenn die Bildschirme unterschiedlich skaliert sind. Die Liste der Bildschirme liefert `QCoreApplication.instance().screens()` (die QGIS-Anwendung ist eine `QApplication`); im Mock-Modus gibt es sie nicht. Umgesetzt in `sn_verfahrensgebiet/ui/eigentuemer_anzeige.py` (Verfahrensgebiet #56), in QGIS noch zu bestätigen. (06.10.2026) +- **Kein randloses Vollbild für eigene Fenster (Verfahrensgebiet #56).** Das Anzeigefenster für den Eigentümer sollte per `windowHandle().setScreen()`, `setGeometry()` und `showFullScreen()` auf dem zweiten Bildschirm erscheinen. Im QGIS-Test (06.10.2026, Windows) landete es trotzdem immer auf dem Hauptbildschirm und ließ sich im Vollbild weder verschieben noch schließen; es verdeckte QGIS, nur ein hartes Beenden von QGIS half. Seitdem öffnet es als normales Fenster mit Rahmen, der Bearbeiter schiebt es selbst auf den richtigen Bildschirm und kann es dort maximieren; `saveGeometry()`/`restoreGeometry()` merkt die Lage für die nächste Sitzung. Allgemein: Fenster, die QGIS verdecken können, brauchen immer einen Weg hinaus (Rahmen, Schließen-Knopf). (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.