From 9d600bc33a8bbaa2f5e65093edd49abb0bc17b91 Mon Sep 17 00:00:00 2001 From: AIIrondev Date: Fri, 28 Aug 2026 14:13:36 +0200 Subject: [PATCH] implementation of the upload --- Website/main.py | 104 +++++++++++++++++++++++--- Website/templates/admin_invoices.html | 17 ++++- Website/templates/my_invoices.html | 30 ++++---- 3 files changed, 124 insertions(+), 27 deletions(-) diff --git a/Website/main.py b/Website/main.py index 3830f84..5042e5a 100644 --- a/Website/main.py +++ b/Website/main.py @@ -23,6 +23,7 @@ from markupsafe import escape from pymongo import MongoClient from pymongo.errors import PyMongoError from bson.objectid import ObjectId +from bson.binary import Binary import user as user_store import buchungen import server_steering as steering @@ -119,6 +120,7 @@ LEGAL_DOWNLOAD_FILES = ( ], ), ) +ALLOWED_CONTRACT_EXTENSIONS = {'pdf', 'png', 'jpg', 'jpeg', 'webp'} MONGO_URI = os.environ.get("MONGO_URI", "mongodb://localhost:27017") MONGO_DB_NAME = os.environ.get("MONGO_DB_NAME", "Invario_Website") INSTANCE_REPO_URL = os.environ.get("INSTANCE_REPO_URL", "https://git.invario-software.eu/Invario/Inventarsystem") @@ -184,6 +186,10 @@ def _is_allowed_image_filename(filename: str) -> bool: return lowered.endswith(".jpg") or lowered.endswith(".jpeg") or lowered.endswith(".png") or lowered.endswith(".webp") +def _allowed_contract_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_CONTRACT_EXTENSIONS + + def _is_allowed_tutorial_video_filename(filename: str) -> bool: if not filename: return False @@ -2732,25 +2738,102 @@ def blog_post(post_id): return render_template("blog_post.html", post=post) -@app.route('/my/invoices') +@app.route('/invoices//hauptvertrag') @login_required -def my_invoices(): - invoices = [] +def view_hauptvertrag(invoice_id): client = None try: client, col = _get_collection("invoices") - invoices = list(col.find({"username": session.get("username")}, {"_id": 0}).sort("created_at", -1)) + invoice = col.find_one({"_id": ObjectId(invoice_id)}) - # Beträge in Float umwandeln, da das Jinja-Template echte Zahlen erwartet - for inv in invoices: + if not invoice or "hauptvertrag_data" not in invoice: + flash("Kein Hauptvertrag für diese Rechnung vorhanden.", "error") + return redirect(url_for("my_invoices")) + + # Sicherheitsprüfung: Darf der Nutzer die Datei sehen? (Nutzer selbst oder Admin) + current_user = session.get("username") + is_admin = session.get("is_admin") + if invoice.get("username") != current_user and not is_admin: + flash("Keine Berechtigung zum Anschauen dieses Hauptvertrags.", "error") + return redirect(url_for("my_invoices")) + + # Datei-Streams aus den Binärdaten in MongoDB erzeugen + file_data = invoice["hauptvertrag_data"] + mimetype = invoice.get("hauptvertrag_mimetype", "application/pdf") + filename = invoice.get("hauptvertrag_filename", "hauptvertrag") + + return send_file( + io.BytesIO(file_data), + mimetype=mimetype, + as_attachment=False, + download_name=filename + ) + except Exception as e: + flash("Fehler beim Abrufen der Datei aus der Datenbank.", "error") + return redirect(url_for("my_invoices")) + finally: + if client: + client.close() + + +@app.route('/my/invoices', methods=['GET', 'POST']) +@login_required +def my_invoices(): + client = None + + # POST-Request: Datei in MongoDB speichern + if request.method == 'POST': + action = request.form.get("action") + invoice_id = request.form.get("invoice_id") + uploaded_file = request.files.get("hauptvertrag_file") + + if action == "upload_hauptvertrag" and invoice_id and uploaded_file: + if _allowed_contract_file(uploaded_file.filename): + file_bytes = uploaded_file.read() + filename = uploaded_file.filename + mimetype = uploaded_file.mimetype or "application/octet-stream" + + try: + client, col = _get_collection("invoices") + # Datei direkt als Binary in das Invoice-Dokument schreiben + col.update_one( + {"_id": ObjectId(invoice_id), "username": session.get("username")}, + {"$set": { + "hauptvertrag_data": Binary(file_bytes), + "hauptvertrag_filename": filename, + "hauptvertrag_mimetype": mimetype, + "paperwork_status": "Hauptvertrag hochgeladen", + "updated_at": _utc_now_iso() + }} + ) + flash("Hauptvertrag erfolgreich in der Datenbank gespeichert.", "success") + except Exception: + flash("Fehler beim Speichern in MongoDB.", "error") + finally: + if client: + client.close() + else: + flash("Ungültiges Format. Erlaubt sind PDF, JPG, PNG und WEBP.", "error") + return redirect(url_for("my_invoices")) + + # GET-Request: Rechnungen laden + invoices = [] + try: + client, col = _get_collection("invoices") + raw_invoices = list(col.find({"username": session.get("username")}).sort("created_at", -1)) + + for inv in raw_invoices: + inv["id"] = str(inv["_id"]) amt = inv.get("amount_eur", 0) if isinstance(amt, str): try: - # Entfernt Tausenderpunkte und wandelt das deutsche Komma in einen Punkt um amt = float(amt.replace('.', '').replace(',', '.')) except ValueError: amt = 0.0 inv["amount_eur"] = amt + # Flag setzen, ob Vertrag in MongoDB liegt (ohne die schweren Binärdaten ins Template zu laden) + inv["has_hauptvertrag"] = "hauptvertrag_data" in inv + invoices.append(inv) except PyMongoError: flash("Rechnungen konnten nicht geladen werden.", "error") @@ -2758,15 +2841,12 @@ def my_invoices(): if client: client.close() - # Load instance requests for this user instance_requests = [] try: req_client, req_col = _get_collection("instance_requests") instance_requests = list(req_col.find({"username": session.get("username")}).sort("created_at", -1)) for item in instance_requests: item["id"] = str(item.get("_id")) - - # Auch hier sicherstellen, dass der Betrag ein Float ist amt = item.get("amount_eur", 0) if isinstance(amt, str): try: @@ -2774,15 +2854,15 @@ def my_invoices(): except ValueError: amt = 0.0 item["amount_eur"] = amt - except PyMongoError: flash("Instanz-Anfragen konnten nicht geladen werden.", "error") finally: if 'req_client' in locals() and req_client: req_client.close() - + return render_template("my_invoices.html", invoices=invoices, instance_requests=instance_requests) + @app.route('/my/instance', methods=['GET', 'POST']) @login_required def my_instance_management(): diff --git a/Website/templates/admin_invoices.html b/Website/templates/admin_invoices.html index 8713e5e..c2bb520 100644 --- a/Website/templates/admin_invoices.html +++ b/Website/templates/admin_invoices.html @@ -44,11 +44,23 @@

Unterlagen: {{ item.paperwork_status or 'Offen' }}{% if item.consent_accepted %} | Einwilligung bestätigt{% endif %}

Kontaktdaten: {{ item.booking_data.get('contact_person', '-') }} | {{ item.booking_data.get('contact_email', '-') }} | {{ item.booking_data.get('billing_city', '-') }}

{% endif %} +
{% if item.pdf_path %} -

PDF: Ansehen - +

Rechnungs-PDF: + Ansehen

{% endif %} + + {% if item.hauptvertrag_data is defined or item.get('hauptvertrag_filename') %} +

Hauptvertrag (aus MongoDB): + + {{ item.hauptvertrag_filename or 'Hauptvertrag öffnen' }} + +

+ {% else %} +

Hauptvertrag: Noch nicht in MongoDB hochgeladen

+ {% endif %} +
@@ -82,6 +94,7 @@ + diff --git a/Website/templates/my_invoices.html b/Website/templates/my_invoices.html index 7bfa84c..1ae47d4 100644 --- a/Website/templates/my_invoices.html +++ b/Website/templates/my_invoices.html @@ -6,7 +6,7 @@

Rechnungseinsicht

-

Alle Rechnungen im Überblick. Der Admin prüft neue Rechnungen zuerst; der Status zeigt dir, ob die Rechnung noch geprüft wird oder bereits bearbeitet wurde.

+

Alle Rechnungen im Überblick.

@@ -22,7 +22,8 @@ Betrag Fälligkeit Status - PDF + Rechnung PDF + Hauptvertrag @@ -36,26 +37,29 @@ {% if invoice.pdf_path %} PDF ansehen - {% else %} - {% endif %} + + {% if invoice.has_hauptvertrag %} + Hauptvertrag ansehen + {% else %} + + + + + + + {% endif %} + {% else %} - Noch keine Rechnungen vorhanden. + Noch keine Rechnungen vorhanden. {% endfor %}
- - -{% endblock %} +{% endblock %} \ No newline at end of file