'use client' import { useState, useEffect } from 'react' export interface FpAnnualKPIs { revenue: number ebit: number personal: number netIncome: number steuern: number liquiditaet: number customers: number headcount: number mrr: number arr: number arpu: number revPerEmp: number ebitMargin: number grossMargin: number nrr: number // Net Revenue Retention % paybackMonths: number // CAC Payback Period in months } interface SheetRow { row_label?: string values?: Record values_total?: Record } /** * Loads annual KPIs directly from fp_* tables (source of truth). * Returns a map of year keys (y2026-y2030) to KPI objects. */ export function useFpKPIs(isWandeldarlehen?: boolean) { const [kpis, setKpis] = useState>({}) const [loading, setLoading] = useState(true) useEffect(() => { async function load() { try { const param = isWandeldarlehen ? '?scenarioId=c0000000-0000-0000-0000-000000000200' : '' const [guvRes, liqRes, persRes, kundenRes] = await Promise.all([ fetch(`/api/finanzplan/guv${param}`, { cache: 'no-store' }), fetch(`/api/finanzplan/liquiditaet${param}`, { cache: 'no-store' }), fetch(`/api/finanzplan/personalkosten${param}`, { cache: 'no-store' }), fetch(`/api/finanzplan/kunden${param}`, { cache: 'no-store' }), ]) const [guv, liq, pers, kunden] = await Promise.all([guvRes.json(), liqRes.json(), persRes.json(), kundenRes.json()]) const guvRows: SheetRow[] = guv.rows || [] const liqRows: SheetRow[] = liq.rows || [] const persRows: SheetRow[] = pers.rows || [] const kundenRows: SheetRow[] = kunden.rows || [] const findGuv = (label: string) => guvRows.find(r => (r.row_label || '').includes(label)) const findLiq = (label: string) => liqRows.find(r => (r.row_label || '').includes(label)) const kundenGesamt = kundenRows.find(r => r.row_label === 'Bestandskunden gesamt') const result: Record = {} for (const y of [2026, 2027, 2028, 2029, 2030]) { const yk = `y${y}` const mk = `m${(y - 2026) * 12 + 12}` // December const revenue = findGuv('Umsatzerlöse')?.values?.[yk] || 0 const ebit = findGuv('EBIT')?.values?.[yk] || 0 const personal = findGuv('Summe Personalaufwand')?.values?.[yk] || 0 const netIncome = findGuv('Jahresüberschuss')?.values?.[yk] || findGuv('Jahresueber')?.values?.[yk] || 0 const steuern = findGuv('Steuern gesamt')?.values?.[yk] || 0 const liquiditaet = findLiq('LIQUIDIT')?.values?.[mk] || findLiq('LIQUIDITAET')?.values?.[mk] || 0 const customers = kundenGesamt?.values?.[mk] || 0 const headcount = persRows.filter(r => ((r.values_total || r.values)?.[mk] || 0) > 0).length const mrr = revenue > 0 ? Math.round(revenue / 12) : 0 const arr = mrr * 12 const arpu = customers > 0 ? Math.round(mrr / customers) : 0 const revPerEmp = headcount > 0 ? Math.round(revenue / headcount) : 0 const ebitMargin = revenue > 0 ? Math.round((ebit / revenue) * 100) : 0 const grossMargin = revenue > 0 ? Math.round(((revenue - (findGuv('Summe Materialaufwand')?.values?.[yk] || 0)) / revenue) * 100) : 0 // NRR: compare Dec revenue of this year vs Dec revenue of prior year // NRR = (MRR_Dec_thisYear / MRR_Dec_lastYear) * 100 const prevMk = `m${(y - 2026) * 12}` // December of prior year (m0 for 2026 = no prior) const prevRevenue = y > 2026 ? (findGuv('Umsatzerlöse')?.values?.[`y${y - 1}`] || 0) : 0 const nrr = prevRevenue > 0 ? Math.round((revenue / prevRevenue) * 100) : 0 // Payback: Marketing costs / monthly gross profit per new customer // Simplified: annual marketing spend / (new customers * monthly ARPU) const sonstAufw = findGuv('Sonst. betriebl. Aufwend')?.values?.[yk] || 0 // Marketing ~ 10% of sonstige (rough estimate from our formula) const marketingSpend = Math.round(revenue * 0.10) const prevCustomers = y > 2026 ? (kundenGesamt?.values?.[`m${(y - 2026) * 12}`] || 0) : 0 const newCustomers = Math.max(customers - prevCustomers, 1) const cac = newCustomers > 0 ? Math.round(marketingSpend / newCustomers) : 0 const monthlyGrossProfit = arpu > 0 ? arpu * (grossMargin / 100) : 0 const paybackMonths = monthlyGrossProfit > 0 ? Math.round(cac / monthlyGrossProfit) : 0 result[yk] = { revenue, ebit, personal, netIncome, steuern, liquiditaet, customers, headcount, mrr, arr, arpu, revPerEmp, ebitMargin, grossMargin, nrr, paybackMonths } } setKpis(result) } catch { /* ignore */ } setLoading(false) } load() }, [isWandeldarlehen]) const last = kpis.y2030 return { kpis, loading, last } }