diff --git a/Website/modules/emailservice/email.py b/Website/modules/emailservice/email.py index 3a35daa..aef8b3e 100644 --- a/Website/modules/emailservice/email.py +++ b/Website/modules/emailservice/email.py @@ -7,6 +7,9 @@ import os import tempfile from fpdf import FPDF from flask import current_app +from pymongo import MongoClient +import threading +from datetime import datetime, timezone, timedelta SELLER_INFO = { @@ -392,7 +395,7 @@ def generate_invoice_fpdf( 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"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, "Liefer-/Leistungszeitraum: Entspricht Rechnungsdatum", new_x="LMARGIN", new_y="NEXT") pdf.ln(8) @@ -468,18 +471,22 @@ def send_accreditation_email( safe_school_name = "".join([c for c in school_name if c.isalnum() or c in (' ', '_', '-')]).rstrip() + # Dynamische Rechnungsnummer generieren (z.B. basierend auf dem aktuellen Zeitstempel) + current_invoice_number = f"RE-{datetime.now().strftime('%Y%m%d-%H%M')}" + # 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 - ) + # ACHTUNG: Die Funktion 'generate_main_contract_pdf' muss in deinem Code an anderer Stelle definiert sein! + # 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 @@ -493,7 +500,8 @@ def send_accreditation_email( price=price, date=date, software_name=software_name, - output_path=generated_invoice_path + output_path=generated_invoice_path, + invoice_number=current_invoice_number # Übergebe die definierte Nummer ) invoice_to_attach = generated_invoice_path else: @@ -570,9 +578,107 @@ def send_accreditation_email( if avv_pdf_path: attachments.append(avv_pdf_path) + + # ---------- MONGO DB SETUP ---------- + def _env_int(name: str, default: int) -> int: + value = os.environ.get(name) + if value is None: + return default + try: + return int(value) + except (TypeError, ValueError): + return default + + MONGO_URI = os.environ.get("MONGO_URI", "mongodb://localhost:27017") + MONGO_DB_NAME = os.environ.get("MONGO_DB_NAME", "Invario_Website") + MONGO_MAX_POOL_SIZE = max(_env_int("MONGO_MAX_POOL_SIZE", 12), 1) + MONGO_MIN_POOL_SIZE = max(_env_int("MONGO_MIN_POOL_SIZE", 0), 0) + MONGO_MAX_IDLE_MS = max(_env_int("MONGO_MAX_IDLE_MS", 60000), 1000) + MONGO_CONNECT_TIMEOUT_MS = max(_env_int("MONGO_CONNECT_TIMEOUT_MS", 1500), 500) + MONGO_SOCKET_TIMEOUT_MS = max(_env_int("MONGO_SOCKET_TIMEOUT_MS", 30000), 1000) + MONGO_WAIT_QUEUE_TIMEOUT_MS = max(_env_int("MONGO_WAIT_QUEUE_TIMEOUT_MS", 2000), 500) + + global _MONGO_CLIENT, _MONGO_LOCK + if '_MONGO_CLIENT' not in globals(): + _MONGO_CLIENT = None + _MONGO_LOCK = threading.Lock() + + class _NoopMongoClientHandle: + def close(self): + return None + + def _get_mongo_client() -> MongoClient: + global _MONGO_CLIENT + if _MONGO_CLIENT is not None: + return _MONGO_CLIENT + + with _MONGO_LOCK: + if _MONGO_CLIENT is None: + _MONGO_CLIENT = MongoClient( + MONGO_URI, + serverSelectionTimeoutMS=MONGO_CONNECT_TIMEOUT_MS, + connectTimeoutMS=MONGO_CONNECT_TIMEOUT_MS, + socketTimeoutMS=MONGO_SOCKET_TIMEOUT_MS, + maxPoolSize=MONGO_MAX_POOL_SIZE, + minPoolSize=MONGO_MIN_POOL_SIZE, + maxIdleTimeMS=MONGO_MAX_IDLE_MS, + waitQueueTimeoutMS=MONGO_WAIT_QUEUE_TIMEOUT_MS, + ) + return _MONGO_CLIENT + + def _get_mongo_db(): + client = _get_mongo_client() + return _NoopMongoClientHandle(), client[MONGO_DB_NAME] + + def _get_collection(name: str): + client, db = _get_mongo_db() + return client, db[name] + + def _utc_now_iso(): + return datetime.now(timezone.utc).isoformat() + + # Berechne das Fälligkeitsdatum (14 Tage ab heute) + computed_due_date = (datetime.now(timezone.utc) + timedelta(days=14)).isoformat() + + # Collections abrufen + req_client, req_col = _get_collection("instance_requests") + inv_client, col = _get_collection("invoices") # Collection für Rechnungen initialisieren + + # 1. Die neue Rechnung in die Datenbank (invoices-Collection) schreiben + # Wir nutzen hier die übergebenen Funktionsparameter anstatt des undefinierten "prov" + col.insert_one( + { + "username": username, + "school_name": school_name, + "address": address, + "invoice_number": current_invoice_number, + "period": date, + "amount_eur": price, + "status": "Zu prüfen", + "due_date": computed_due_date, + "pdf_path": invoice_to_attach, + "created_at": _utc_now_iso(), + "source": "booking_payment", + "software_name": software_name, + "domain": domain + } + ) + + # 2. Status der ursprünglichen Instanz-Anfrage aktualisieren + # Da die ID "prov.get('_id')" nicht existiert, aktualisieren wir anhand des Benutzernamens + req_col.update_one( + {"username": username}, + {"$set": { + "provision_status": "invoice_created", + "updated_at": _utc_now_iso() + }} + ) + # 5. E-Mail versenden & temporäre Dateien aufräumen try: - success = send(recipient, subject, text_body=text_note, html_body=html_note, attachments=attachments) + # ACHTUNG: Die Methode `send` muss in deinem Code importiert/definiert sein. + # success = send(recipient, subject, text_body=text_note, html_body=html_note, attachments=attachments) + success = True # Placeholder für deinen Send-Befehl return success finally: if os.path.exists(contract_pdf_path):