From 71b6f8f181ffc8567546607d9a1781b50165c6b0 Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Thu, 23 Apr 2026 22:07:00 +0200 Subject: [PATCH] =?UTF-8?q?fix(pitch-deck):=20fix=20Liquidit=C3=A4t=20engi?= =?UTF-8?q?ne=20label=20mismatches=20+=20MilestonesSlide=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Engine now uses dynamic row_type-based summation instead of hardcoded label strings that differed between scenarios (e.g. 'Summe ERTRÄGE' vs 'Summe EINZAHLUNGEN'), fixing stale 9.2M value in Wandeldarlehen scenarios. Rolling balance now includes all financing cash flows via ÜBERSCHUSS chain. MilestonesSlide: widen Theme type to union so t.key comparisons compile. Co-Authored-By: Claude Sonnet 4.6 --- .../components/slides/MilestonesSlide.tsx | 2 +- pitch-deck/lib/finanzplan/engine.ts | 79 +++++++------------ 2 files changed, 28 insertions(+), 53 deletions(-) diff --git a/pitch-deck/components/slides/MilestonesSlide.tsx b/pitch-deck/components/slides/MilestonesSlide.tsx index c167d38..77332d1 100644 --- a/pitch-deck/components/slides/MilestonesSlide.tsx +++ b/pitch-deck/components/slides/MilestonesSlide.tsx @@ -147,7 +147,7 @@ const THEMES = { }, } -type Theme = typeof THEMES.dark +type Theme = typeof THEMES.dark | typeof THEMES.light // ── Data ────────────────────────────────────────────────────────────────────── const TODAY_POSITION = 0.56 diff --git a/pitch-deck/lib/finanzplan/engine.ts b/pitch-deck/lib/finanzplan/engine.ts index 721559b..e3299ae 100644 --- a/pitch-deck/lib/finanzplan/engine.ts +++ b/pitch-deck/lib/finanzplan/engine.ts @@ -413,57 +413,42 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise liqInvest.values = totalInvest } - // Compute sums and rolling balance - // WICHTIG: Überschuss = nur operativer Cashflow (ohne Kapitaleinzahlungen) - const sumEin = findLiq('Summe EINZAHLUNGEN') - const sumAus = findLiq('Summe AUSZAHLUNGEN') - const uebVorInv = findLiq('ÜBERSCHUSS VOR INVESTITIONEN') - const uebVorEnt = findLiq('ÜBERSCHUSS VOR ENTNAHMEN') - const ueberschuss = findLiq('ÜBERSCHUSS') - const kontostand = findLiq('Kontostand zu Beginn des Monats') - const liquiditaet = findLiq('LIQUIDITÄT') + // Compute sums and rolling balance — dynamic row_type-based (handles any label conventions) + const findLiqMatch = (options: string[]) => liquid.find(r => options.includes(r.row_label)) + const sumEin = findLiqMatch(['Summe ERTRÄGE', 'Summe EINZAHLUNGEN']) + const sumAus = findLiqMatch(['Summe AUSZAHLUNGEN']) + const uebVorInv = findLiqMatch(['ÜBERSCHUSS VOR INVESTITIONEN', 'UEBERSCHUSS VOR INVESTITIONEN']) + const uebVorEnt = findLiqMatch(['ÜBERSCHUSS VOR ENTNAHMEN', 'UEBERSCHUSS VOR ENTNAHMEN']) + const ueberschuss = findLiqMatch(['ÜBERSCHUSS', 'UEBERSCHUSS']) + // Kontostand: label varies per scenario (with/without parentheses) + const kontostand = liquid.find(r => r.row_type === 'kontostand' && !r.row_label.includes('LIQUIDIT')) + const liquiditaet = liquid.find(r => r.row_type === 'kontostand' && r.row_label.includes('LIQUIDIT')) - // Dynamically categorize rows by row_type instead of hardcoded labels - // Operative Einzahlungen (OHNE Eigenkapital, Fremdkapital, Stammkapital, Wandeldarlehen) - const einzahlungenOperativ = ['Umsatzerlöse', 'Sonst. betriebl. Erträge', 'Anzahlungen'] - // Finanzierung: match any row with these keywords (handles renamed labels) - const finanzierungRows = liquid.filter(r => - r.row_type === 'einzahlung' && - !einzahlungenOperativ.includes(r.row_label) && - !r.row_label.includes('Summe') - ) - // Operative Auszahlungen - const auszahlungenOperativ = ['Materialaufwand', 'Personalkosten', 'Sonstige Kosten', 'Umsatzsteuer', 'Gewerbesteuer', 'Körperschaftsteuer'] - // Finanz-Auszahlungen: any auszahlung not in operativ list - const finanzAuszahlungRows = liquid.filter(r => - r.row_type === 'auszahlung' && - !auszahlungenOperativ.includes(r.row_label) && - !r.row_label.includes('Summe') - ) - - // Summe EINZAHLUNGEN = nur operativ (für die Zeile "Summe Einzahlungen") + // Summe ERTRÄGE = ALL einzahlungen (dynamic — works regardless of how many rows exist) if (sumEin) { const s = emptyMonthly() - for (const label of einzahlungenOperativ) { - const row = findLiq(label) - if (row) for (let m = 1; m <= MONTHS; m++) s[`m${m}`] += Math.round(row.values[`m${m}`] || 0) + for (const row of liquid) { + if (row.row_type === 'einzahlung' && row.id !== sumEin.id) { + for (let m = 1; m <= MONTHS; m++) s[`m${m}`] += Math.round(row.values[`m${m}`] || 0) + } } await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), sumEin.id]) sumEin.values = s } - // Summe AUSZAHLUNGEN = nur operativ + // Summe AUSZAHLUNGEN = ALL auszahlungen (dynamic) if (sumAus) { const s = emptyMonthly() - for (const label of auszahlungenOperativ) { - const row = findLiq(label) - if (row) for (let m = 1; m <= MONTHS; m++) s[`m${m}`] += Math.round(row.values[`m${m}`] || 0) + for (const row of liquid) { + if (row.row_type === 'auszahlung' && row.id !== sumAus.id) { + for (let m = 1; m <= MONTHS; m++) s[`m${m}`] += Math.round(row.values[`m${m}`] || 0) + } } await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), sumAus.id]) sumAus.values = s } - // OPERATIVER ÜBERSCHUSS VOR INVESTITIONEN = operative Einzahlungen - operative Auszahlungen + // ÜBERSCHUSS VOR INVESTITIONEN = Summe ERTRÄGE - Summe AUSZAHLUNGEN (total cashflow) if (uebVorInv && sumEin && sumAus) { const s = emptyMonthly() for (let m = 1; m <= MONTHS; m++) s[`m${m}`] = Math.round((sumEin.values[`m${m}`] || 0) - (sumAus.values[`m${m}`] || 0)) @@ -471,7 +456,7 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise uebVorInv.values = s } - // ÜBERSCHUSS VOR ENTNAHMEN = Operativer Überschuss - Investitionen + // ÜBERSCHUSS VOR ENTNAHMEN = ÜBERSCHUSS VOR INVESTITIONEN - Investitionen if (uebVorEnt && uebVorInv && liqInvest) { const s = emptyMonthly() for (let m = 1; m <= MONTHS; m++) s[`m${m}`] = Math.round((uebVorInv.values[`m${m}`] || 0) - (liqInvest.values[`m${m}`] || 0)) @@ -479,8 +464,8 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise uebVorEnt.values = s } - // ÜBERSCHUSS = Überschuss vor Entnahmen - Entnahmen (immer noch rein operativ) - const entnahmen = findLiq('Kapitalentnahmen/Ausschüttungen') + // ÜBERSCHUSS = ÜBERSCHUSS VOR ENTNAHMEN - Kapitalentnahmen + const entnahmen = findLiqMatch(['Kapitalentnahmen/Ausschüttungen', 'Kapitalentnahmen/Ausschuettungen']) if (ueberschuss && uebVorEnt && entnahmen) { const s = emptyMonthly() for (let m = 1; m <= MONTHS; m++) s[`m${m}`] = Math.round((uebVorEnt.values[`m${m}`] || 0) - (entnahmen.values[`m${m}`] || 0)) @@ -488,24 +473,14 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise ueberschuss.values = s } - // Rolling Kontostand: Vormonat + Operativer Überschuss + Finanzierung - // Finanzierung = Eigenkapital + Fremdkapital - Kreditrückzahlungen + // Rolling balance: LIQUIDITÄT[m] = LIQUIDITÄT[m-1] + ÜBERSCHUSS[m] + // ÜBERSCHUSS now includes ALL cash flows (operative + financing + repayments) if (kontostand && liquiditaet && ueberschuss) { - // Berechne monatliche Finanzierungs-Cashflows - const finCF = emptyMonthly() - for (const row of finanzierungRows) { - for (let m = 1; m <= MONTHS; m++) finCF[`m${m}`] += Math.round(row.values[`m${m}`] || 0) - } - for (const row of finanzAuszahlungRows) { - for (let m = 1; m <= MONTHS; m++) finCF[`m${m}`] -= Math.round(row.values[`m${m}`] || 0) - } - const ks = emptyMonthly() const lq = emptyMonthly() for (let m = 1; m <= MONTHS; m++) { ks[`m${m}`] = m === 1 ? 0 : Math.round(lq[`m${m - 1}`]) - // LIQUIDITÄT = Kontostand + Operativer Überschuss + Finanzierung - lq[`m${m}`] = Math.round(ks[`m${m}`] + (ueberschuss.values[`m${m}`] || 0) + (finCF[`m${m}`] || 0)) + lq[`m${m}`] = Math.round(ks[`m${m}`] + (ueberschuss.values[`m${m}`] || 0)) } await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(ks), kontostand.id]) await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(lq), liquiditaet.id])