'use client' import { FMResult } from '@/lib/types' import { AccountingStandard } from './AnnualPLTable' import { Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, ReferenceLine, Line, ComposedChart, Cell, } from 'recharts' interface AnnualCashflowChartProps { results: FMResult[] initialFunding: number lang: 'de' | 'en' standard?: AccountingStandard } interface AnnualCFRow { year: string revenue: number costs: number netCashflow: number cashBalance: number cumulativeFundingNeed: number // HGB specific operatingCF?: number investingCF?: number financingCF?: number } export default function AnnualCashflowChart({ results, initialFunding, lang, standard = 'hgb' }: AnnualCashflowChartProps) { const de = lang === 'de' // Aggregate into yearly const yearMap = new Map() for (const r of results) { if (!yearMap.has(r.year)) yearMap.set(r.year, []) yearMap.get(r.year)!.push(r) } let cumulativeNeed = 0 const data: AnnualCFRow[] = Array.from(yearMap.entries()).map(([year, months]) => { const revenue = months.reduce((s, m) => s + m.revenue_eur, 0) const costs = months.reduce((s, m) => s + m.total_costs_eur, 0) const netIncome = months.reduce((s, m) => s + (m.net_income_eur || m.revenue_eur - m.total_costs_eur), 0) const depreciation = months.reduce((s, m) => s + (m.depreciation_eur || 0), 0) const cogs = months.reduce((s, m) => s + m.cogs_eur, 0) const lastMonth = months[months.length - 1] const netCF = netIncome if (netCF < 0) cumulativeNeed += Math.abs(netCF) // Operating CF = Net Income + Depreciation (non-cash add-back) const operatingCF = netIncome + depreciation // Investing CF = Hardware COGS (approximation for CapEx) const investingCF = -cogs // Financing CF = 0 for now (no debt/equity events modeled) const financingCF = 0 return { year: year.toString(), revenue: Math.round(revenue), costs: Math.round(costs), netCashflow: Math.round(netCF), cashBalance: Math.round(lastMonth.cash_balance_eur), cumulativeFundingNeed: Math.round(cumulativeNeed), operatingCF: Math.round(operatingCF), investingCF: Math.round(investingCF), financingCF: Math.round(financingCF), } }) const formatValue = (value: number) => { if (Math.abs(value) >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M` if (Math.abs(value) >= 1_000) return `${(value / 1_000).toFixed(0)}k` return value.toString() } // Calculate total funding needed beyond initial funding const totalFundingGap = Math.max(0, cumulativeNeed - initialFunding) const isUSGAAP = standard === 'usgaap' return (
{/* Summary Cards */}

{de ? 'Startkapital' : 'Initial Funding'}

{formatValue(initialFunding)} EUR

{de ? 'Kum. Finanzbedarf' : 'Cum. Funding Need'}

{formatValue(cumulativeNeed)} EUR

{de ? 'Finanzierungsluecke' : 'Funding Gap'}

0 ? 'text-red-400' : 'text-emerald-400'}`}> {totalFundingGap > 0 ? formatValue(totalFundingGap) + ' EUR' : (de ? 'Gedeckt' : 'Covered')}

{/* Chart */}
{ const labels: Record = isUSGAAP ? { netCashflow: 'Net Cash Flow', cashBalance: 'Cash Balance', cumulativeFundingNeed: 'Cum. Funding Need', } : { netCashflow: de ? 'Netto-Cashflow' : 'Net Cash Flow', cashBalance: de ? 'Cash-Bestand' : 'Cash Balance', cumulativeFundingNeed: de ? 'Kum. Finanzbedarf' : 'Cum. Funding Need', } return [formatValue(value) + ' EUR', labels[name] || name] }} /> {/* Net Cashflow Bars */} {data.map((entry, i) => ( = 0 ? 'rgba(34, 197, 94, 0.7)' : 'rgba(239, 68, 68, 0.6)'} /> ))} {/* Cash Balance Line */} {/* Cumulative Funding Need Line */}
{/* Legend */}
{isUSGAAP ? 'Net Cash Flow' : (de ? 'Netto-Cashflow' : 'Net Cash Flow')} {isUSGAAP ? 'Cash Balance' : (de ? 'Cash-Bestand' : 'Cash Balance')} {isUSGAAP ? 'Cum. Funding Need' : (de ? 'Kum. Finanzbedarf' : 'Cum. Funding Need')}
) }