Zum Inhalt

Warenkorb & Pfand

Der Warenkorb (Basket) ist die zentrale Sammelstelle vor dem Checkout. Der Shop erweitert den Standard-Oscar-Warenkorb um Pfand-Logik und Wunschlisten-Integration.

Ort

Dashboard → Bestellungen → Warenkörbe (zur Einsicht offener Warenkörbe)

Funktionsweise

Pfand-System

Der Shop trennt Warenwert und Pfand strikt:

Konzept Property Beschreibung
Gesamtwert basket.total Brutto inkl. Rabatte
Pfandbetrag basket.deposit Summe aller Pfand-Positionen
Wert ohne Pfand basket.after_deposit total - deposit — Basis für Versand-Schwellwerte
# apps/basket/models.py
class Basket(abstract_models.AbstractBasket):
    @property
    def deposit(self):
        deposit_excl_tax = D()
        deposit_incl_tax = D()
        for line in self.lines.all():
            if line.deposit:
                deposit_excl_tax += line.deposit.excl * line.quantity
                deposit_incl_tax += line.deposit.incl * line.quantity
        return Deposit(excl=deposit_excl_tax, incl=deposit_incl_tax)

    @property
    def after_deposit(self):
        return PriceBase(
            incl=self.total.incl - self.deposit.incl,
            excl=self.total.excl - self.deposit.excl,
        )

Pfand-Ausschluss bei Berechnungen

after_deposit wird als Basis verwendet für: - Versand-Schwellwerte (minimum_order_value, free_shipping_threshold) - Mindermengenzuschlag (small_order_threshold) - Rabatt-Bedingungen (Warenkorbwert)

Kisten-Zählung

@property
def num_boxes(self):
    qs = self.lines.filter(product__has_box=True)
    return sum(qs.values_list("quantity", flat=True))

Wichtig für: Kistenzuschlag (price_per_box) in der Versandkostenberechnung.

Wunschlisten-Integration

Warenkorb-Positionen können mit Wunschlisten verknüpft sein:

class Line(abstract_models.AbstractLine):
    wishlist_line = models.ForeignKey(
        WishListLine, on_delete=models.CASCADE,
        related_name="basket_lines", blank=True, null=True,
    )

Dadurch kann der Warenkorb die Lieferadresse aus einer Wunschliste übernehmen:

@cached_property
def shipping_address(self):
    line = self.lines.filter(
        wishlist_line__wishlist__shipping_address__isnull=False
    ).first()
    if line:
        return line.wishlist_line.wishlist.shipping_address
    return None

Preis-Objekte

Der Shop nutzt spezielle Preis-Objekte (apps/partner/price.py):

Klasse Beschreibung
BasePrice Netto/Brutto-Basispreis
Deposit Pfandbetrag (Netto/Brutto)
SpecialPrice Aktionspreis nach Rabatten
Discount Rabatt-Differenz
Total Gesamtpreis
Price Kombination: BasePrice + Deposit + SpecialPrice

Preisaufschlüsselung (Line)

Jede Warenkorbzeile bietet price_breakdown:

@property
def price_breakdown(self):
    prices = []
    for incl, excl, qty in self.get_price_breakdown():
        if self.product.deposit:
            deposit = Deposit(incl=self.product.deposit, quantity=qty)
            incl -= deposit.incl
            excl -= deposit.excl
        else:
            deposit = None
        base_price = BasePrice(incl=self.price.incl, excl=self.price.excl, quantity=qty)
        special_price = SpecialPrice(incl=incl, excl=excl, quantity=qty)
        if special_price == base_price:
            special_price = None
        prices.append(Price(base_price, deposit=deposit, special_price=special_price, quantity=qty))
    return prices

Diese Aufschlüsselung ermöglicht die getrennte Anzeige von: - Warenwert - Pfand - Rabatt

im Checkout und auf der Bestellbestätigung.

Wichtige Properties

Property Typ Beschreibung
total Total(incl, excl) Gesamtpreis mit Rabatten
deposit Deposit(incl, excl) Pfandsumme
after_deposit PriceBase(incl, excl) Warenwert ohne Pfand
discount Discount(incl, excl) Rabatt-Differenz
num_boxes int Anzahl Kisten im Warenkorb
shipping_address UserAddress or None Lieferadresse aus Wunschliste