Zum Inhalt

Checkout-Flow

Der Bestellablauf erstreckt sich über mehrere Django-Apps und folgt einem definierten Prozess mit Pfand-Berechnung, Versandzuschlägen und Kommunikations-Dispatch.

Übersicht

Basket                    Checkout                   Order
┌────────────┐           ┌────────────────┐         ┌──────────┐
│ Warenkorb  │ ────────→ │ 1. Lieferadresse│        │Bestellung│
│ + Pfand    │           │ 2. Versandwahl  │ ──────→ │erstellt  │
│ + Angebote │           │ 3. Zahlung      │        │          │
│ + Zuschläge│           │ 4. Bestätigung  │        │          │
└────────────┘           └────────────────┘         └──────────┘
                                 │
                                 ▼
                          ┌────────────────┐
                          │ Communication  │
                          │ + OU-Routing   │
                          │ + E-Mail       │
                          │ + API-Dispatch │
                          └────────────────┘

Phasen im Detail

1. Warenkorb (Basket)

Datei: apps/basket/models.py

Der Basket enthält Line-Objekte mit folgenden Erweiterungen:

Konzept Property/Methode Beschreibung
Pfand-Ausschluss after_deposit Warenkorbwert ohne Pfand (Basis für Schwellwerte)
Kisten-Anzahl num_boxes Summe der Kisten im Warenkorb
Verfügbarkeit is_available_to_buy() Produkt-Prüfung pro Line
Angebote offer_applicator.apply() Oscar-Standard + Custom Benefits

2. Checkout-Schritte

Views: apps/checkout/views.pyPaymentDetailsView

Schritt 1: Lieferadresse

  • Auswahl aus gespeicherten Adressen oder Neueingabe
  • Validierung: Postleitzahl muss zum Liefergebiet passen
  • Session-basierte Zwischenspeicherung

Schritt 2: Versandmethode

Forms: apps/checkout/forms.pySingleShippingMethodForm

  • Listet verfügbare Versandmethoden (ShippingMethod.objects.available())
  • Berücksichtigt:
    • Mindestbestellwert (minimum_order_value)
    • Postleitzahl-Zonen (Zone + Postcode)
    • Lieferzeitfenster-Verfügbarkeit
  • Zeigt Zuschläge als separate Positionen

Stockwerk-Auswahl

Das Formular zeigt ein Dropdown für die Stockwerksanzahl:

# get_number_choices(): Nur wenn floor_only=True
# Zeigt Anzahl der Stockwerke mit zugehörigem Preis
choices = [
    (0, "Erdgeschoss – 0,00 €"),
    (1, "1. Stock – 2,00 €"),
    (2, "2. Stock – 4,00 €"),
]

Schritt 3: Zahlungsmethode

Provider: apps/payment/providers.py

Unterstützte Methoden: - Vorkasse - PayPal - Rechnung - Barzahlung (bei Abholung/Lieferung)

Schritt 4: Bestätigung & Submit

  • Zusammenfassung aller Positionen, Zuschläge und Rabatte
  • Checkbox für AGB / Widerrufsbelehrung
  • Abschicken triggert handle_order_submission()

3. SurchargeApplicator

Datei: apps/checkout/applicator.py

Der SurchargeApplicator erbt von oscar.apps.checkout.applicator.Applicator und erweitert die get_surcharges()-Methode:

def get_surcharges(self, basket, shipping_method, **kwargs):
    surcharges = []
    if shipping_method:
        surcharges = shipping_method.get_extra_charges(basket)
    return surcharges

Jeder Zuschlag wird als Surcharge-Objekt mit name, code und price angelegt – separat von der Versandmethode.

4. Bestellabschluss

Datei: apps/checkout/mixins.pyhandle_successful_order()

1. Basket.freeze() → Order erstellen
       ↓
2. Order in DB speichern
       ↓
3. send_custom_emails(order, …)
       ↓
4. CommunicationEvent ORDER_PLACED auslösen
       ↓
5. Basket leeren + Session bereinigen

Pfand-Splitting in der Bestellung

apps/order/models.py enthält Logik zur Trennung von Warenwert und Pfand in der Bestellungsaufstellung. Der Pfand wird als separate Position mit eigener MwSt.-Behandlung geführt.

5. Kommunikation nach Bestellabschluss

Datei: apps/communication/utils.pyCustomDispatcher

ORDER_PLACED Event
       ↓
CustomDispatcher.send_event_type()
       ↓
OrganizationUnit.get_responsible_ous(event, user, order)
       ↓
OrganizationUnit.send_all(event, user, messages)
       ↓
       ├──→ E-Mail an Kunde
       ├──→ E-Mail an Zusatzempfänger
       ├──→ E-Mail an OU-Mitglieder
       └──→ API-Request (optional)

Siehe E-Mail-Versand für den vollständigen Ablauf.

Wichtige Dateien

Datei Funktion
apps/basket/models.py Basket, Line, Pfand-Logik (after_deposit)
apps/checkout/views.py PaymentDetailsView
apps/checkout/forms.py SingleShippingMethodForm (Versandwahl)
apps/checkout/applicator.py SurchargeApplicator
apps/checkout/mixins.py handle_successful_order(), send_custom_emails()
apps/checkout/session.py Checkout-Session-Daten
apps/shipping/models.py OrderAndItemCharges (alle Zuschläge)
apps/shipping/repository.py Shipping method repository
apps/payment/providers.py Zahlungsprovider
apps/communication/utils.py CustomDispatcher
user/models.py OrganizationUnit.send_all()
apps/order/models.py Order mit Pfand-Splitting

Edge Cases

Mindestbestellwert nicht erreicht

Die Versandmethode steht nicht zur Auswahl. Der Kunde sieht eine Meldung: „Ihr Warenkorb liegt unter dem Mindestbestellwert von 30,00 €“.

Kein Liefertermin verfügbar

Wenn alle Zeitfenster belegt oder gesperrt sind, erscheint ein Hinweis statt des Dropdowns. Der Checkout kann nicht fortgesetzt werden.

Zahlungsfehler

Bei fehlgeschlagener Zahlung (z.B. PayPal-Ablehnung) wird die Bestellung im Status „Zahlung ausstehend“ angelegt. Dashboard-User können den Zahlungsstatus manuell korrigieren.