import { addMonths } from "date-fns"; /** * Regeln für gekaufte Karten (CustomerPass). Bewusst ohne Datenbank, damit * Buchen, Anzeige und Kunden-Detail dieselben Entscheidungen treffen. */ type PassState = { status: string; validUntil: Date | null; creditsRemaining: number; }; /** Ablaufdatum beim Kauf oder Vergeben. null = unbegrenzt gültig. */ export function validUntilFrom(start: Date, validityMonths: number | null): Date | null { return validityMonths === null ? null : addMonths(start, validityMonths); } /** Gilt die Karte für einen Kurs mit diesen Kursarten? Leere Kursarten auf der Karte = alle Kurse. */ export function passCoversCourse(passSports: string[], courseSports: string[]): boolean { return passSports.length === 0 || courseSports.some((sport) => passSports.includes(sport)); } /** Aktiv und nicht abgelaufen - unabhängig davon, ob noch Einheiten übrig sind. */ export function isPassActive(pass: Pick, now: Date): boolean { return pass.status === "active" && (pass.validUntil === null || pass.validUntil > now); } /** Lässt sich die Karte jetzt für `seats` Plätze einlösen? */ export function canRedeemPass(pass: PassState, seats: number, now: Date): boolean { return isPassActive(pass, now) && pass.creditsRemaining >= seats; } export type PassDisplayStatus = | "active" | "used_up" | "expired" | "ended" | "refunding" | "refunded"; /** Ein Status für Badge und Sortierung. Erstattet und beendet gehen vor abgelaufen. */ export function passDisplayStatus(pass: PassState, now: Date): PassDisplayStatus { if (pass.status === "refunded") return "refunded"; if (pass.status === "refunding") return "refunding"; if (pass.status === "ended") return "ended"; if (pass.validUntil !== null && pass.validUntil <= now) return "expired"; if (pass.creditsRemaining <= 0) return "used_up"; return "active"; } export const PASS_STATUS_LABELS: Record = { active: "Aktiv", used_up: "Aufgebraucht", expired: "Abgelaufen", ended: "Beendet", refunding: "Erstattung läuft", refunded: "Erstattet", }; /** * Studio-Umsatz aus einem Kartenkauf: was der Kunde gezahlt hat, ohne die * Servicegebühr. Eine Erstattung geht immer über den vollen Betrag, danach * bleibt nichts. Vergebene Karten bringen keinen Umsatz. */ export function passRevenueCents(pass: { status: string; amountPaid: number | null; platformFee: number | null; }): number { if (pass.status === "refunded" || pass.amountPaid === null) return 0; return pass.amountPaid - (pass.platformFee ?? 0); } /** "14.03.2027" in deutscher Zeit, für Listen und E-Mails. */ export function formatPassDate(date: Date): string { return date.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric", timeZone: "Europe/Berlin", }); }