Files
Key-service-Server/Website/modules/emailservice/email.py
T

588 lines
26 KiB
Python

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
import smtplib
import time
import os
import tempfile
from fpdf import FPDF
from flask import current_app
SELLER_INFO = {
"company_name": "Invario UG",
"sub_title": "Software Development",
"owner": "Invario Team",
"street": "Am Sportplatz 10",
"zip_city": "83052 Bruckmühl",
"email": "info@invario-software.de",
"website": "https://invario-software.de",
"bank_name": "Commerzbank AG",
"iban": "DE27 7114 0041 0183 3136 00",
"bic": "COBADEFFXXX",
"tax_number": "143/100/12345",
"vat_id": "HRB 35441"
}
def _build_smtp_client():
smtp = smtplib.SMTP(
"mail.invario-software.de",
587,
timeout=10,
)
smtp.ehlo()
if True: # Always use TLS for security
smtp.starttls()
smtp.ehlo()
smtp.login("no-reply@invario-software.de", "#,EATwIn,68")
return smtp
def send(
email: list | str,
subject: str,
text_body: str,
html_body: str = None,
attachments: list = None
) -> bool:
"""Sends the email with Plain Text, HTML, and optional File Attachments support."""
if isinstance(email, str):
email = [email]
if attachments is None:
attachments = []
TEXT_SIGNATURE = (
"\n\n--\n"
"Mit freundlichen Grüßen\n"
"Ihr Invario Team\n\n"
"Invario UG\n"
"Am Sportplatz 10\n"
"83052 Bruckmühl"
)
HTML_SIGNATURE = """
<br><br>
<table cellpadding="0" cellspacing="0" border="0" style="font-family: Arial, Helvetica, sans-serif; font-size: 13px; color: #555555; line-height: 1.5; border-top: 1px solid #eaebed; padding-top: 15px; width: 100%;">
<tr>
<td>
<p style="margin:0 0 5px 0;">Mit freundlichen Grüßen,</p>
<p style="margin:0;"><strong style="font-size:15px; color: #333333;">Ihr Invario Team</strong></p><br>
<p style="margin:0 0 0 0; font-size: 12px; color: #888888;">
<strong>Invario UG</strong><br>
Am Sportplatz 10<br>
83052 Bruckmühl
</p>
</td>
</tr>
</table>
"""
text_content = f"{text_body}{TEXT_SIGNATURE}"
if html_body:
html_content = f"<html><body style='background-color: #f9f9f9; padding: 20px;'><div style='background-color: #ffffff; padding: 30px; border-radius: 8px; max-width: 600px; margin: 0 auto; box-shadow: 0px 2px 5px rgba(0,0,0,0.05);'>{html_body}{HTML_SIGNATURE}</div></body></html>"
else:
# Fallback if only text is provided
html_safe_text = text_body.replace('\n', '<br>')
html_content = f"<html><body><p style='font-family: Arial, sans-serif; color: #333333;'>{html_safe_text}</p>{HTML_SIGNATURE}</body></html>"
mails_per_second = 10
interval = 1.0 / mails_per_second
smtp = None
try:
smtp = _build_smtp_client()
for i, recipient in enumerate(email):
start_time = time.time()
# Root message structure for email with attachments
msg = MIMEMultipart("mixed")
msg["Subject"] = subject
msg["From"] = "Invario Team <no-reply@invario-software.de>"
msg["To"] = str(recipient)
# Sub-container for Plain Text and HTML bodies
msg_body = MIMEMultipart("alternative")
msg_body.attach(MIMEText(text_content, "plain"))
msg_body.attach(MIMEText(html_content, "html"))
msg.attach(msg_body)
# Attach files if provided
for file_path in attachments:
if file_path and os.path.isfile(file_path):
filename = os.path.basename(file_path)
with open(file_path, "rb") as file:
part = MIMEApplication(file.read(), Name=filename)
part['Content-Disposition'] = f'attachment; filename="{filename}"'
msg.attach(part)
smtp.sendmail(
from_addr="no-reply@invario-software.de",
to_addrs=[recipient],
msg=msg.as_string()
)
elapsed_time = time.time() - start_time
sleep_time = interval - elapsed_time
if sleep_time > 0 and i < len(email) - 1:
time.sleep(sleep_time)
return True
except Exception as e:
print(f"Debug: Fehler beim Senden der E-Mail: {e}")
return False
finally:
if smtp:
try:
smtp.quit()
except Exception:
pass
def send_register_token(email: str, token: str) -> bool:
"""Sends a professionally styled registration token to the user."""
subject = "Aktion erforderlich: Ihr Bestätigungscode für Invario"
text_note = (
"Herzlich willkommen bei Invario!\n\n"
"Um die Einrichtung Ihres Kontos abzuschließen und Ihre E-Mail-Adresse zu bestätigen, "
"geben Sie bitte den folgenden Sicherheitscode ein:\n\n"
f"Code: {token}\n\n"
"Dieser Code ist aus Sicherheitsgründen nur für begrenzte Zeit gültig. "
"Falls Sie diese Registrierung nicht angefordert haben, können Sie diese E-Mail einfach ignorieren."
)
html_note = f"""
<div style="font-family: Arial, Helvetica, sans-serif; color: #333333;">
<h2 style="color: #2c3e50; margin-top: 0;">Willkommen bei Invario!</h2>
<p style="font-size: 15px; line-height: 1.6;">
Vielen Dank für Ihre Registrierung. Um die Einrichtung Ihres Kontos abzuschließen und Ihre E-Mail-Adresse zu verifizieren, verwenden Sie bitte den folgenden Bestätigungscode:
</p>
<div style="background-color: #f1f5f9; border-left: 4px solid #3b82f6; border-radius: 4px; padding: 25px; text-align: center; margin: 30px 0;">
<span style="font-size: 32px; font-weight: bold; letter-spacing: 8px; color: #1e293b;">{token}</span>
</div>
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">
Dieser Code ist aus Sicherheitsgründen nur für eine begrenzte Zeit gültig.<br>
Falls Sie kein Konto bei uns erstellt haben, können Sie diese E-Mail sicher ignorieren.
</p>
</div>
"""
return send(email, subject, text_body=text_note, html_body=html_note)
def send_password_reset_token(email: str, token: str) -> bool:
"""Sends a professionally styled password reset token to the user."""
subject = "Aktion erforderlich: Ihr Passwort-Zurücksetzungs-Code für Invario"
text_note = (
"Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts gestellt.\n\n"
"Um Ihr Passwort zurückzusetzen, geben Sie bitte den folgenden Sicherheitscode ein:\n\n"
f"Code: {token}\n\n"
"Dieser Code ist aus Sicherheitsgründen nur für begrenzte Zeit gültig. "
"Falls Sie diese Anfrage nicht gestellt haben, können Sie diese E-Mail einfach ignorieren."
)
html_note = f"""
<div style="font-family: Arial, Helvetica, sans-serif; color: #333333;">
<h2 style="color: #2c3e50; margin-top: 0;">Passwort zurücksetzen</h2>
<p style="font-size: 15px; line-height: 1.6;">
Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts gestellt. Um fortzufahren, verwenden Sie bitte den folgenden Bestätigungscode:
</p>
<div style="background-color: #f1f5f9; border-left: 4px solid #3b82f6; border-radius: 4px; padding: 25px; text-align: center; margin: 30px 0;">
<span style="font-size: 32px; font-weight: bold; letter-spacing: 8px; color: #1e293b;">{token}</span>
</div>
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">
Dieser Code ist aus Sicherheitsgründen nur für eine begrenzte Zeit gültig.<br>
Falls Sie kein Zurücksetzen des Passworts angefordert haben, können Sie diese E-Mail sicher ignorieren.
</p>
</div>
"""
return send(email, subject, text_body=text_note, html_body=html_note)
def generate_main_contract_pdf(
school_name: str,
address: str,
price: str,
date: str,
output_path: str,
software_name: str = "Invario Inventarsystem"
):
"""Generiert den Hauptvertrag mit zentrierter und aufeinander abgestimmter Kopf- und Fußzeilen-Formatierung."""
pdf = FPDF()
pdf.add_page()
pdf.set_auto_page_break(auto=True, margin=15)
# --- KOPFBEREICH ---
pdf.set_font("Helvetica", style="B", size=13)
pdf.cell(0, 6, "Vertrag über die Nutzung der Software-Lizenz", ln=True, align="C")
pdf.cell(0, 6, f'"{software_name}"', ln=True, align="C")
pdf.ln(6)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 5, "Zwischen", ln=True, align="C")
pdf.set_font("Helvetica", size=10)
pdf.cell(0, 5, "Invario UG", ln=True, align="C")
pdf.cell(0, 5, "Am Sportplatz 10, 83052 Bruckmühl", ln=True, align="C")
pdf.set_font("Helvetica", style="I", size=9)
pdf.cell(0, 5, "- Anbieter / Lizenzgeber -", ln=True, align="C")
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 5, "und", ln=True, align="C")
pdf.set_font("Helvetica", size=10)
pdf.cell(0, 5, f"{school_name}", ln=True, align="C")
pdf.cell(0, 5, f"{address}", ln=True, align="C")
pdf.set_font("Helvetica", style="I", size=9)
pdf.cell(0, 5, "- Auftraggeber / Schule -", ln=True, align="C")
pdf.ln(8)
# --- PARAGRAPHEN ---
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "1. Vertragsgegenstand", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
f'(1) Der Anbieter stellt dem Auftraggeber die Software "{software_name}" (nachfolgend "Software") als webbasierte Anwendung im Wege des Software-as-a-Service (SaaS) zur Verfügung. Die Software wird auf der vom Anbieter genutzten Infrastruktur betrieben.\n'
"(2) Dieser Hauptvertrag wird durch folgende Dokumente ergänzt, die als integrale Bestandteile dieses Vertrages gelten:\n"
"a) Anlage 1: Allgemeine Geschäftsbedingungen (AGB) und Lizenzbedingungen (EULA) des Anbieters\n"
"b) Anlage 2: Auftragsverarbeitungsvertrag (AVV) gemäß Art. 28 DS-GVO nebst technischen und organisatorischen Maßnahmen (TOM)"
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "2. Vertragsbeginn", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
"(1) Dieser Vertrag kommt entweder mit dem Datum der beidseitigen Unterzeichnung dieses Dokumentes oder durch die Nutzung der integrierten digitalen Bestellfunktion im Invario-Portal durch einen hierzu autorisierten Vertreter des Auftraggebers zustande.\n"
"(2) Die Vertragslaufzeit beginnt mit der Bereitstellung der Zugangsdaten durch den Anbieter."
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "3. Laufzeit und Kündigung", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
"(1) Der Vertrag wird zunächst für die Dauer von zwölf (12) Monaten geschlossen (Mindestlaufzeit).\n"
"(2) Er verlängert sich automatisch um jeweils ein weiteres Jahr, sofern er nicht von einer Partei mit einer Frist von drei (3) Monaten zum Ende der jeweiligen Laufzeit gekündigt wird.\n"
"(3) Die Kündigung kann in Textform (z. B. per E-Mail) oder über die dafür vorgesehene Funktion im Invario-Portal erfolgen. Das Recht zur außerordentlichen Kündigung aus wichtigem Grund bleibt unberührt."
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "4. Vergütung und Bezahlung", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
f"(1) Für die Nutzung der Software zahlt der Auftraggeber eine jährliche Lizenzgebühr in Höhe von {price} EUR (zzgl. ggf. anfallender gesetzlicher Umsatzsteuer).\n"
"(2) Die Vergütung ist jeweils für ein Vertragsjahr im Voraus fällig.\n"
"(3) Der Rechnungsbetrag ist innerhalb von 30 Tagen nach Erhalt der Rechnung ohne Abzug zur Zahlung fällig."
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "5. Nutzung und Support", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
"(1) Der Umfang der Lizenzeinräumung sowie die Nutzungsbeschränkungen richten sich nach den Bestimmungen der anliegenden EULA (Anlage 1).\n"
"(2) Der technische Support wird innerhalb der in den AGB (Anlage 1) definierten Servicezeiten über das integrierte Ticket-System oder per E-Mail erbracht."
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "6. Datenschutz und Datensicherheit", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
"(1) Die Parteien verpflichten sich zur Einhaltung der datenschutzrechtlichen Bestimmungen. Die Details der Verarbeitung der personenbezogenen Daten werden im anliegenden AVV (Anlage 2) verbindlich geregelt.\n"
"(2) Der Anbieter sichert zu, dass die Speicherung und Verarbeitung der Daten des Auftraggebers ausschließlich in Rechenzentren mit Standort innerhalb der Bundesrepublik Deutschland erfolgt."
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "7. Haftung", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
"(1) Die Haftung des Anbieters richtet sich nach den Regelungen in Ziffer 7 der Allgemeinen Geschäftsbedingungen (Anlage 1).\n"
"(2) Für Unterbrechungen der Verfügbarkeit durch höhere Gewalt, Störungen im Bereich außerhalb des Übergabepunktes (z. B. Internet-Backbone) sowie für unvorhersehbare Sicherheitslücken, die nach dem aktuellen Stand der Technik zum Zeitpunkt des Auftretens noch nicht abwehrbar waren (Zero-Day-Exploits), haftet der Anbieter nur nach Maßgabe der Haftungsregelungen in den AGB, es sei denn, ihn trifft ein Verschulden an der verzögerten Behebung. Gesetzliche Datenschutzpflichten und Meldepflichten bleiben unberührt."
)
pdf.ln(4)
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "8. Schlussbestimmungen", ln=True)
pdf.set_font("Helvetica", size=10)
pdf.multi_cell(0, 5,
"(1) Änderungen und Ergänzungen dieses Vertrages bedürfen der Textform.\n"
"(2) Sollten einzelne Bestimmungen dieses Vertrages unwirksam oder undurchführbar sein oder werden, bleibt die Wirksamkeit der übrigen Bestimmungen hiervon unberührt. Anstelle der unwirksamen oder undurchführbaren Bestimmung gelten die gesetzlichen Vorschriften. Sofern es sich nicht um Allgemeine Geschäftsbedingungen handelt, verpflichten sich die Parteien, eine wirksame Regelung zu vereinbaren, die dem wirtschaftlichen Zweck der unwirksamen oder undurchführbaren Bestimmung möglichst nahekommt.\n"
"(3) Es gilt das Recht der Bundesrepublik Deutschland. Ausschließlicher Gerichtsstand für alle Streitigkeiten aus oder im Zusammenhang mit diesem Vertrag ist, soweit gesetzlich zulässig, der Sitz des Anbieters."
)
pdf.ln(6)
# --- UNTERSCHRIFTEN ---
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 6, "Unterschriften und Einverständnis", ln=True, align="C")
pdf.set_font("Helvetica", size=9)
pdf.multi_cell(0, 4, "Mit der Unterzeichnung bestätigen die Parteien den Abschluss des Hauptvertrages inklusive der beigefügten Anlagen (AGB, EULA und AVV).", align="C")
pdf.ln(8)
col_width = 85
start_x = (210 - (col_width * 2)) / 2
pdf.set_font("Helvetica", size=9)
pdf.set_x(start_x)
pdf.cell(col_width, 5, "Für den Auftraggeber", align="C", ln=False)
pdf.cell(col_width, 5, "Für den Anbieter", align="C", ln=True)
pdf.ln(8)
pdf.set_x(start_x)
pdf.cell(col_width, 5, f"{date} ________________________", align="C", ln=False)
pdf.cell(col_width, 5, f"{date} ___________ _____________", align="C", ln=True)
pdf.set_x(start_x)
pdf.cell(col_width, 5, "(Verantwortlicher)", align="C", ln=False)
pdf.cell(col_width, 5, "(Simon Brieger)", align="C", ln=True)
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,
invoice_number: str = "RE-2026-08-001"
):
"""Erstellt eine rechtssichere Rechnung inkl. Dankessatz und SELLER_INFO Daten 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()
# 2. Kopfzeile: Aussteller-Informationen aus SELLER_INFO (Rechtsbündig)
pdf.set_font("Helvetica", size=9)
pdf.cell(0, 5, f"{SELLER_INFO['company_name']} - {SELLER_INFO['sub_title']}", new_x="LMARGIN", new_y="NEXT", align="R")
pdf.cell(0, 5, SELLER_INFO['street'], new_x="LMARGIN", new_y="NEXT", align="R")
pdf.cell(0, 5, SELLER_INFO['zip_city'], new_x="LMARGIN", new_y="NEXT", align="R")
pdf.cell(0, 5, f"St.-Nr.: {SELLER_INFO['tax_number']} | USt-IdNr.: {SELLER_INFO['vat_id']}", new_x="LMARGIN", new_y="NEXT", align="R")
pdf.ln(10)
# 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)
# 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} (Fortlaufend)", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 5, f"Rechnungsdatum: {date}", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 5, "Liefer-/Leistungszeitraum: Entspricht Rechnungsdatum", new_x="LMARGIN", new_y="NEXT")
pdf.ln(8)
# Netter Dankessatz direkt über der Tabelle
pdf.set_font("Helvetica", style="I", size=10)
pdf.cell(0, 6, "Vielen Dank für Ihren Einkauf! Wir freuen uns sehr über das entgegengebrachte Vertrauen.", new_x="LMARGIN", new_y="NEXT")
pdf.ln(6)
# 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")
# Positionen (Zeile)
pdf.set_font("Helvetica", size=10)
pdf.cell(20, 8, "1", border=1)
pdf.cell(100, 8, f"Nutzungs Lizenz 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")
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, Bankverbindung & 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 Bruttobetrag innerhalb von 14 Tagen unter Angabe der", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 5, f"Rechnungsnummer ({invoice_number}) auf unser unten angegebenes Bankkonto. Es wurde kein Skonto vereinbart.", new_x="LMARGIN", new_y="NEXT")
pdf.ln(5)
# Bankinformationen Block aus SELLER_INFO
pdf.set_font("Helvetica", style="B", size=10)
pdf.cell(0, 5, "Bankverbindung für Ihre Überweisung:", new_x="LMARGIN", new_y="NEXT")
pdf.set_font("Helvetica", size=10)
pdf.cell(0, 5, f"Bankinstitut: {SELLER_INFO['bank_name']}", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 5, f"IBAN: {SELLER_INFO['iban']}", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 5, f"BIC: {SELLER_INFO['bic']}", new_x="LMARGIN", new_y="NEXT")
# Speichern
pdf.output(output_path)
def send_accreditation_email(
recipient: str,
domain: str,
username: str,
password: str,
school_name: str = "[Name der Schule / des Auftraggebers]",
address: str = "[Straße, Hausnummer] [PLZ, Ort]",
price: str = "250,00",
date: str = "[Ort, Datum]",
software_name: str = "Invario Inventarsystem",
invoice_path: str = None
) -> bool:
"""Generiert Hauptvertrag und Rechnung temporär als PDF und versendet sie."""
safe_school_name = "".join([c for c in school_name if c.isalnum() or c in (' ', '_', '-')]).rstrip()
# 1. Hauptvertrag dynamisch erzeugen
contract_filename = f"Hauptvertrag_Invario_{safe_school_name}.pdf"
contract_pdf_path = os.path.join(tempfile.gettempdir(), contract_filename)
generate_main_contract_pdf(
school_name=school_name,
address=address,
price=price,
date=date,
software_name=software_name,
output_path=contract_pdf_path
)
# 1b. Rechnung dynamisch mit FPDF erzeugen inkl. SELLER_INFO
generated_invoice_path = None
if not invoice_path:
invoice_filename = f"Rechnung_Invario_{safe_school_name}.pdf"
generated_invoice_path = os.path.join(tempfile.gettempdir(), invoice_filename)
generate_invoice_fpdf(
school_name=school_name,
address=address,
price=price,
date=date,
software_name=software_name,
output_path=generated_invoice_path
)
invoice_to_attach = generated_invoice_path
else:
invoice_to_attach = invoice_path
# 2. Statischen Ordner aus Flask dynamisch ermitteln
if current_app:
static_dir = current_app.static_folder
else:
static_dir = os.path.join(os.getcwd(), "static")
def get_valid_pdf_path(filename):
possible_paths = [
os.path.join(static_dir, "downloads", filename),
os.path.join(static_dir, "download", filename),
os.path.join("/opt/Key-service-Server/Website/static/downloads", filename),
os.path.join("/opt/Key-service-Server/Website/static/download", filename),
]
for path in possible_paths:
if os.path.exists(path):
return path
return None
agb_pdf_path = get_valid_pdf_path("AGB Invario.pdf")
avv_pdf_path = get_valid_pdf_path("AVV Invario.pdf")
# 3. E-Mail Inhalte
subject = "Ihre Zugangsdaten und Unterlagen für Invario"
text_note = (
f"Sehr geehrte/r {username},\n\n"
"Vielen Dank für Ihre Akkreditierung bei Invario. "
"Im Anhang finden Sie Ihre Unterlagen (Hauptvertrag, Rechnung, AGB und AVV).\n\n"
f"Ihre Zugangsdaten:\n"
f"Domain: https://{domain}\n"
f"Benutzername: {username}\n"
f"Passwort: {password}\n\n"
"Bitte bewahren Sie diese Informationen sicher auf."
)
html_note = f"""
<div style="font-family: Arial, Helvetica, sans-serif; color: #333333;">
<h2 style="color: #2c3e50; margin-top: 0;">Willkommen bei Invario!</h2>
<p style="font-size: 15px; line-height: 1.6;">
Vielen Dank für Ihre Akkreditierung. Im Anhang finden Sie Ihren Hauptvertrag, Ihre Rechnung sowie AGB und AVV.
Bitte laden Sie die Dokumente herunter und unterschreiben Sie den Hauptvertrag. Danach können Sie die Unterlagen per E-Mail an uns zurücksenden oder über das Invario-Portal hochladen, um die Akkreditierung abzuschließen.
</p>
<h3 style="color: #2c3e50;">Ihre Zugangsdaten:</h3>
<ul style="font-size: 15px; line-height: 1.6;">
<li><strong>Domain:</strong> <a href="https://{domain}">https://{domain}</a></li>
<li><strong>Benutzername:</strong> {username}</li>
<li><strong>Passwort:</strong> {password}</li>
</ul>
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">
Bitte bewahren Sie diese Informationen sicher auf.
</p>
</div>
"""
# 4. Vorhandene Anhänge sammeln
attachments = []
if os.path.exists(contract_pdf_path):
attachments.append(contract_pdf_path)
if invoice_to_attach and os.path.exists(invoice_to_attach):
attachments.append(invoice_to_attach)
if agb_pdf_path:
attachments.append(agb_pdf_path)
if avv_pdf_path:
attachments.append(avv_pdf_path)
# 5. E-Mail versenden & temporäre Dateien aufräumen
try:
success = send(recipient, subject, text_body=text_note, html_body=html_note, attachments=attachments)
return success
finally:
if os.path.exists(contract_pdf_path):
try:
os.remove(contract_pdf_path)
except OSError:
pass
if generated_invoice_path and os.path.exists(generated_invoice_path):
try:
os.remove(generated_invoice_path)
except OSError:
pass