# Riccardo Deals

Deal-Preise (bis 100 % Rabatt) auf ausgewählte Artikel – freigeschaltet, sobald der Warenkorb genug Umsatz
aus **margenstarken Warengruppen** enthält. Inspiriert von der „Mindestbestellwert“-Mechanik bei eis.de,
aber an die eigene Marge gekoppelt statt an beliebigen Umsatz.

## So funktioniert ein Deal

| Einstellung | Bedeutung |
|---|---|
| Deal-Artikel | Produkt (gilt für alle Varianten) |
| Preisart / Wert | Festpreis (z. B. 0,00 € / 0,99 €) oder Rabatt in % |
| Max. Stück pro Bestellung | Menge wird im Warenkorb automatisch begrenzt (Hinweis an den Kunden) |
| Mindestumsatz | Warenwert, den der Warenkorb aus der Warengruppe enthalten muss |
| Qualifizierende Kategorien | Artikel dieser Kategorien (inkl. Unterkategorien) zählen. Leer = alle Artikel |
| Mindestmarge (%) | Optional: nur Artikel mit mind. dieser Marge (Basis: Einkaufspreis netto) zählen |
| Name der Warengruppe | Kundenanzeige, z. B. „Liquids“ |
| Verhalten unter Minimum | **Normalpreis** (Deal-Preis erst bei erfüllter Bedingung) oder **Bestellung sperren** (Deal-Preis sofort, Kasse gesperrt – wie eis.de) |
| Gültig ab / bis, Priorität | Laufzeit; bei mehreren Deals auf denselben Artikel gewinnt die höchste Priorität |

Deal-Artikel zählen nie zur Warengruppe – auch nicht die anderer Deals.

Pflege: **Administration → Marketing → Riccardo Deals**.

## Technik

- Entität `riccardo_deal` (+ Zuordnung `riccardo_deal_category`), Migration `Migration1789660000CreateDealTables`
- `DealCartProcessor` (Collector + Processor, Priorität 4950 – nach Produkten, vor Promotions)
  - ersetzt nur den **berechneten** Preis der Position, nie die Preisdefinition. Der reguläre Preis bleibt
    Rechengrundlage, der Deal wird bei jeder Warenkorb-Berechnung neu geprüft.
  - schreibt den Status in `payload.riccardoDeal` (für Storefront/Store-API)
  - `DealMinimumNotReachedError` sperrt die Bestellung, `DealQuantityLimitedError` informiert über die Mengenbegrenzung
- `DealEvaluator` berechnet den qualifizierenden Umsatz (Kategorien, Marge aus `purchasePrices`)
- `DealLoader` lädt gültige Deals (pro Request gecacht, `kernel.reset`)
- Twig: `riccardo_deal(product, context)`, `riccardo_deal_price(deal, product)`
- Storefront: Deal-Plakette mit Tooltip (Produktkarte), Deal-Box (Produktseite), Status je Position mit
  Fortschrittsbalken (Warenkorb, Flyout, Checkout). Der Positions-Status hängt am Block der Lieferzeit
  (`core.cart.showDeliveryTime` muss aktiv sein).

## Tests

`dev/demo-data/test-deals.php` spielt 8 Szenarien über die Store-API durch (Minimum, Warengruppe, Marge,
Sperre, Mengenlimit, Grenzfälle). `dev/demo-data/deals.php` legt die Beispiel-Deals an.

## Rechtlich prüfen lassen

- Werbung für E-Zigaretten/Nachfüllbehälter ist eingeschränkt (TabakerzG) – insbesondere Gratis-Abgabe
  nikotinhaltiger Produkte. Deals daher bevorzugt auf Zubehör/nikotinfreie Artikel.
- Preisangaben (PAngV/UWG): Bedingung muss klar erkennbar sein (Plakette, Tooltip, Deal-Box sind vorhanden).

## Darstellung im Shop

- **Deal-Seite** `/deals` (Route `frontend.riccardo.deals.page`) mit allen aktiven Deals, Erklärung und FAQ
- **Einkaufswelt-Block „Riccardo Deals“** (Kategorie *Commerce*) mit Überschrift und maximaler Anzahl – auf der
  Startseite nach dem Hero platziert
- **Navigation:** „Deals“ in der Hauptnavigation und im mobilen Menü
