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 |