PER_RECHTSPERSON als Rohwert in der Trefferliste zeigen (#56)

PER_RECHTSPERSON kennt die Werte 0, 1, 5 und 9 und ist kein Ja/Nein-Feld.
Die Trefferliste bekommt dafür eine eigene Spalte mit dem unveränderten
Wert; abweichende Werte einer Person werden gesammelt. Die Entschlüsselung
folgt später. „verstorben“ (PER_VERSTORBEN 0/1) bleibt als Hinweis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 13:13:50 +02:00
co-authored by Claude Opus 5.5
parent 29719a8a99
commit b105894241
5 changed files with 40 additions and 23 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
- Neu: Eigentümersuche im Tab „Eigentümer“. Den Serienbriefexport (CSV) von Hand als Layer laden, als Eigentümerdatei wählen, Name und/oder Vorname eingeben: Die Trefferliste zeigt Name, Anschrift, E-Mail und die Kennzeichen „verstorben“/„Rechtsperson“. Gleiche Personen mit gleicher Anschrift werden zusammengefasst, Groß-/Kleinschreibung und Umlautschreibweise spielen keine Rolle (#56)
- Neu: Eigentümersuche im Tab „Eigentümer“. Den Serienbriefexport (CSV) von Hand als Layer laden, als Eigentümerdatei wählen, Name und/oder Vorname eingeben: Die Trefferliste zeigt Name, Anschrift, E-Mail, den Wert von PER_RECHTSPERSON (0, 1, 5 oder 9, noch nicht entschlüsselt) und das Kennzeichen „verstorben“. Gleiche Personen mit gleicher Anschrift werden zusammengefasst, Groß-/Kleinschreibung und Umlautschreibweise spielen keine Rolle (#56)
- Intern: Die Auswahltabelle aus „Verfahrensgebiete aus Landkreis laden“ (Suchfeld, „Alle auswählen“, Mehrfachauswahl) ist als gemeinsame Funktion herausgelöst und wird von der Eigentümer-Trefferliste mitgenutzt; der Landkreis-Dialog verhält sich unverändert (#56)
- Neu: Tab „Eigentümer“ mit Flurstückssuche. Gemarkung (Name oder Nummer) und Flurstücksnummer (z. B. 562 oder 12/3) eingeben, die Treffer werden im Layer „Flurstücke“ ausgewählt und angezoomt. Ohne Nenner werden alle Flurstücke mit diesem Zähler gefunden. Fehlt der Layer „Flurstücke“, weist ein Dialog darauf hin, zuerst Verfahrensgebiet und Flurstücke aus ALKIS zu laden (#56)
- Bugfix: Nach dem Laden des Verfahrensgebiets (ALKIS oder Shape) sprang die Karte an die falsche Stelle, wenn ein anderes Projekt-KBS als EPSG:25833 eingestellt war. Der Zoom rechnet die Ausdehnung jetzt in das KBS der Kartenansicht um (`zoom_auf_layer` aus sn_basis, setzt sn_basis mit Basis #99 voraus) (Basis #99)
+11 -7
View File
@@ -62,7 +62,7 @@ class Eigentuemer:
ortsteil: str = ""
emails: Tuple[str, ...] = ()
verstorben: bool = False
rechtsperson: bool = False
rechtsperson: Tuple[str, ...] = ()
per_ids: Tuple[str, ...] = ()
konten: Tuple[str, ...] = ()
feature_ids: Tuple[int, ...] = ()
@@ -85,8 +85,12 @@ class Eigentuemer:
@property
def hinweise(self) -> str:
"""Kennzeichen für die Trefferliste (nicht für das Anzeigefenster)."""
return ", ".join(t for t, gesetzt in (("verstorben", self.verstorben),
("Rechtsperson", self.rechtsperson)) if gesetzt)
return "verstorben" if self.verstorben else ""
@property
def rechtsperson_text(self) -> str:
"""Rohwert(e) von ``PER_RECHTSPERSON`` (bekannt: 0, 1, 5, 9), noch nicht entschlüsselt."""
return ", ".join(self.rechtsperson)
def _text(wert) -> str:
@@ -192,17 +196,17 @@ def suche_eigentuemer(layer, nachname: str, vorname: str) -> List[Eigentuemer]:
gruppe = gruppen.setdefault(_gruppenschluessel(werte), {
"werte": werte, "emails": [], "per_ids": [], "konten": [], "feature_ids": [],
"verstorben": False, "rechtsperson": False,
"verstorben": False, "rechtsperson": [],
})
for k in ("anrede", "titel"):
if not gruppe["werte"][k] and werte[k]:
gruppe["werte"][k] = werte[k]
for liste, wert in (("emails", werte["email"]), ("per_ids", werte["per_id"]), ("konten", werte["konto"])):
for liste, wert in (("emails", werte["email"]), ("per_ids", werte["per_id"]),
("konten", werte["konto"]), ("rechtsperson", werte["rechtsperson"])):
if wert and wert not in gruppe[liste]:
gruppe[liste].append(wert)
gruppe["feature_ids"].append(feature.id())
gruppe["verstorben"] = gruppe["verstorben"] or _ist_gesetzt(werte["verstorben"])
gruppe["rechtsperson"] = gruppe["rechtsperson"] or _ist_gesetzt(werte["rechtsperson"])
treffer = []
for gruppe in gruppen.values():
@@ -211,7 +215,7 @@ def suche_eigentuemer(layer, nachname: str, vorname: str) -> List[Eigentuemer]:
anrede=w["anrede"], titel=w["titel"], vorname=w["vorname"], nachname=w["nachname"],
strasse=w["strasse"], plz=w["plz"], wohnort=w["wohnort"], ortsteil=w["ortsteil"],
emails=tuple(gruppe["emails"]),
verstorben=gruppe["verstorben"], rechtsperson=gruppe["rechtsperson"],
verstorben=gruppe["verstorben"], rechtsperson=tuple(gruppe["rechtsperson"]),
per_ids=tuple(gruppe["per_ids"]), konten=tuple(gruppe["konten"]),
feature_ids=tuple(gruppe["feature_ids"]),
))
+19 -8
View File
@@ -139,18 +139,29 @@ class TestSuche(unittest.TestCase):
treffer = es.suche_eigentuemer(layer, "Beispiel", "Anna")
self.assertEqual([e.wohnort for e in treffer], ["Altdorf", "Zettlitz"])
def test_kennzeichen_verstorben_und_rechtsperson(self):
"""Prüft, dass „verstorben“ und „Rechtsperson“ aus üblichen Ja-Werten erkannt werden und Nein-Werte, Leeres und NULL nicht zählen."""
ja = ["J", "ja", "1", "true", "X"]
nein = ["", "0", "N", "nein", "false", None, _Null()]
zeilen = [_zeile(f"Ja{i}", "Test", PER_VERSTORBEN=w, PER_RECHTSPERSON=w) for i, w in enumerate(ja)]
zeilen += [_zeile(f"Nein{i}", "Test", PER_VERSTORBEN=w, PER_RECHTSPERSON=w) for i, w in enumerate(nein)]
def test_kennzeichen_verstorben(self):
"""Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht."""
ja = ["1", 1]
nein = ["0", 0, "", None, _Null()]
zeilen = [_zeile(f"Ja{i}", "Test", PER_VERSTORBEN=w) for i, w in enumerate(ja)]
zeilen += [_zeile(f"Nein{i}", "Test", PER_VERSTORBEN=w) for i, w in enumerate(nein)]
for e in es.suche_eigentuemer(_Layer(zeilen), "", "Test"):
erwartet = e.nachname.startswith("Ja")
with self.subTest(name=e.nachname):
self.assertEqual(e.verstorben, erwartet)
self.assertEqual(e.rechtsperson, erwartet)
self.assertEqual(e.hinweise, "verstorben, Rechtsperson" if erwartet else "")
self.assertEqual(e.hinweise, "verstorben" if erwartet else "")
def test_rechtsperson_als_rohwert(self):
"""Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden."""
layer = _Layer([
_zeile("Beispiel", "Anna", PER_RECHTSPERSON="0"),
_zeile("Gemeinde Musterstadt", "", strasse="Markt 1", PER_RECHTSPERSON=5),
_zeile("Muster", "Bernd", PER_RECHTSPERSON="1"),
_zeile("Muster", "Bernd", PER_RECHTSPERSON="9"),
_zeile("Ohnewert", "Clara", PER_RECHTSPERSON=_Null()),
])
werte = {e.nachname: e.rechtsperson_text for e in es.suche_eigentuemer(layer, "e", "")}
self.assertEqual(werte, {"Beispiel": "0", "Gemeinde Musterstadt": "5", "Muster": "1, 9", "Ohnewert": ""})
def test_plz_mit_fuehrender_null(self):
"""Prüft, dass eine von QGIS als Zahl gelesene PLZ wieder fünfstellig mit führender Null erscheint und Zeilen dadurch zusammenfinden."""
+4 -3
View File
@@ -103,7 +103,8 @@ def _eig_tab(nachname="Beispiel", vorname="", layer_id="csv1"):
_ANNA = Eigentuemer(vorname="Anna", nachname="Beispiel", strasse="Hauptstraße 1", plz="01234",
wohnort="Musterstadt", emails=("anna@example.org",), verstorben=True)
wohnort="Musterstadt", emails=("anna@example.org",), verstorben=True,
rechtsperson=("0",))
@patch.object(et, "info")
@@ -152,10 +153,10 @@ class TestEigentuemerSuchen(unittest.TestCase):
class TestTrefferliste(unittest.TestCase):
def test_trefferzeile_mit_hinweis(self):
"""Prüft, dass die Trefferliste Name, Anschrift, E-Mail und die Kennzeichen „verstorben“/„Rechtsperson“ zeigt."""
"""Prüft, dass die Trefferliste Name, Anschrift, E-Mail, den Rohwert von PER_RECHTSPERSON und das Kennzeichen „verstorben“ zeigt."""
self.assertEqual(
trefferzeile(_ANNA),
("Beispiel, Anna", "Hauptstraße 1, 01234 Musterstadt", "anna@example.org", "verstorben"),
("Beispiel, Anna", "Hauptstraße 1, 01234 Musterstadt", "anna@example.org", "0", "verstorben"),
)
def test_ausgewaehlte_eigentuemer(self):
+5 -4
View File
@@ -1,8 +1,8 @@
"""sn_verfahrensgebiet/ui/eigentuemer_treffer.py – Trefferliste der Eigentümersuche (#56).
Nicht modales Fenster für den Bearbeiter. Es zeigt die zusammengefassten
Treffer mit den Kennzeichen „verstorben“ und „Rechtsperson“ und erlaubt die
Mehrfachauswahl. Die Tabelle kommt aus
Treffer mit dem Wert von ``PER_RECHTSPERSON`` (noch nicht entschlüsselt) und dem
Kennzeichen „verstorben“ und erlaubt die Mehrfachauswahl. Die Tabelle kommt aus
:func:`~sn_verfahrensgebiet.functions.verfahrensgebiet_dialoge.baue_auswahltabelle`,
dieselbe wie bei „Verfahrensgebiete aus Landkreis laden“.
"""
@@ -17,15 +17,16 @@ from sn_verfahrensgebiet.functions.verfahrensgebiet_dialoge import (
ausgewaehlte_indizes,
)
SPALTENKOEPFE = ["Name", "Anschrift", "E-Mail", "Hinweis"]
SPALTENKOEPFE = ["Name", "Anschrift", "E-Mail", "Rechtsperson", "Hinweis"]
def trefferzeile(eigentuemer: Eigentuemer) -> Tuple[str, str, str, str]:
def trefferzeile(eigentuemer: Eigentuemer) -> Tuple[str, str, str, str, str]:
"""Spaltentexte eines Treffers in der Reihenfolge von :data:`SPALTENKOEPFE`."""
return (
eigentuemer.name,
eigentuemer.anschrift,
", ".join(eigentuemer.emails),
eigentuemer.rechtsperson_text,
eigentuemer.hinweise,
)