Erklaerung fuers Amt / die Steuerberaterin als PDF
Auf seine Bitte: ein Blatt, das erklaert, WAS an Rechnungsschreibung und Steuerbuch geaendert wurde, WARUM, und auf welche Paragraphen es sich stuetzt. Drei Seiten, im Briefkopf des Betriebs. Inhalt: Ausgangslage (dieselbe Leistung mehrfach im Ordner, unvollstaendige Empfaengerangaben), die zwei festen Wege (Berichtigung / Storno), die drei Storno-Faelle A/B/C, Pflichtfelder, Umgang mit doppelten Nummern, wie ein Storno in die Steueranmeldung eingeht - dazu eine Tabelle mit elf Fundstellen (UStG, UStDV, AO, SaechsKAG, Beherbergungssteuersatzung Markkleeberg) und was im Programm daraus folgt. Am Ende die zwei Punkte, die noch offen sind: steuerbefreite Uebernachtungen (§ 4 Abs. 1 Nr. 1 der Satzung - Kinder bis 18, im Katalog gibt es eine eigene Position dafuer) und die Abrundung je Uebernachtung (§ 3 Abs. 3). Beide wirken zugunsten der Stadt, sind aber ungebaut - das gehoert offen aufs Blatt und nicht verschwiegen. Als Skript und nicht von Hand geschrieben, weil der Text Paragraphen nennt: aendert sich das Programm, aendert man den Text hier und erzeugt das Blatt neu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017bEsoFUk16DfnNA7MjY36H
This commit is contained in:
parent
ef3eef2852
commit
965278020a
1 changed files with 267 additions and 0 deletions
267
doku_aenderungen_pdf.py
Normal file
267
doku_aenderungen_pdf.py
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
# -*- 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("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>: „storniert mit 2026-901“, „Storno zu 2026-900“, „Neuausstellung“. "
|
||||
"So ist auch dann nachvollziehbar, was zusammengehört, 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")
|
||||
Loading…
Add table
Add a link
Reference in a new issue