# -*- coding: utf-8 -*- """Pruefstand: Kopf- und Fussbild auf der Rechnung. Drei Dinge muessen stimmen, sonst faellt es erst beim Drucken auf: 1. Ein gewaehltes Bild wird KOPIERT (Ordner "bilder"), nicht nur verlinkt - sonst ist das Logo weg, sobald der Nutzer seinen Desktop aufraeumt. 2. Das Fussbild steht auf der Kundenrechnung, aber NICHT auf dem Amt-Blatt: ein Briefpapier-Band traegt Bank und Steuernummer, und die haben auf dem Blatt fuers Amt nichts zu suchen. 3. Der Haken "Angaben stehen im Bild" laesst die gezeichneten Texte weg - und zwar nur dort, wo er gilt. """ import json import os import shutil import sys import tempfile QUELLE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, QUELLE) import config # noqa: E402 tmp = tempfile.mkdtemp(prefix="bilder_pruef_") config.BASE_DIR = tmp config.CONFIG_PATH = os.path.join(tmp, "config.json") shutil.copy(os.path.join(QUELLE, "config.json"), config.CONFIG_PATH) import bilder # noqa: E402 import pdf_renderer # noqa: E402 from berechnung import berechne # noqa: E402 fehler = [] def pruefe(bedingung, text): print(("OK | " if bedingung else "FEHL | ") + text) if not bedingung: fehler.append(text) def testbild(pfad, farbe): """Kleines PNG erzeugen - der Pruefstand braucht keine echten Kundenbilder.""" from PIL import Image Image.new("RGB", (600, 120), farbe).save(pfad) return pfad # ---- 1. Uebernehmen: kopieren, nicht verlinken ----------------------------- fremd = os.path.join(tmp, "vom_desktop.png") testbild(fremd, (10, 90, 60)) gespeichert = bilder.uebernehmen(fremd) pruefe(gespeichert == "bilder/vom_desktop.png", f"Pfad wird kurz gemerkt: {gespeichert}") pruefe(os.path.exists(os.path.join(tmp, "bilder", "vom_desktop.png")), "Bild liegt im Ordner bilder/") # Das Original loeschen - die Rechnung muss trotzdem ein Bild haben. os.remove(fremd) pruefe(bool(bilder.aufloesen(gespeichert)), "Bild bleibt da, wenn das Original geloescht wird") # Dasselbe Bild zweimal waehlen -> kein zweites Exemplar. shutil.copy(os.path.join(tmp, "bilder", "vom_desktop.png"), fremd) pruefe(bilder.uebernehmen(fremd) == "bilder/vom_desktop.png", "gleiches Bild zweimal -> kein Duplikat") # Anderer Inhalt, gleicher Name -> das alte Bild wird NICHT ueberschrieben. testbild(fremd, (200, 30, 30)) zweit = bilder.uebernehmen(fremd) pruefe(zweit == "bilder/vom_desktop-2.png", f"gleicher Name, anderes Bild -> {zweit}") # ---- 2. Rendern: Kunde vs. Amt -------------------------------------------- cfg = config.load_config() cfg["firma"]["logo"] = bilder.uebernehmen(testbild(os.path.join(tmp, "kopf.png"), (20, 60, 120))) cfg["firma"]["fussbild"] = bilder.uebernehmen(testbild(os.path.join(tmp, "fuss.png"), (120, 60, 20))) cfg["firma"]["bank"] = "Bank Testbank" mengen = {1: {"anzahl": 3, "personen": 0}} r = berechne(cfg["katalog"], mengen, cfg) kopf = {"art": "rechnung", "rechnungsnummer": "2026-999", "datum": "05.09.2026", "kunde_zeilen": ["Herr", "Max Muster", "Musterweg 1", "04416 Markkleeberg"], "anreise": "01.09.2026", "abreise": "04.09.2026", "naechte": 3} pfad = os.path.join(tmp, "probe.pdf") pdf_renderer.render_rechnung(pfad, cfg, r, kopf) pruefe(os.path.exists(pfad), "PDF mit beiden Bildern entsteht") def seiten_bilder_und_text(pdf): """Je Seite: (Anzahl eingebetteter Bilder, Text).""" from pypdf import PdfReader raus = [] for seite in PdfReader(pdf).pages: xo = seite.get("/Resources", {}).get("/XObject", {}) try: xo = xo.get_object() except AttributeError: pass anzahl = sum(1 for k in xo if xo[k].get_object().get("/Subtype") == "/Image") raus.append((anzahl, seite.extract_text() or "")) return raus seiten = seiten_bilder_und_text(pfad) pruefe(len(seiten) == 1, f"genau EIN Blatt ({len(seiten)} Seiten)") kunde_bilder, kunde_text = seiten[0] pruefe(kunde_bilder == 2, f"das Blatt traegt Kopf- UND Fussbild ({kunde_bilder})") pruefe("Testbank" in kunde_text, "Bankverbindung steht auf der Rechnung") pruefe("Steuernummer" in kunde_text, "die Steuernummer steht auf der Rechnung (ยง 14 Abs. 4 Nr. 2 UStG)") # ---- 3. Die Firmenfarbe faerbt das Blatt ---------------------------------- # Jede Firma faerbt ihre Rechnung selbst. Geprueft wird, dass die gewaehlte # Farbe wirklich im PDF landet - und dass eine kaputte Eingabe das Schreiben # NICHT verhindert, sondern still auf die Vorgabe zurueckfaellt. cfg["design"] = {"akzent": "#8a1f1f"} pfad2 = os.path.join(tmp, "probe_rot.pdf") pdf_renderer.render_rechnung(pfad2, cfg, r, kopf) pruefe(pdf_renderer.ACCENT.hexval().endswith("8a1f1f"), f"gewaehlte Farbe steht im Blatt: {pdf_renderer.ACCENT.hexval()}") pruefe(pdf_renderer.GREY_BG.red > 0.9, "helle Flaeche wird aus der Farbe gemischt (bleibt hell)") cfg["design"] = {"akzent": "gruen bitte"} pfad3 = os.path.join(tmp, "probe_kaputt.pdf") pdf_renderer.render_rechnung(pfad3, cfg, r, kopf) pruefe(os.path.exists(pfad3), "kaputte Farbe verhindert die Rechnung NICHT") pruefe(pdf_renderer.ACCENT.hexval().endswith("1f5c3d"), "kaputte Farbe faellt auf die Vorgabe zurueck") cfg["design"] = {"akzent": pdf_renderer.AKZENT_VORGABE} # Ohne Bilder muss das Blatt aussehen wie immer. cfg["firma"]["logo"] = "" cfg["firma"]["fussbild"] = "" pfad4 = os.path.join(tmp, "probe_ohne_bild.pdf") pdf_renderer.render_rechnung(pfad4, cfg, r, kopf) seiten3 = seiten_bilder_und_text(pfad4) pruefe(seiten3[0][0] == 0, "ohne Bilder: keine Bilder im PDF") pruefe(cfg["firma"]["name"] in seiten3[0][1], "ohne Bild steht der Firmenname gezeichnet oben") print() print(f"Ordner: {tmp}") if fehler: print(f"FEHLGESCHLAGEN: {len(fehler)}") for f in fehler: print(" -", f) sys.exit(1) print("ALLES GRUEN")