changes to the rehcnungs generation
This commit is contained in:
@@ -351,42 +351,98 @@ def generate_main_contract_pdf(
|
|||||||
pdf.output(output_path)
|
pdf.output(output_path)
|
||||||
return output_path
|
return output_path
|
||||||
|
|
||||||
def generate_invoice_fpdf(school_name: str, address: str, price: str, date: str, software_name: str, output_path: str):
|
def generate_invoice_fpdf(
|
||||||
"""Erstellt eine Rechnung als PDF mittels FPDF2."""
|
school_name: str,
|
||||||
|
address: str,
|
||||||
|
price: str,
|
||||||
|
date: str,
|
||||||
|
software_name: str,
|
||||||
|
output_path: str,
|
||||||
|
invoice_number: str = "RE-2026-08-001", # Fortlaufende Rechnungsnummer
|
||||||
|
delivery_date: str = "Entspricht Rechnungsdatum",
|
||||||
|
issuer_name: str = "Invario GmbH",
|
||||||
|
issuer_address: str = "Musterstraße 1\n12345 Musterstadt",
|
||||||
|
issuer_tax_id: str = "USt-IdNr.: DE123456789"
|
||||||
|
):
|
||||||
|
"""Erstellt eine rechtssichere Rechnung inkl. aller Pflichtangaben mittels FPDF2."""
|
||||||
|
|
||||||
|
# 1. Beträge berechnen (Annahme: übergebener Preis ist Brutto)
|
||||||
|
try:
|
||||||
|
brutto = float(price.replace('.', '').replace(',', '.'))
|
||||||
|
except ValueError:
|
||||||
|
brutto = 250.00
|
||||||
|
|
||||||
|
netto = brutto / 1.19
|
||||||
|
mwst = brutto - netto
|
||||||
|
|
||||||
pdf = FPDF()
|
pdf = FPDF()
|
||||||
pdf.add_page()
|
pdf.add_page()
|
||||||
|
|
||||||
# Header
|
# 2. Kopfzeile: Aussteller-Informationen (Rechtsbündig)
|
||||||
pdf.set_font("Helvetica", style="B", size=18)
|
pdf.set_font("Helvetica", size=9)
|
||||||
pdf.cell(0, 10, "Rechnung", new_x="LMARGIN", new_y="NEXT", align="L")
|
pdf.cell(0, 5, f"{issuer_name}", new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
for line in issuer_address.split('\n'):
|
||||||
|
pdf.cell(0, 5, line, new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
pdf.cell(0, 5, issuer_tax_id, new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
pdf.ln(10)
|
pdf.ln(10)
|
||||||
|
|
||||||
# Adress- und Datumsblock
|
# 3. Empfänger-Informationen (Linksbündig)
|
||||||
pdf.set_font("Helvetica", size=12)
|
pdf.set_font("Helvetica", size=11)
|
||||||
pdf.cell(0, 8, f"Datum: {date}", new_x="LMARGIN", new_y="NEXT", align="R")
|
pdf.cell(0, 5, school_name, new_x="LMARGIN", new_y="NEXT")
|
||||||
pdf.cell(0, 8, school_name, new_x="LMARGIN", new_y="NEXT")
|
pdf.cell(0, 5, address, new_x="LMARGIN", new_y="NEXT")
|
||||||
pdf.cell(0, 8, address, new_x="LMARGIN", new_y="NEXT")
|
|
||||||
pdf.ln(15)
|
pdf.ln(15)
|
||||||
|
|
||||||
# Leistung
|
# 4. Rechnungs-Metadaten
|
||||||
pdf.set_font("Helvetica", style="B", size=12)
|
pdf.set_font("Helvetica", style="B", size=16)
|
||||||
pdf.cell(0, 10, "Leistungsbeschreibung:", new_x="LMARGIN", new_y="NEXT")
|
pdf.cell(0, 10, "Rechnung", new_x="LMARGIN", new_y="NEXT")
|
||||||
pdf.set_font("Helvetica", size=12)
|
pdf.set_font("Helvetica", size=10)
|
||||||
pdf.cell(0, 8, f"Nutzungslizenz / Akkreditierung für {software_name}", new_x="LMARGIN", new_y="NEXT")
|
pdf.cell(0, 5, f"Rechnungsnummer: {invoice_number}", new_x="LMARGIN", new_y="NEXT")
|
||||||
|
pdf.cell(0, 5, f"Rechnungsdatum: {date}", new_x="LMARGIN", new_y="NEXT")
|
||||||
|
pdf.cell(0, 5, f"Liefer-/Leistungszeitpunkt: {delivery_date}", new_x="LMARGIN", new_y="NEXT")
|
||||||
pdf.ln(10)
|
pdf.ln(10)
|
||||||
|
|
||||||
# Preis
|
# 5. Positionen (Tabellenkopf)
|
||||||
pdf.set_font("Helvetica", style="B", size=12)
|
pdf.set_font("Helvetica", style="B", size=10)
|
||||||
pdf.cell(0, 10, f"Zu zahlender Betrag: {price} EUR", new_x="LMARGIN", new_y="NEXT")
|
pdf.cell(20, 8, "Menge", border=1)
|
||||||
pdf.ln(20)
|
pdf.cell(100, 8, "Bezeichnung / Art der Leistung", border=1)
|
||||||
|
pdf.cell(35, 8, "Einzelpreis (Netto)", border=1, align="R")
|
||||||
|
pdf.cell(35, 8, "Gesamt (Netto)", border=1, new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
|
||||||
# Footer
|
# Positionen (Zeile)
|
||||||
pdf.set_font("Helvetica", size=10)
|
pdf.set_font("Helvetica", size=10)
|
||||||
pdf.cell(0, 8, "Bitte ueberweisen Sie den offenen Betrag innerhalb von 14 Tagen nach Rechnungserhalt.", new_x="LMARGIN", new_y="NEXT")
|
pdf.cell(20, 8, "1", border=1)
|
||||||
|
pdf.cell(100, 8, f"Akkreditierung für {software_name}", border=1)
|
||||||
|
pdf.cell(35, 8, f"{netto:.2f} EUR", border=1, align="R")
|
||||||
|
pdf.cell(35, 8, f"{netto:.2f} EUR", border=1, new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
pdf.ln(5)
|
||||||
|
|
||||||
|
# 6. Summenblock
|
||||||
|
pdf.set_left_margin(120) # Einrücken für die Summen
|
||||||
|
pdf.cell(35, 8, "Summe Netto:", align="R")
|
||||||
|
pdf.cell(35, 8, f"{netto:.2f} EUR", new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
|
||||||
|
pdf.cell(35, 8, "zzgl. 19% USt.:", align="R")
|
||||||
|
pdf.cell(35, 8, f"{mwst:.2f} EUR", new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
|
||||||
|
# Skonto / Minderungen (als Platzhalter bei 0)
|
||||||
|
pdf.cell(35, 8, "Abzüglich Skonto:", align="R")
|
||||||
|
pdf.cell(35, 8, "0.00 EUR", new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
|
||||||
|
pdf.set_font("Helvetica", style="B", size=11)
|
||||||
|
pdf.cell(35, 10, "Gesamtbetrag (Brutto):", align="R")
|
||||||
|
pdf.cell(35, 10, f"{brutto:.2f} EUR", new_x="LMARGIN", new_y="NEXT", align="R")
|
||||||
|
|
||||||
|
# 7. Zahlungsbedingungen / Footer
|
||||||
|
pdf.set_left_margin(10) # Rand zurücksetzen
|
||||||
|
pdf.ln(10)
|
||||||
|
pdf.set_font("Helvetica", size=10)
|
||||||
|
pdf.cell(0, 5, "Bitte überweisen Sie den fälligen Betrag innerhalb von 14 Tagen ohne Abzüge auf das unten", new_x="LMARGIN", new_y="NEXT")
|
||||||
|
pdf.cell(0, 5, "angegebene Konto. Es wurde keine Minderung im Voraus vereinbart.", new_x="LMARGIN", new_y="NEXT")
|
||||||
|
|
||||||
# Speichern
|
# Speichern
|
||||||
pdf.output(output_path)
|
pdf.output(output_path)
|
||||||
|
|
||||||
|
|
||||||
def send_accreditation_email(
|
def send_accreditation_email(
|
||||||
recipient: str,
|
recipient: str,
|
||||||
domain: str,
|
domain: str,
|
||||||
@@ -416,7 +472,7 @@ def send_accreditation_email(
|
|||||||
output_path=contract_pdf_path
|
output_path=contract_pdf_path
|
||||||
)
|
)
|
||||||
|
|
||||||
# 1b. Rechnung dynamisch mit FPDF erzeugen
|
# 1b. Rechnung dynamisch mit FPDF erzeugen inkl. Pflichtangaben
|
||||||
generated_invoice_path = None
|
generated_invoice_path = None
|
||||||
if not invoice_path:
|
if not invoice_path:
|
||||||
invoice_filename = f"Rechnung_Invario_{safe_school_name}.pdf"
|
invoice_filename = f"Rechnung_Invario_{safe_school_name}.pdf"
|
||||||
@@ -498,18 +554,12 @@ def send_accreditation_email(
|
|||||||
|
|
||||||
if invoice_to_attach and os.path.exists(invoice_to_attach):
|
if invoice_to_attach and os.path.exists(invoice_to_attach):
|
||||||
attachments.append(invoice_to_attach)
|
attachments.append(invoice_to_attach)
|
||||||
else:
|
|
||||||
print(f"[WARNING] Rechnung unter {invoice_to_attach} nicht gefunden.")
|
|
||||||
|
|
||||||
if agb_pdf_path:
|
if agb_pdf_path:
|
||||||
attachments.append(agb_pdf_path)
|
attachments.append(agb_pdf_path)
|
||||||
else:
|
|
||||||
print(f"[WARNING] AGB unter static/(downloads) nicht gefunden.")
|
|
||||||
|
|
||||||
if avv_pdf_path:
|
if avv_pdf_path:
|
||||||
attachments.append(avv_pdf_path)
|
attachments.append(avv_pdf_path)
|
||||||
else:
|
|
||||||
print(f"[WARNING] AVV unter static/(downloads) nicht gefunden.")
|
|
||||||
|
|
||||||
# 5. E-Mail versenden & temporäre Dateien aufräumen
|
# 5. E-Mail versenden & temporäre Dateien aufräumen
|
||||||
try:
|
try:
|
||||||
@@ -519,11 +569,11 @@ def send_accreditation_email(
|
|||||||
if os.path.exists(contract_pdf_path):
|
if os.path.exists(contract_pdf_path):
|
||||||
try:
|
try:
|
||||||
os.remove(contract_pdf_path)
|
os.remove(contract_pdf_path)
|
||||||
except OSError as e:
|
except OSError:
|
||||||
print(f"[WARNING] Konnte temporären Vertrag nicht löschen: {e}")
|
pass
|
||||||
|
|
||||||
if generated_invoice_path and os.path.exists(generated_invoice_path):
|
if generated_invoice_path and os.path.exists(generated_invoice_path):
|
||||||
try:
|
try:
|
||||||
os.remove(generated_invoice_path)
|
os.remove(generated_invoice_path)
|
||||||
except OSError as e:
|
except OSError:
|
||||||
print(f"[WARNING] Konnte temporäre Rechnung nicht löschen: {e}")
|
pass
|
||||||
Reference in New Issue
Block a user