Zum Inhalt

Datenmodell

Die wichtigsten Modelle, ihre Beziehungen und domänenspezifische Erweiterungen im Überblick.

E-Commerce-Core (Oscar)

Produkt-Domäne

ProductClass
  │  track_stock, requires_shipping, is_deposit
  │
  └── Product  ─── ProductCategory ─── Category
        │              (through)
        ├── ProductImage
        ├── ProductRecommendation
        ├── ProductAttributeValue
        ├── StockRecord (Preis)
        └── parent → Product (Varianten)
Modell Besonderheiten
ProductClass is_deposit steuert Pfandpflicht. requires_shipping unterscheidet physische von digitalen Produkten
Product parent-FK für Varianten-Gruppierung. Produkte ohne parent sind Stammprodukte
ProductCategory 1. Kategorie = Hauptkategorie (Breadcrumbs, URL)
StockRecord Preis pro Partner. price_excl_tax wird aus Brutto-Preis errechnet

Bestell-Domäne

Basket ── Line ── Product
  │         │
  │         ├── LinePrice
  │         └── LineAttribute
  │
  └── Surcharge (durch SurchargeApplicator)

Order ── Line ── Product
  │         │
  │         ├── LinePrice
  │         └── LineAttribute
  │
  ├── ShippingAddress
  ├── BillingAddress
  ├── ShippingEvent
  ├── PaymentEvent
  └── Surcharge (persistiert)
Modell Besonderheiten
Basket after_deposit Property: Warenkorbwert ohne Pfand
Order Pfand-Splitting: Pfand-Positionen werden getrennt von Warenpositionen geführt
Surcharge Persistent gespeichert in der Order (nicht flüchtig wie bei Oscar)

Angebots-Domäne

Offer ── Condition       ── Range
  │        │                 │
  │        ├── Value         ├── Produkte
  │        └── Coverage      ├── Kategorien
  │                          └── Produktklassen
  └── Benefit
       ├── PercentageDiscountBenefit
       ├── AbsoluteDiscountBenefit
       └── ShippingBenefit

Shop-Eigene Modelle

Versand (Shipping)

OrderAndItemCharges (shipping.models)
  ├── price_per_order: Decimal         # Bestellpauschale
  ├── price_per_item: Decimal          # Stückpreis × Artikel
  ├── price_per_floor: Decimal         # Stockwerkszuschlag × Etage
  ├── maximum_floor_difference: int    # Max. Stockwerke (0 = deaktiviert)
  ├── price_per_box: Decimal           # Kistenzuschlag × Kisten
  ├── small_order_surcharge: Decimal   # Mindermengenzuschlag
  ├── small_order_threshold: Decimal   # Schwellwert (None = deaktiviert)
  ├── minimum_order_value: Decimal     # Mindestbestellwert
  └── free_shipping_threshold: Decimal # Versandkostenfrei ab

ShippedFloor
  ├── shipping_method → OrderAndItemCharges
  ├── floor: int
  └── price_per_floor: Decimal         # Etagenspezifischer Preis

Logistik

TimeSlot (logistics.models)
  ├── start / end: DateTime           # Beginn und Ende der Lieferung
  ├── rule → Rule                     # Generierungsregel
  ├── route → Route                   # Tourenzuweisung
  ├── capacity: int                   # Max. Bestellungen (0 = unbegrenzt)
  ├── blocked: bool                   # Manuelle Sperre
  ├── blocker → Blocker               # Datums-Sperre
  ├── is_from_db: bool                # False = virtuelles Zeitfenster
  └── is_fully_booked_db: bool        # Kapazität erreicht

Schedule
  ├── rules → Rule (related)          # Wiederholungsregeln
  ├── zones → Zone (M2M)              # PLZ-Bereiche
  ├── cutoff_time: Time               # Annahmeschluss
  ├── min_advance_days: int           # Mindestvorlauf
  └── min_advance_time: Time          # Mindestvorlauf-Uhrzeit

Rule
  ├── schedule → Schedule
  ├── start / end: Time               # Uhrzeit-Bereich
  ├── weekdays: str                   # Komma-getrennt: "1,2,3,4,5"
  ├── frequency: Frequency            # Wöchentlich / 2-wöchentlich
  ├── interval: int
  └── order_quota: int                # Max. Bestellungen pro Slot

Route
  ├── name: str
  ├── number: int
  ├── max_stops: int
  └── vehicle: str (optional)

Zone
  ├── name: str
  └── postcodes → Postcode

Postcode
  ├── zone → Zone
  ├── postcode: str
  ├── city: str
  └── blocker_set → Blocker

Blocker
  ├── date: date
  ├── postcode → Postcode (optional)
  └── description: str

Kommunikation & Organisation

OrganizationUnit (user.models)
  ├── type: INTERNAL(0) | ADDITIONAL_EMAIL_RECIPIENT(1)
  ├── members → User (M2M)
  ├── email_recipients (TextField, komma-getrennt)
  ├── event_types → CommunicationEventType (M2M)
  ├── customers → User (M2M)
  ├── product_classes → ProductClass (M2M)
  ├── source_types (TextField)
  ├── minimum_order_value / maximum_order_value
  └── api_endpoint (URL, optional)

CommunicationEventType (communication.models)
  ├── code: str                      # ORDER_PLACED, REGISTRATION, …
  ├── name: str
  ├── category: USER | ORDER | CUSTOM
  └── email_templates → EmailTemplate
        ├── subject
        ├── body_html
        └── body_text

Wichtige Properties & Methoden

Modell Property/Methode Beschreibung
Basket after_deposit Warenkorbwert ohne Pfand (Brutto)
Basket num_boxes Anzahl Kisten (product.has_box)
Basket num_items Anzahl Artikel
OrderAndItemCharges get_extra_charges(basket) Alle Zuschläge als Liste von Dicts
OrderAndItemCharges is_free_shipping(basket) Versandkostenfreigrenze prüfen
OrderAndItemCharges _get_basket_total_without_deposit(basket) Pfand-freier Warenkorbwert
OrganizationUnit check_responsibility(event, user, order) Zuständigkeit für Event prüfen
OrganizationUnit send_all(event, user, messages) Kommunikation über alle OUs senden
OrganizationUnit get_responsible_ous(event, user, order) Alle zuständigen OUs ermitteln
OrganizationUnit get_email(messages) E-Mail mit Empfängern + Attachments
TimeSlot is_fully_booked Kapazität erreicht?
TimeSlot status blocked / fully_booked / bookable / plannable
TimeSlot is_from_db True = gespeichert, False = virtuell
TimeSlot date Lokales Datum des Slots
Schedule get_next_time_slots(postcode) Nächste verfügbare Zeitfenster