// Rabatt-Logik. Unterstützt "alle Art von Rabatten" (Wunsch vom 02.08.2026): // 1. Klassischer Sale-Preis (bereits vorhanden: `preis`/`preisAlt`, manuell gesetzt). // 2. Prozentualer oder fester Euro-Rabatt am Produkt, automatisch berechnet, optional // zeitlich befristet (läuft am Stichtag von selbst wieder ab, ohne dass VanVan etwas // manuell zurückstellen muss). // 3. Mengenrabatt-Staffel (z.B. "ab 3 Stück 10% Rabatt") — wird im Warenkorb automatisch // auf die Zeile angewendet. // 4. Gutscheincodes (site-weit, siehe src/data/gutscheine.ts) — im Warenkorb/Checkout einlösbar. import type { Product } from "./products"; /** Prüft, ob ein befristeter Rabatt heute noch gültig ist (Vergleich läuft im Browser zur * echten aktuellen Uhrzeit, nicht nur zur Build-Zeit — der Rabatt läuft also wirklich * automatisch ab, auch ohne neuen Deploy). */ export function istRabattAktiv(bis?: string): boolean { if (!bis) return true; const heute = new Date(); const ende = new Date(`${bis}T23:59:59`); return heute <= ende; } /** Berechnet den effektiven Verkaufspreis eines Produkts unter Berücksichtigung von * Prozent-/Betrag-Rabatt (falls aktiv). Reiner Sale-Preis über `preisAlt` bleibt unabhängig * davon bestehen und wird hier NICHT verändert — beide Mechanismen sind unabhängig * kombinierbar (z.B. Sale-Preis als Basis, zusätzlich zeitlich befristete Rabattaktion oben drauf). */ export function effektiverPreis(product: Product): number { const basis = product.preis; const r = product.rabatt; if (!r || !istRabattAktiv(r.bis)) return basis; if (typeof r.prozent === "number" && r.prozent > 0) { return Math.max(0, basis * (1 - r.prozent / 100)); } if (typeof r.betrag === "number" && r.betrag > 0) { return Math.max(0, basis - r.betrag); } return basis; } /** Gibt an, ob gerade ein aktiver Rabatt greift (für Badge-Anzeige "-20%" o.ä.). */ export function hatAktivenRabatt(product: Product): boolean { return effektiverPreis(product) < product.preis; } /** Ob ein Produkt gerade "im Sale" ist — zählt sowohl den klassischen, manuell gesetzten * Sale-Preis (preisAlt) als auch einen automatisch berechneten, noch aktiven Prozent-/ * Betrags-Rabatt. Wird u.a. von Partner-Rabattcodes genutzt, die per Admin-Einstellung NUR auf * regulär bepreiste Artikel gelten sollen (siehe Partnercode.giltAufSale in partnercodes.ts). */ export function istImSale(product: Product): boolean { return Boolean(product.preisAlt) || hatAktivenRabatt(product); } /** Für die Badge-Anzeige: "-20%" oder "-5 €", je nachdem was hinterlegt ist. */ export function rabattLabel(product: Product): string | null { const r = product.rabatt; if (!r || !istRabattAktiv(r.bis)) return null; if (typeof r.prozent === "number" && r.prozent > 0) return `-${r.prozent}%`; if (typeof r.betrag === "number" && r.betrag > 0) return `-${r.betrag.toFixed(2).replace(".", ",")} €`; return null; } /** Mengenrabatt: findet den besten (höchsten) Prozentsatz, der bei der gegebenen Stückzahl * greift. Gibt 0 zurück, wenn kein Mengenrabatt hinterlegt ist oder die Menge zu klein ist. */ export function mengenrabattProzent(product: Product, menge: number): number { const stufen = product.rabatt?.mengenrabatt ?? []; let bester = 0; for (const stufe of stufen) { if (menge >= stufe.abMenge && stufe.prozent > bester) bester = stufe.prozent; } return bester; } /** Effektiver Zeilenpreis (Einzelpreis inkl. Rabatt × Menge, zusätzlich mit Mengenrabatt * verrechnet) — das ist die Zahl, die im Warenkorb pro Position angezeigt/summiert wird. */ export function effektiverZeilenpreis(product: Product, menge: number): number { const einzelpreis = effektiverPreis(product); const mengenProzent = mengenrabattProzent(product, menge); const zeile = einzelpreis * menge; return mengenProzent > 0 ? zeile * (1 - mengenProzent / 100) : zeile; }