import { NextResponse } from "next/server"; import { createServiceClient } from "@/lib/supabase"; import { requireAdmin } from "@/lib/admin-auth"; export const dynamic = "force-dynamic"; export async function GET() { const check = await requireAdmin(); if (check instanceof NextResponse) return check; const db = createServiceClient(); const heute = new Date(); heute.setHours(0, 0, 0, 0); const heuteISO = heute.toISOString().slice(0, 10); // Fenster: 12 Monate zurück für Statistiken const vor12Monaten = new Date(heute); vor12Monaten.setMonth(vor12Monaten.getMonth() - 12); const vor12ISO = vor12Monaten.toISOString().slice(0, 10); // Fenster: 3 Monate voraus für Planungsübersicht const in3Monaten = new Date(heute); in3Monaten.setMonth(in3Monaten.getMonth() + 3); in3Monaten.setDate(in3Monaten.getDate() + 1); const in3ISO = in3Monaten.toISOString().slice(0, 10); // ── Alle Anfragen (Status + Datum) ───────────────────────────────────── const { data: anfragen } = await db .from("anfragen") .select("id, status, created_at, firma, telefon, email") .order("created_at", { ascending: false }); // ── Alle Positionen (Statistik: letzte 12 Monate) ────────────────────── const { data: allePositionen } = await db .from("anfragen_positionen") .select( "id, anfrage_id, maschine_name, maschine_kategorie, mietbeginn, mietende, gesamt_tage, tagessatz, lieferung" ) .gte("mietbeginn", vor12ISO) .order("mietbeginn"); // ── Planungs-Positionen (heute bis +3 Monate) ────────────────────────── const { data: planPositionen } = await db .from("anfragen_positionen") .select( "id, anfrage_id, maschine_name, maschine_kategorie, mietbeginn, mietende, gesamt_tage, tagessatz" ) .lte("mietbeginn", in3ISO) .gte("mietende", heuteISO) .order("mietbeginn"); const anfrageMap = new Map( (anfragen ?? []).map((a) => [a.id, a]) ); // ── KPI-Berechnung (nur abgeschlossene Mietvorgänge zählen, die zuvor bestätigt waren) ── // Lade Audit Log um zu verifizieren: bestätigt → abgeschlossenen Workflow const { data: auditLogs } = await db .from("anfrage_status_audit") .select("anfrage_id, status_zu"); // Map: anfrage_id → hat bestätigt Status durchlaufen? const hatBestaetigt = new Map(); (auditLogs ?? []).forEach((log: any) => { if (log.status_zu === "bestaetigt") { hatBestaetigt.set(log.anfrage_id, true); } }); const bestaetigtIds = new Set( (anfragen ?? []) .filter((a) => a.status === "abgeschlossen" && hatBestaetigt.has(a.id)) .map((a) => a.id) ); const kpiPositionen = (allePositionen ?? []).filter((p) => bestaetigtIds.has(p.anfrage_id) ); const umsatzGesamt = kpiPositionen.reduce( (sum, p) => sum + (p.tagessatz != null ? p.tagessatz * p.gesamt_tage : 0), 0 ); const mietTageGesamt = kpiPositionen.reduce( (sum, p) => sum + (p.gesamt_tage ?? 0), 0 ); // ── Auslastung pro Maschine ───────────────────────────────────────────── const maschinenMap: Record< string, { name: string; kategorie: string; tage: number; umsatz: number; anfragen: number } > = {}; for (const p of allePositionen ?? []) { if (!bestaetigtIds.has(p.anfrage_id)) continue; const key = p.maschine_name; if (!maschinenMap[key]) { maschinenMap[key] = { name: p.maschine_name, kategorie: p.maschine_kategorie ?? "", tage: 0, umsatz: 0, anfragen: 0, }; } maschinenMap[key].tage += p.gesamt_tage ?? 0; maschinenMap[key].umsatz += p.tagessatz != null ? p.tagessatz * p.gesamt_tage : 0; maschinenMap[key].anfragen += 1; } const maschinenStats = Object.values(maschinenMap).sort( (a, b) => b.tage - a.tage ); // ── Monatliche Entwicklung (letzte 6 Monate) ─────────────────────────── const monatsStats: { monat: string; label: string; anfragen: number; tage: number; umsatz: number; }[] = []; for (let i = 5; i >= 0; i--) { const d = new Date(heute); d.setMonth(d.getMonth() - i); const monat = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`; const label = d.toLocaleDateString("de-DE", { month: "short", year: "2-digit" }); const mPositionen = (allePositionen ?? []).filter( (p) => bestaetigtIds.has(p.anfrage_id) && p.mietbeginn?.startsWith(monat) ); monatsStats.push({ monat, label, anfragen: mPositionen.length, tage: mPositionen.reduce((s, p) => s + (p.gesamt_tage ?? 0), 0), umsatz: mPositionen.reduce( (s, p) => s + (p.tagessatz != null ? p.tagessatz * p.gesamt_tage : 0), 0 ), }); } // ── Planungs-Daten mit Anfragestatus anreichern ───────────────────────── const planDaten = (planPositionen ?? []).map((p) => { const anfrage = anfrageMap.get(p.anfrage_id); return { ...p, anfrage_status: anfrage?.status ?? "offen", firma: anfrage?.firma ?? "", }; }); return NextResponse.json({ kpi: { anfragenGesamt: anfragen?.length ?? 0, bestaetigtGesamt: bestaetigtIds.size, umsatzGesamt, mietTageGesamt, }, maschinenStats, monatsStats, planDaten, heuteISO, in3ISO, }); }