From 9d33e9c99682db245b7acc52144a0f7c976b0531 Mon Sep 17 00:00:00 2001 From: AIIrondev Date: Mon, 24 Aug 2026 21:49:18 +0200 Subject: [PATCH] implementation for the warenkorb system --- Website/main.py | 92 ++-- Website/templates/preise.html | 696 +++++++++++-------------- Website/templates/schnell_buchung.html | 51 +- 3 files changed, 415 insertions(+), 424 deletions(-) diff --git a/Website/main.py b/Website/main.py index dbb66c2..5c73a61 100644 --- a/Website/main.py +++ b/Website/main.py @@ -1972,34 +1972,54 @@ def preise(): @app.route('/preise/book-option', methods=['POST']) @login_required def book_option_package(): - package_raw = _sanitize_text(request.form.get("package") or "", 40).lower() + # Unterstützt "packages" (Warenkorb) und Fallback auf "package" (altes Formular) + packages_raw = _sanitize_text(request.form.get("packages") or request.form.get("package") or "", 200).lower() booking_flow = _sanitize_text(request.form.get("flow") or "payment", 24).lower() + if booking_flow not in BOOKING_FLOW_MAP: booking_flow = "payment" - selected_package = BOOKING_PACKAGE_MAP.get(package_raw) - if not selected_package: - flash("Ungültige Buchungsoption ausgewählt.", "error") + if not packages_raw: + flash("Keine Buchungsoption ausgewählt.", "error") return redirect(url_for("preise")) - return redirect(url_for("booking_payment", package=package_raw, flow=booking_flow)) + return redirect(url_for("booking_payment", packages=packages_raw, flow=booking_flow)) @app.route('/booking/payment', methods=['GET', 'POST']) @login_required def booking_payment(): - package_raw = _sanitize_text((request.values.get("package") or ""), 40).lower() - booking_flow = _sanitize_text((request.values.get("flow") or "payment"), 24).lower() + # Hole kommagetrennte Liste (z. B. "inventarsystem,terminverwaltung") + packages_str = _sanitize_text(request.values.get("packages") or request.values.get("package") or "", 200).lower() + booking_flow = _sanitize_text(request.values.get("flow") or "payment", 24).lower() + if booking_flow not in BOOKING_FLOW_MAP: booking_flow = "payment" - selected_package = BOOKING_PACKAGE_MAP.get(package_raw) - package_price = BOOKING_PRICE_MAP.get(package_raw) - if not selected_package: - flash("Ungültige Buchungsoption ausgewählt.", "error") + if not packages_str: + flash("Keine Buchungsoption ausgewählt.", "error") return redirect(url_for("preise")) + package_list = [p.strip() for p in packages_str.split(',') if p.strip()] + + # Validiere Pakete, sammle Namen und berechne Gesamtpreis + selected_package_names = [] + total_price = 0.0 + for pkg in package_list: + name = BOOKING_PACKAGE_MAP.get(pkg) + if name: + selected_package_names.append(name) + total_price += BOOKING_PRICE_MAP.get(pkg, 0.0) + else: + flash(f"Ungültige Buchungsoption: {pkg}", "error") + return redirect(url_for("preise")) + + # Zusammengefasster String für Frontend und Datenbank (z.B. "Inventarsystem, Terminplaner") + selected_package = ", ".join(selected_package_names) + package_raw = packages_str # Speichert alle raw-keys für DB + flow_config = BOOKING_FLOW_MAP[booking_flow] + form_data = { "contact_person": _sanitize_text(request.values.get("contact_person") or "", 120), "school_name": _sanitize_text(request.values.get("school_name") or "", 160), @@ -2018,7 +2038,7 @@ def booking_payment(): review_data = { "package": selected_package, "package_key": package_raw, - "package_price": package_price, + "package_price": total_price, "flow": booking_flow, "title": flow_config["title"], "intro": flow_config["intro"], @@ -2031,6 +2051,7 @@ def booking_payment(): if request.method == "GET": return render_template("schnell_buchung.html", **review_data) + # --- POST Logik & Validierung --- required_fields = { "contact_person": "Ansprechpartner", "school_name": "Schule/Einrichtung", @@ -2039,6 +2060,7 @@ def booking_payment(): "billing_zip": "PLZ", "billing_city": "Ort", } + missing_labels = [label for key, label in required_fields.items() if not form_data.get(key)] if missing_labels: flash(f"Bitte folgende Pflichtfelder ausfüllen: {', '.join(missing_labels)}.", "error") @@ -2079,20 +2101,17 @@ def booking_payment(): "message": message, "created_at": _utc_now_iso(), "booking_flow": booking_flow, - "booking_package": package_raw, + "booking_package": package_raw, # Hier steht nun z.B. "inventarsystem,terminverwaltung" "booking_data": form_data, } ) + if booking_flow == "payment": - # Do NOT create an invoice directly. Instead request provisioning via server_steering booking_number = f"BOOK-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}" - # Build a subdomain from the school name (fallback to username) - # Use lowercase, sanitized school name and check for existing instances. raw_base = form_data.get("school_name") or session.get("username") or booking_number base = _slugify_subdomain(raw_base).lower()[:51] - # Check existing instances and require the user to choose a different name on conflict try: existing = steering.instace.list() or [] except Exception: @@ -2103,11 +2122,10 @@ def booking_payment(): return render_template("schnell_buchung.html", **review_data) subdomain = base[:63] - provision_status = "not_started" provision_port = None provision_error = "" - provision_config_ok = False + provision_config_ok = True # Startet mit True, wird False wenn ein Modul fehlschlägt try: try: @@ -2115,6 +2133,7 @@ def booking_payment(): except Exception as e: port = False provision_error = str(e) + provision_config_ok = False if port and port is not False: provision_status = "started" @@ -2123,19 +2142,24 @@ def booking_payment(): except Exception: provision_port = port - # Configure module if applicable - module = None - if package_raw in {"inventarsystem", "buecherei", "terminverwaltung", "emailversand", "starter", "advanced"}: - module = package_raw - if module: + # Multi-Modul-Konfiguration: Startpaket wird in Inventar + Bücherei aufgeschlüsselt + modules_to_provision = set() + for pkg in package_list: + if pkg == "startpaket": + modules_to_provision.update(["inventarsystem", "buecherei"]) + elif pkg in {"inventarsystem", "buecherei", "terminverwaltung", "emailversand", "starter", "advanced"}: + modules_to_provision.add(pkg) + + # Einzelne Module in der erstellten Instanz editieren/aktivieren + for mod in modules_to_provision: try: - edit_ok = steering.instace.edit(subdomain, module) - provision_config_ok = bool(edit_ok) - if not provision_config_ok and not provision_error: - provision_error = f"Module configuration failed for {module}" + edit_ok = steering.instace.edit(subdomain, mod) + if not edit_ok: + provision_config_ok = False + provision_error += f" | Edit failed for {mod}" except Exception as e: provision_config_ok = False - provision_error = str(e) + provision_error += f" | {mod}: {str(e)}" else: provision_status = "failed" if not provision_error: @@ -2149,7 +2173,7 @@ def booking_payment(): "booking_number": booking_number, "package": package_raw, "package_label": selected_package, - "amount_eur": BOOKING_PRICE_MAP.get(package_raw, 0.0), + "amount_eur": total_price, "booking_flow": booking_flow, "booking_data": form_data, "assigned_admin_username": assigned_admin_username, @@ -2159,7 +2183,7 @@ def booking_payment(): "provision_status": provision_status, "provision_port": provision_port or "", "provision_config_ok": provision_config_ok, - "provision_error": provision_error, + "provision_error": provision_error.strip(" | "), "created_at": _utc_now_iso(), "source": "booking_payment", } @@ -2173,8 +2197,8 @@ def booking_payment(): req_client.close() except PyMongoError: - flash("Buchungsanfrage konnte nicht gesendet werden.", "error") - return redirect(url_for("booking_payment", package=package_raw, flow=booking_flow)) + flash("Buchungsanfrage konnte nicht gesendet werden. Bitte versuchen Sie es erneut.", "error") + return redirect(url_for("booking_payment", packages=package_raw, flow=booking_flow)) finally: if client: client.close() @@ -2185,13 +2209,13 @@ def booking_payment(): else f"Buchung für {selected_package} wurde erfolgreich erfasst. Die Rechnung wird per E-Mail versendet." ) flash(success_text, "success") + if booking_flow == "payment": return redirect(url_for("my_invoices")) else: return redirect(url_for("user_chat")) - @app.route('/instance_request/delete', methods=['POST']) @login_required def instance_request_delete(): diff --git a/Website/templates/preise.html b/Website/templates/preise.html index 05a3990..84db49c 100644 --- a/Website/templates/preise.html +++ b/Website/templates/preise.html @@ -5,6 +5,7 @@ {% block head %} {{ super() }} {% endblock %} {% block content %}
-

Preise

-

Preise zu unseren Modulen -

Unsere Module und Pakete sind klar strukturiert und transparent. Wählen Sie die passende Lösung für Ihre Schule und senden Sie die Anfrage direkt an unser Admin-Team.

-

Nach der Anfrage wird sich ein Administrator zeitnah über das Portal mit Ihnen in Verbindung setzen, um die Buchung persönlich vor Ort zu besprechen oder einen direkten Start zu vereinbaren.

-

Unsere Server stehen in einem Deutschen Data-Center der Firma Strato und garantieren eine hohe Verfügbarkeit und Sicherheit Ihrer Daten.

+

Preise & Module

+

Unsere Module und Pakete sind klar strukturiert und transparent. Stellen Sie sich Ihre Lösung flexibel zusammen.

+

Nach Ihrer Anfrage meldet sich unser Admin-Team, um die Bereitstellung oder einen Termin zur Abstimmung einzuplanen. Die Server stehen in einem deutschen Rechenzentrum (Strato) – DSGVO-konform und hochverfügbar.

+
@@ -368,30 +154,28 @@

Inventarsystem

- 350 € - / Jahr + 350 €/ Jahr
-

Der stabile Einstieg in die Schulverwaltung mit Inventarverwaltung, Benutzerrechten und Berichten für den gesamten Schulbetrieb.

+

Der stabile Einstieg in die Schulverwaltung mit Inventarverwaltung, Benutzerrechten und Berichten.

  • Inventarverwaltung mit Rollenrechten
  • Basis-Support und Ticketing
  • Schulweite Übersichten und Berichte
  • -
  • Erweiterte Instanzverwaltung
  • Ihre Schullehrmittel auf einen Blick
{% if 'username' in session %} -
- - -
+ {% else %} - Hinweis: Registrieren Sie sich oder loggen sich bitte ein, um einen gemeinsamen Termin zu vereinbaren. + Zum Buchen einloggen {% endif %}
+
@@ -399,29 +183,27 @@

Bibliothekssystem

- 100 € - / Jahr + 100 €/ Jahr
-

Ein spezialisiertes Modul für Bibliotheken und Medienzentren mit Ausleihprozessen, Medienbeständen und historischer Transparenz.

+

Spezialisiertes Modul für Bibliotheken und Medienzentren inkl. Ausleihprozessen und Historie.

  • Ausgabe- und Rückgabeprozesse
  • Bestandsübersichten für Medien
  • Transparente Historie pro Medium
  • -
{% if 'username' in session %} -
- - -
+ {% else %} - Mit Konto buchen + Zum Buchen einloggen {% endif %}
+
@@ -429,59 +211,57 @@

Terminplaner

- 100 € - / Jahr + 100 €/ Jahr
-

Ein für flexible Terminplanung entwickeltes Modul, dass dem Nutzer erlaubt, seine Termine für beispielsweise Elternsprechstunden komfortabel und schnell im Zugriff zu haben.

+

Für flexible Terminplanung entwickelt (z.B. Elternsprechstunden), um Termine hürdenlos online anzubieten.

    -
  • Flexible Slot- und Zeitfenster-Erstellung
  • +
  • Flexible Slot- und Zeitfenster-Erstellung
  • Hürdenlose Buchung ohne Registrierung
  • -
  • Echtzeit-Aktualisierung gebuchter Termine
  • -
  • Direkte Terminvergabe per Einladungslink
  • +
  • Echtzeit-Aktualisierung
  • +
  • Terminvergabe per Einladungslink
{% if 'username' in session %} -
- - -
+ {% else %} - Mit Konto buchen + Zum Buchen einloggen {% endif %}
-
+ +
- Paket + Sparpaket

Startpaket

- 420 € - / Jahr + 420 €/ Jahr
-

Das Starterpaket enhält die Inventarverwaltung und das Bibliothekssystem, als ideale Komplettlösung.

+

Enthält Inventarverwaltung und Bibliothekssystem als ideale Komplettlösung zum Vorzugspreis.

    -
  • Das Inventarsystem idealer start Punkt
  • -
  • Das Bibliothekssystem als passende Ergänzung
  • -
  • Ein einfaches und intuitive Benutzeroberfläche
  • -
  • Der Perfekte Start für Ihre Digitalisierung
  • +
  • Beinhaltet alle Features des Inventarsystems
  • +
  • Beinhaltet alle Features des Bibliothekssystems
  • +
  • Perfekter Start für Ihre Digitalisierung
  • +
  • 30 € Ersparnis gegenüber Einzelkauf
{% if 'username' in session %} -
- - -
+ {% else %} - Mit Konto buchen + Zum Buchen einloggen {% endif %}
+
@@ -489,88 +269,238 @@

Schulträger

- Auf Anfrage + Individuell
-

Individuelles Angebot für Schulträger mit mehreren Schulen und speziellen Anforderungen an die Verwaltung.

+

Individuelles Angebot für Schulträger mit mehreren Schulen und speziellen Anforderungen.

  • Mandantenfähige Verwaltung
  • Zentrale Übersicht aller Schulen
  • -
  • Individuelle Anpassungen
  • +
  • Individuelle Anpassungen & SSO
  • Persönlicher Ansprechpartner
{% if 'username' in session %} -
- - -
+ {% else %} - Mit Konto anfragen + Zum Anfragen einloggen {% endif %}
-
-
- {% if 'username' in session %} -

Hinweis: Deine Buchungsanfrage wird direkt vom Admin-Team bearbeitet und deiner Schulinstanz zugeordnet.

- {% else %} -

Hinweis: Bitte logge dich ein oder registriere dich, um einen Termin zu vereinbaren.

- {% endif %} -
+ +
+ + + +
0
+
- -