Rabatt-System, größerer Mengen-Stepper, Zahlungsart zuerst, echte HasiDog-Inhalte
- Neues Rabatt-System: Prozent-/Betrag-Rabatt mit optionalem Ablaufdatum, Mengenrabatt-Staffel pro Produkt, sowie site-weite Gutscheincodes. Wird live im Warenkorb/Checkout berechnet (src/data/rabatt.ts, src/data/gutscheine.ts, cart.ts) und ist im Adminbereich direkt unter dem Preis bearbeitbar (config.yml: "Rabatte" + neue Collection "Gutscheincodes"). Effektiver Preis + Rabatt-Badge auf Produktkarten und Produktseite sichtbar. - Mengen-Stepper auf der Produktseite vergrößert (bereits vorbereitet, jetzt mit Build/Test verifiziert). - Zahlungsart im Checkout auf Platz 1, mit markenfarbigen, fertig gestalteten Buttons (PayPal/Klarna/Banküberweisung). - Unterkategorie-Seiten zeigen jetzt VanVans eigene Produktbeschreibung und das echte Fotos des Produkts statt Platzhaltertext, mit kleinem Link zur HasiDog-Universum-Seite statt großem Button. Verifiziert per Puppeteer-Funktionstest gegen den echten Production-Build (Rabatt-Berechnung, Mengenrabatt, Gutschein-Einlösung, Checkout-Summary, keine Konsolenfehler).
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
Reference in New Issue
Block a user