diff --git a/Website/modules/emailservice/email.py b/Website/modules/emailservice/email.py index 866e145..f341c86 100644 --- a/Website/modules/emailservice/email.py +++ b/Website/modules/emailservice/email.py @@ -351,42 +351,98 @@ def generate_main_contract_pdf( pdf.output(output_path) return output_path -def generate_invoice_fpdf(school_name: str, address: str, price: str, date: str, software_name: str, output_path: str): - """Erstellt eine Rechnung als PDF mittels FPDF2.""" +def generate_invoice_fpdf( + 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.add_page() - # Header - pdf.set_font("Helvetica", style="B", size=18) - pdf.cell(0, 10, "Rechnung", new_x="LMARGIN", new_y="NEXT", align="L") + # 2. Kopfzeile: Aussteller-Informationen (Rechtsbündig) + pdf.set_font("Helvetica", size=9) + 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) - # Adress- und Datumsblock - pdf.set_font("Helvetica", size=12) - pdf.cell(0, 8, f"Datum: {date}", new_x="LMARGIN", new_y="NEXT", align="R") - pdf.cell(0, 8, school_name, new_x="LMARGIN", new_y="NEXT") - pdf.cell(0, 8, address, new_x="LMARGIN", new_y="NEXT") + # 3. Empfänger-Informationen (Linksbündig) + pdf.set_font("Helvetica", size=11) + pdf.cell(0, 5, school_name, new_x="LMARGIN", new_y="NEXT") + pdf.cell(0, 5, address, new_x="LMARGIN", new_y="NEXT") pdf.ln(15) - # Leistung - pdf.set_font("Helvetica", style="B", size=12) - pdf.cell(0, 10, "Leistungsbeschreibung:", new_x="LMARGIN", new_y="NEXT") - pdf.set_font("Helvetica", size=12) - pdf.cell(0, 8, f"Nutzungslizenz / Akkreditierung für {software_name}", new_x="LMARGIN", new_y="NEXT") + # 4. Rechnungs-Metadaten + pdf.set_font("Helvetica", style="B", size=16) + pdf.cell(0, 10, "Rechnung", new_x="LMARGIN", new_y="NEXT") + pdf.set_font("Helvetica", size=10) + 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) - # Preis - pdf.set_font("Helvetica", style="B", size=12) - pdf.cell(0, 10, f"Zu zahlender Betrag: {price} EUR", new_x="LMARGIN", new_y="NEXT") - pdf.ln(20) + # 5. Positionen (Tabellenkopf) + pdf.set_font("Helvetica", style="B", size=10) + pdf.cell(20, 8, "Menge", border=1) + 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.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 pdf.output(output_path) + def send_accreditation_email( recipient: str, domain: str, @@ -416,7 +472,7 @@ def send_accreditation_email( output_path=contract_pdf_path ) - # 1b. Rechnung dynamisch mit FPDF erzeugen + # 1b. Rechnung dynamisch mit FPDF erzeugen inkl. Pflichtangaben generated_invoice_path = None if not invoice_path: 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): attachments.append(invoice_to_attach) - else: - print(f"[WARNING] Rechnung unter {invoice_to_attach} nicht gefunden.") if agb_pdf_path: attachments.append(agb_pdf_path) - else: - print(f"[WARNING] AGB unter static/(downloads) nicht gefunden.") if 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 try: @@ -519,11 +569,11 @@ def send_accreditation_email( if os.path.exists(contract_pdf_path): try: os.remove(contract_pdf_path) - except OSError as e: - print(f"[WARNING] Konnte temporären Vertrag nicht löschen: {e}") + except OSError: + pass if generated_invoice_path and os.path.exists(generated_invoice_path): try: os.remove(generated_invoice_path) - except OSError as e: - print(f"[WARNING] Konnte temporäre Rechnung nicht löschen: {e}") \ No newline at end of file + except OSError: + pass \ No newline at end of file