# -*- coding: utf-8 -*- """Kleine Helfer, die überall gebraucht werden: Zahlen, Datum, Namen, Prüfungen.""" import datetime import config def parse_num(s): """'12', '3,5' -> float; leer/ungueltig -> 0.0""" s = (s or "").strip().replace(",", ".") if not s: return 0.0 try: return float(s) except ValueError: return 0.0 def parse_date(s): """'28.01.2026' -> date; sonst None""" s = (s or "").strip() for fmt in ("%d.%m.%Y", "%d.%m.%y", "%Y-%m-%d"): try: return datetime.datetime.strptime(s, fmt).date() except ValueError: continue return None def safe_filename(s): keep = "-_.() " return "".join(c for c in s if c.isalnum() or c in keep).strip() or "Rechnung" def saeubern(s): """Rand-Leerzeichen weg, Mehrfachleerzeichen zu einem. Wird vor der Pruefung auf die Felder angewendet - geprueft werden muss genau das, was nachher auf dem Blatt steht. """ return " ".join(str(s or "").split()) def buchstaben(s): return sum(1 for c in str(s) if c.isalpha()) def ziffern(s): return sum(1 for c in str(s) if c.isdigit()) # Auswahl fuer das Anrede-Feld. Die Liste ist nicht bindend - das Feld bleibt # tippbar, damit Sonderfaelle nicht am Programm scheitern. ANREDEN = ["Herr", "Frau", "Herr und Frau", "Familie", "Firma", "Eheleute"] def nummer_normalisieren(s): """'2026-5', ' 2026-005 ' -> '2026-005'; alles andere kommt getrimmt zurueck. Damit findet die Suche die Rechnung auch, wenn die fuehrenden Nullen fehlen. """ s = str(s or "").strip() teile = s.split("-") if len(teile) == 2: try: return config.format_rechnungsnummer(int(teile[0]), int(teile[1])) except ValueError: pass return s def kunde_maengel(kunde): """Beanstandungen an Name und Anschrift; leere Liste = in Ordnung. Prueft nicht nur "irgendwas eingetippt": ein Feld aus Leerzeichen, Bindestrichen oder einem einzelnen Zeichen ist kein Name und keine Anschrift. Vier Ziffern reichen, damit auch oesterreichische und niederlaendische Postleitzahlen durchgehen. """ maengel = [] for feld, schluessel in (("Vorname", "vorname"), ("Nachname / Firma", "nachname"), ("Straße / Nr.", "strasse")): wert = saeubern(kunde.get(schluessel)) if not wert: maengel.append(f"{feld}: ist leer") elif buchstaben(wert) < 2: maengel.append(f"{feld}: „{wert}“ ergibt keinen Namen") ort = saeubern(kunde.get("plzort")) if not ort: maengel.append("PLZ / Ort: ist leer") elif ziffern(ort) < 4 or buchstaben(ort) < 2: maengel.append(f"PLZ / Ort: „{ort}“ – bitte Postleitzahl UND Ort, " "z. B. 04416 Markkleeberg") return maengel def name_aufteilen(ganz): """'Andres Schäfer' -> ('Andres', 'Schäfer'); ein einzelnes Wort wird Nachname.""" teile = str(ganz or "").split() if len(teile) > 1: return " ".join(teile[:-1]), teile[-1] return "", (teile[0] if teile else "")