Sein Fund: "an der storno verweist du auf die alte rechnung, muss aber
lauten neu zu und neue rechnungs nummer. heisst kette ist so: rechnung
storniert weist auf die storno nummer, storno verweist auf neue rechnung.
wer soll denn sonst wissen wo er suchen muss."
Er hat recht: eine Zeile, die nur zurueckzeigt, laesst den Leser stehen.
Jetzt sagt jede Zeile, wo es WEITERGEHT:
Rechnung -> "storniert mit 2026-901"
Storno -> "neue Rechnung 2026-902" (Fall A)
-> "neu abgerechnet mit 2026-343" (Fall B, Doppelung)
-> "Storno zu 2026-900" (Fall C / alte Belege ohne Angabe)
neue R. -> "Neuausstellung"
Dafuer wandert die Folgenummer aus den PDF-Kenndaten bis ins Journal:
Buchung.folge_nummer + folge_art (neu | vorhanden | ""), gelesen aus
korrektur_nummer/ersatz_vorhanden/nur_storno, zwei neue DB-Spalten, alte
Journale bauen sich wie gehabt selbst um.
An seinen ECHTEN Belegen geprueft: Storno 2026-455 -> folge 2026-369
(vorhanden), 2026-456 -> 2026-343 (vorhanden). Die Angabe steht in den
Kenndaten schon drin, sie wurde bisher nur nicht gelesen.
Fenster und Blatt zeigen dieselben Worte. Erklaerungs-PDF um eine
Kettentabelle ergaenzt. Pruefkette gruen (storno_verrechnet inkl.
September-Blatt, doppelte_leistung, nur_storno).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bEsoFUk16DfnNA7MjY36H
308 lines
18 KiB
Python
308 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""Erzeugt die Erklaerung fuers Amt / die Steuerberaterin als PDF.
|
||
|
||
python doku_aenderungen_pdf.py [Zielpfad.pdf]
|
||
|
||
Warum als eigenes Skript und nicht von Hand geschrieben: der Text nennt
|
||
Paragraphen und Betraege. Wenn sich etwas am Programm aendert, aendert man den
|
||
Text hier und erzeugt das Blatt neu - statt eine Word-Datei zu suchen, die
|
||
niemand mehr findet.
|
||
|
||
Briefkopf und Farben kommen aus derselben Konfiguration wie die Rechnungen
|
||
(firma.lade()), damit das Blatt erkennbar zum Betrieb gehoert.
|
||
"""
|
||
import os
|
||
import sys
|
||
|
||
from reportlab.lib import colors
|
||
from reportlab.lib.enums import TA_JUSTIFY
|
||
from reportlab.lib.pagesizes import A4
|
||
from reportlab.lib.styles import ParagraphStyle
|
||
from reportlab.lib.units import mm
|
||
from reportlab.pdfbase.pdfmetrics import stringWidth
|
||
from reportlab.platypus import (BaseDocTemplate, Frame, KeepTogether, PageTemplate,
|
||
Paragraph, Spacer, Table, TableStyle)
|
||
|
||
HIER = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, HIER)
|
||
import firma as firma_lib # noqa: E402
|
||
|
||
ACCENT = colors.HexColor("#1f5c3d")
|
||
GREY_BG = colors.HexColor("#eef1ee")
|
||
LINE = colors.HexColor("#c8d0c8")
|
||
MUTED = colors.HexColor("#555555")
|
||
|
||
PAGE_W, PAGE_H = A4
|
||
MARGIN_L = MARGIN_R = 20 * mm
|
||
CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R
|
||
|
||
|
||
def stile():
|
||
return {
|
||
"titel": ParagraphStyle("titel", fontName="Helvetica-Bold", fontSize=17,
|
||
textColor=ACCENT, leading=21, spaceAfter=2),
|
||
"untertitel": ParagraphStyle("untertitel", fontName="Helvetica", fontSize=9.5,
|
||
textColor=MUTED, leading=13, spaceAfter=10),
|
||
"h": ParagraphStyle("h", fontName="Helvetica-Bold", fontSize=11.5,
|
||
textColor=ACCENT, leading=15, spaceBefore=9, spaceAfter=4),
|
||
"t": ParagraphStyle("t", fontName="Helvetica", fontSize=9.5, leading=13.5,
|
||
alignment=TA_JUSTIFY, spaceAfter=5),
|
||
"klein": ParagraphStyle("klein", fontName="Helvetica", fontSize=8, leading=11,
|
||
textColor=MUTED, spaceAfter=4),
|
||
"th": ParagraphStyle("th", fontName="Helvetica-Bold", fontSize=8.5, leading=11),
|
||
"z": ParagraphStyle("z", fontName="Helvetica", fontSize=8.5, leading=11.5),
|
||
}
|
||
|
||
|
||
def kopf_fuss(canvas, doc, cfg):
|
||
canvas.saveState()
|
||
f = cfg
|
||
canvas.setFont("Helvetica-Bold", 12)
|
||
canvas.setFillColor(ACCENT)
|
||
canvas.drawRightString(PAGE_W - MARGIN_R, PAGE_H - 15 * mm, f.get("name", ""))
|
||
canvas.setFont("Helvetica-Oblique", 8)
|
||
canvas.setFillColor(MUTED)
|
||
canvas.drawRightString(PAGE_W - MARGIN_R, PAGE_H - 20 * mm, f.get("untertitel", ""))
|
||
canvas.setStrokeColor(ACCENT)
|
||
canvas.setLineWidth(0.8)
|
||
canvas.line(MARGIN_L, PAGE_H - 23 * mm, PAGE_W - MARGIN_R, PAGE_H - 23 * mm)
|
||
canvas.setFont("Helvetica", 7.5)
|
||
canvas.setFillColor(MUTED)
|
||
canvas.drawString(MARGIN_L, 12 * mm, f.get("absender", ""))
|
||
canvas.drawRightString(PAGE_W - MARGIN_R, 12 * mm, f"Seite {canvas.getPageNumber()}")
|
||
canvas.restoreState()
|
||
|
||
|
||
def tabelle(daten, st, breiten):
|
||
zeilen = [[Paragraph(z, st["th"] if i == 0 else st["z"]) for z in reihe]
|
||
for i, reihe in enumerate(daten)]
|
||
t = Table(zeilen, colWidths=breiten, repeatRows=1, hAlign="LEFT")
|
||
t.setStyle(TableStyle([
|
||
("BACKGROUND", (0, 0), (-1, 0), GREY_BG),
|
||
("LINEBELOW", (0, 0), (-1, 0), 0.8, ACCENT),
|
||
("LINEBELOW", (0, 1), (-1, -1), 0.3, LINE),
|
||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||
("TOPPADDING", (0, 0), (-1, -1), 4),
|
||
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
|
||
("LEFTPADDING", (0, 0), (-1, -1), 4),
|
||
("RIGHTPADDING", (0, 0), (-1, -1), 4),
|
||
]))
|
||
return t
|
||
|
||
|
||
def bauen(pfad, cfg):
|
||
st = stile()
|
||
s = []
|
||
P = lambda text, art="t": s.append(Paragraph(text, st[art])) # noqa: E731
|
||
|
||
P("Änderungen an Rechnungsschreibung und Steuerbuch", "titel")
|
||
P("Campinghof Bartl · Stand 4. September 2026 · Rechnungstool v1.5.5, "
|
||
"Steuerbuch (Beherbergungssteuer) v1.6.7", "untertitel")
|
||
|
||
P("Warum überhaupt geändert wurde", "h")
|
||
P("Im laufenden Betrieb sind zwei Dinge aufgefallen, die beide dieselbe Ursache haben – "
|
||
"es gab keine feste Vorgabe, wie eine bereits geschriebene Rechnung korrigiert wird:")
|
||
P("<b>1. Dieselbe Leistung stand mehrfach im Rechnungsordner.</b> Wenn an einer Rechnung etwas "
|
||
"nicht stimmte, wurde mit dem alten Programm einfach eine neue geschrieben – mit einer neuen "
|
||
"Nummer. Die alte blieb liegen. Damit stand derselbe Aufenthalt zweimal im Ordner und wäre "
|
||
"zweimal in die Beherbergungssteuer-Anmeldung eingegangen.")
|
||
P("<b>2. Beim Empfänger stand teilweise nur der Vor- oder nur der Nachname.</b> Die "
|
||
"Anmeldekarten der Gäste enthalten alle Angaben; auf der Rechnung landete davon nicht immer "
|
||
"alles.")
|
||
|
||
P("Was jetzt gilt – feste Wege statt freier Hand", "h")
|
||
P("Eine geschriebene Rechnung lässt sich nicht mehr formlos ändern. Es gibt genau zwei Wege, "
|
||
"und beide erzeugen einen eigenen, nachvollziehbaren Beleg:")
|
||
s.append(tabelle([
|
||
["Fall", "Weg im Programm", "Was der Beleg sagt"],
|
||
["Angaben waren falsch, Leistung und Beträge stimmen "
|
||
"(z. B. Name, Anschrift)",
|
||
"<b>Berichtigung</b>",
|
||
"Eigenes Blatt, das sich auf die Rechnung bezieht. Die Rechnung <b>behält ihre "
|
||
"Nummer</b>, Beträge bleiben unverändert."],
|
||
["Die Rechnung selbst muss weg (falsche Leistung, "
|
||
"Doppelung, geplatzte Buchung)",
|
||
"<b>Storno</b> mit drei Auswahlmöglichkeiten",
|
||
"Eigene Stornorechnung mit Minusbeträgen, die die Rechnung nach Nummer, Datum und "
|
||
"Empfänger benennt."],
|
||
], st, [42 * mm, 38 * mm, CONTENT_W - 80 * mm]))
|
||
s.append(Spacer(1, 4 * mm))
|
||
|
||
P("Die drei Wege im Storno", "h")
|
||
P("Nach dem Storno muss auf dem Blatt stehen, wie es weitergeht – sonst wartet der Gast auf "
|
||
"eine Rechnung, die nie kommt, oder es bleibt eine Nummer unnötig reserviert. Deshalb fragt "
|
||
"das Programm zuerst:")
|
||
s.append(tabelle([
|
||
["", "Fall", "Folge auf dem Beleg"],
|
||
["A", "Es folgt eine neue, richtige Rechnung",
|
||
"Die Nummer der berichtigten Rechnung steht bereits auf dem Storno: "
|
||
"alte Nummer → Storno → neue Nummer."],
|
||
["B", "Die Leistung ist schon unter einer anderen Nummer abgerechnet "
|
||
"(der Doppelungsfall)",
|
||
"Das Storno verweist auf die <b>bleibende</b> Rechnung. Das Programm prüft, ob es diese "
|
||
"Nummer gibt und ob Gast, Zeitraum und Betrag dazu passen. Es wird keine Nummer "
|
||
"reserviert und kein Vorgang bleibt offen."],
|
||
["C", "Es wird gar nichts abgerechnet (Buchung geplatzt, Rechnung irrtümlich)",
|
||
"Auf dem Blatt steht, dass keine weitere Rechnung folgt. Es wird ebenfalls keine Nummer "
|
||
"reserviert."],
|
||
], st, [8 * mm, 52 * mm, CONTENT_W - 60 * mm]))
|
||
s.append(Spacer(1, 4 * mm))
|
||
|
||
P("Pflichtfelder beim Empfänger", "h")
|
||
P("Die Felder für den Empfänger sind jetzt <b>Pflicht</b>. Das ist keine Verschärfung durch "
|
||
"das Programm, sondern geltendes Recht: eine Rechnung muss den vollständigen Namen und die "
|
||
"vollständige Anschrift des Leistungsempfängers enthalten. Für die Beherbergungssteuer kommt "
|
||
"hinzu, dass die Meldescheine ohnehin Name, Wohnanschrift, Geburtsdatum sowie An- und "
|
||
"Abreise tragen und zusammen mit den Rechnungskopien aufzubewahren sind – die Angaben liegen "
|
||
"also vor und gehören auf den Beleg. Nur so lässt sich später zweifelsfrei sagen, "
|
||
"<b>wer zu welcher Rechnung gehört</b>.")
|
||
|
||
P("Die Rechnungsnummer steht fest", "h")
|
||
P("Eine einmal ausgestellte Rechnung behält ihre Nummer. Sie lässt sich im Programm nicht mehr "
|
||
"umschreiben – geändert wird ausschließlich über einen der beiden Belege: die "
|
||
"<b>Berichtigung</b> (die Rechnung behält ihre Nummer) oder den <b>Storno</b> (eigener Beleg "
|
||
"mit eigener Nummer). Vier Sperren sorgen dafür:")
|
||
s.append(tabelle([
|
||
["Sperre", "Wirkung"],
|
||
["Eine vergebene Nummer wird nicht wiederverwendet",
|
||
"Vor dem Schreiben prüft das Programm den Rechnungsordner <b>und</b> das gemeinsame "
|
||
"Nummernbuch. Ist die Nummer belegt, wird die vorhandene Rechnung <b>nicht</b> "
|
||
"überschrieben, sondern die nächste freie Nummer angeboten."],
|
||
["Der Zähler kann nicht zurück",
|
||
"Auch nicht über die Einstellungen. Als Quelle gelten die vorhandenen Rechnungs-PDFs und "
|
||
"das Nummernbuch – wer den Zähler kleiner setzt, hebt ihn beim nächsten Beleg "
|
||
"automatisch wieder an."],
|
||
["Beim Storno ist das Nummernfeld gesperrt",
|
||
"Dort vergibt ausschließlich das Programm: die Nummer des Stornos und – wenn eine neue "
|
||
"Rechnung folgt – gleich auch deren Nummer, damit die Kette auf dem Blatt steht."],
|
||
["Keine Nummer auf Vorrat",
|
||
"Verweist das Storno auf eine bereits bestehende Rechnung oder folgt gar nichts, wird "
|
||
"keine Nummer reserviert."],
|
||
], st, [58 * mm, CONTENT_W - 58 * mm]))
|
||
s.append(Spacer(1, 2 * mm))
|
||
P("Eine lückenlose Folge verlangt das Gesetz nicht – eine <b>einmalige</b> schon "
|
||
"(§ 14 Abs. 4 Nr. 4 UStG). Vorwärts darf die Nummerierung also springen, zurück nicht.",
|
||
"klein")
|
||
s.append(Spacer(1, 2 * mm))
|
||
|
||
P("Doppelte Rechnungsnummern werden angezeigt, nicht weggerechnet", "h")
|
||
P("Das Steuerbuch meldet jede Nummer, die mehr als einmal vergeben wurde – rot in der Liste, "
|
||
"im Monatsreiter markiert und in einer eigenen Übersicht mit allen betroffenen Belegen. "
|
||
"Gerechnet wird über das ganze Jahr, weil dieselbe Nummer in zwei Monaten liegen kann. "
|
||
"<b>Die Beträge bleiben dabei in allen Summen stehen</b>: das Buch soll mit dem "
|
||
"Rechnungsordner übereinstimmen. Was zu tun ist, entscheidet der Betrieb – der vorgesehene "
|
||
"Weg steht im Programm: eine der beiden Rechnungen stornieren und, falls nötig, mit freier "
|
||
"Nummer neu ausstellen. Sobald das geschehen ist, verschwindet die Meldung von selbst, weil "
|
||
"eine stornierte Rechnung keine offene mehr ist.")
|
||
|
||
P("Wie ein Storno in die Steueranmeldung eingeht", "h")
|
||
P("Die aufgehobene Rechnung bleibt mit ihrem Plus im Monat der Rechnung stehen, die "
|
||
"Stornorechnung steht mit ihrem Minus im Monat des Stornos. Über das Jahr gleicht sich das "
|
||
"aus. Bewusst wurde der sichere Weg gewählt: im ersten Monat wird eher zu viel gemeldet und "
|
||
"im zweiten entsprechend weniger – nie zuerst zu wenig. Fällt eine Korrektur in einen Monat, "
|
||
"der bereits angemeldet ist, ist eine berichtigte Anmeldung vorgesehen; das amtliche Formular "
|
||
"hat dafür ein eigenes Ankreuzfeld, und die Anmeldung steht ohnehin unter dem Vorbehalt der "
|
||
"Nachprüfung.")
|
||
P("Damit das Blatt fürs Amt aus sich heraus verständlich ist, trägt jede betroffene Zeile eine "
|
||
"<b>Begründung</b>, und zwar als <b>Kette, die vorwärts zeigt</b> – jede Zeile sagt, wo es "
|
||
"weitergeht:")
|
||
s.append(tabelle([
|
||
["Zeile im Bericht", "Begründung", "Bedeutung"],
|
||
["die aufgehobene Rechnung", "storniert mit 2026-901",
|
||
"hier steht, welcher Beleg sie aufhebt"],
|
||
["die Stornorechnung", "neue Rechnung 2026-902",
|
||
"hier steht, unter welcher Nummer die Leistung jetzt abgerechnet ist"],
|
||
["die Stornorechnung im Doppelungsfall", "neu abgerechnet mit 2026-343",
|
||
"die Leistung war bereits abgerechnet – das ist die Rechnung, die bleibt"],
|
||
["die neue Rechnung", "Neuausstellung",
|
||
"sie ersetzt die aufgehobene"],
|
||
], st, [45 * mm, 47 * mm, CONTENT_W - 92 * mm]))
|
||
s.append(Spacer(1, 2 * mm))
|
||
P("So findet man von jedem Beleg aus den nächsten, auch wenn Rechnung und Storno in "
|
||
"verschiedenen Monaten liegen und gar nicht auf demselben Blatt stehen.")
|
||
|
||
P("Die Rechtsgrundlagen im Einzelnen", "h")
|
||
P("„Satzung“ meint die Satzung über die Erhebung einer Beherbergungssteuer in der Stadt "
|
||
"Markkleeberg vom 6. Februar 2024, anzuwenden auf Beherbergungsleistungen ab dem "
|
||
"1. Januar 2025.", "klein")
|
||
s.append(tabelle([
|
||
["Vorschrift", "Inhalt", "Was im Programm daraus folgt"],
|
||
["§ 14 Abs. 4 Nr. 1 UStG", "Vollständiger Name und vollständige Anschrift des "
|
||
"Leistungsempfängers", "Empfängerfelder sind Pflichtfelder"],
|
||
["§ 14 Abs. 4 Nr. 4 UStG", "Fortlaufende Nummer, vom Aussteller <b>einmalig</b> vergeben",
|
||
"Doppelte Nummern werden gemeldet; Stornos und Neuausstellungen bekommen eigene Nummern, "
|
||
"es werden keine reserviert, die nicht gebraucht werden"],
|
||
["§ 31 Abs. 5 UStDV", "Eine Rechnung kann berichtigt werden; das Dokument muss sich "
|
||
"<b>spezifisch und eindeutig</b> auf die Rechnung beziehen",
|
||
"Berichtigungsblatt bezieht sich auf Nummer und Datum; das Storno nennt zusätzlich den "
|
||
"Empfänger – bei einer doppelt vergebenen Nummer wäre die Nummer allein kein "
|
||
"eindeutiger Bezug"],
|
||
["§ 14c Abs. 1 UStG", "Wer in einer Rechnung mehr Steuer ausweist, als er schuldet, "
|
||
"schuldet auch den Mehrbetrag",
|
||
"Deshalb wird eine doppelt geschriebene Rechnung storniert und nicht einfach liegen "
|
||
"gelassen; das Berichtigungsblatt sagt ausdrücklich, dass es keine zweite Leistung ist"],
|
||
["§ 146 Abs. 4 AO", "Eine Buchung darf nicht so verändert werden, dass der ursprüngliche "
|
||
"Inhalt nicht mehr feststellbar ist",
|
||
"Kein freies Bearbeiten von Buchungen mehr; jede Änderung entsteht über einen eigenen "
|
||
"Beleg (Storno oder Berichtigung), gelöscht wird nichts"],
|
||
["§ 147 AO (über § 7 Abs. 4 der Satzung)", "Aufbewahrung der Rechnungskopien und "
|
||
"Meldescheine, Vorlage auf Anforderung", "Rechnungen bleiben als PDF im Ordner; das "
|
||
"Steuerbuch liest sie und legt sie nicht neu an"],
|
||
["§ 3 Abs. 1 und 3 der Satzung",
|
||
"Bemessungsgrundlage ist das Übernachtungsentgelt einschließlich Umsatzsteuer; "
|
||
"5 % je Übernachtung, abgerundet auf volle Cent; höchstens 21 Tage",
|
||
"Die Steuer wird auf die Zwischensumme der Rechnung gerechnet und auf dem Beleg "
|
||
"gesondert ausgewiesen"],
|
||
["§ 6 der Satzung", "Der Steueranspruch entsteht mit Beginn der Beherbergung; ohne Entgelt "
|
||
"entsteht keine Steuer", "Ein Angebot löst nichts aus; erst die Rechnung wird gebucht"],
|
||
["§ 7 Abs. 3 der Satzung", "Meldescheine mit Name, Wohnanschrift, Geburtsdatum sowie "
|
||
"An- und Abreise", "Die Angaben liegen vor und gehören auf die Rechnung"],
|
||
["§ 7 Abs. 5 der Satzung", "Die im Kalendermonat <b>vereinnahmte</b> Steuer ist bis zum "
|
||
"10. des Folgemonats selbst zu berechnen, anzumelden und abzuführen",
|
||
"Monatsbericht als Aufstellung zur Anmeldung; Nullmonate bleiben nachvollziehbar"],
|
||
["§§ 164, 168 AO (über § 3 Abs. 1 SächsKAG)", "Die Steueranmeldung steht einer Festsetzung "
|
||
"unter Vorbehalt der Nachprüfung gleich",
|
||
"Ein bereits angemeldeter Monat kann berichtigt werden – deshalb ist der sichere Weg, "
|
||
"im Zweifel eher zu viel zu melden"],
|
||
], st, [42 * mm, 55 * mm, CONTENT_W - 97 * mm]))
|
||
s.append(Spacer(1, 4 * mm))
|
||
|
||
P("Zwei Punkte, die noch offen sind", "h")
|
||
P("<b>1. Steuerbefreite Übernachtungen.</b> Nach § 4 Abs. 1 Nr. 1 der Satzung sind Kinder und "
|
||
"Jugendliche bis zum vollendeten 18. Lebensjahr von der Beherbergungssteuer befreit. Im "
|
||
"Leistungskatalog gibt es eine eigene Position „Personengebühr Kinder ab 2 Jahre bis 14 "
|
||
"Jahre“. Dieser Umsatz wird derzeit mitversteuert. Das amtliche Anmeldeformular sieht dafür "
|
||
"eine eigene Zeile vor („abzüglich Umsätze aus steuerbefreiten Übernachtungen“). Bis zur "
|
||
"Umstellung wird also eher zu viel als zu wenig gemeldet.")
|
||
P("<b>2. Abrundung je Übernachtung.</b> Nach § 3 Abs. 3 der Satzung wird der Steueranteil je "
|
||
"einzelner Übernachtung auf volle Cent abgerundet. Derzeit werden 5 % auf die Summe "
|
||
"gerechnet. Das amtliche Formular trägt dem mit einer eigenen Zeile Rechnung "
|
||
"(„tatsächlich einbehaltene Beherbergungssteuer“).")
|
||
P("Beide Punkte wirken zugunsten der Stadt und nicht zulasten – sie führen zu einer eher zu "
|
||
"hohen, nicht zu einer zu niedrigen Anmeldung.", "klein")
|
||
|
||
P("Zum Schluss", "h")
|
||
P("Die Programme ändern nichts rückwirkend und löschen nichts. Jede Korrektur ist ein eigener "
|
||
"Beleg mit eigener Nummer, der sagt, worauf er sich bezieht. Damit lässt sich zu jedem "
|
||
"Zeitpunkt nachvollziehen, welche Rechnung zu welchem Gast gehört, welche aufgehoben wurde "
|
||
"und womit – im Rechnungsordner, im Steuerbuch und auf dem Blatt für die Stadt.")
|
||
|
||
doc = BaseDocTemplate(pfad, pagesize=A4,
|
||
leftMargin=MARGIN_L, rightMargin=MARGIN_R,
|
||
topMargin=30 * mm, bottomMargin=18 * mm,
|
||
title="Änderungen an Rechnungsschreibung und Steuerbuch",
|
||
author=cfg.get("name", ""))
|
||
rahmen = Frame(MARGIN_L, 18 * mm, CONTENT_W, PAGE_H - 30 * mm - 18 * mm, id="f")
|
||
doc.addPageTemplates([PageTemplate(id="seite", frames=[rahmen],
|
||
onPage=lambda c, d: kopf_fuss(c, d, cfg))])
|
||
doc.build(s)
|
||
return pfad
|
||
|
||
|
||
if __name__ == "__main__":
|
||
ziel = sys.argv[1] if len(sys.argv) > 1 else os.path.join(
|
||
os.path.expanduser("~"), "Desktop",
|
||
"Campinghof_Aenderungen_Rechnungswesen_2026-09-04.pdf")
|
||
cfg = firma_lib.lade()
|
||
bauen(ziel, cfg)
|
||
print("geschrieben:", ziel, os.path.getsize(ziel), "Bytes")
|