From 5fd65e8a387bd5963236434bd157442b2031640a Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Sat, 28 Mar 2026 17:07:09 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Steuerberechnung=20in=20GuV=20=E2=80=94?= =?UTF-8?q?=20KSt=20+=20GewSt=20+=20Verlustvortrag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stockach 78333, Hebesatz 350%: - Gewerbesteuer: 3,5% × 3,5 = 12,25% - Körperschaftsteuer: 15% + 5,5% Soli = 15,825% - Gesamt: ~28,08% auf den Gewinn Verlustvortrag: - Verluste werden kumuliert und mit künftigen Gewinnen verrechnet - Bis 1 Mio EUR: 100% verrechenbar - Über 1 Mio EUR: nur 60% (Mindestbesteuerung) GuV-Zeilen: Gewerbesteuer, Körperschaftsteuer, Steuern gesamt, Ergebnis nach Steuern, Jahresüberschuss Liquidität: Steuern als monatliche Auszahlungen (1/12 des Jahres) Co-Authored-By: Claude Opus 4.6 (1M context) --- pitch-deck/lib/finanzplan/engine.ts | 96 +++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/pitch-deck/lib/finanzplan/engine.ts b/pitch-deck/lib/finanzplan/engine.ts index 3e67be4..061d1cd 100644 --- a/pitch-deck/lib/finanzplan/engine.ts +++ b/pitch-deck/lib/finanzplan/engine.ts @@ -409,15 +409,103 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise ) } - // EBIT + // EBIT (Betriebsergebnis) const ebit: AnnualValues = {} for (let y = 2026; y <= 2030; y++) { const k = `y${y}` - ebit[k] = (umsatzAnnual[k] || 0) - (materialAnnual[k] || 0) - (personalAnnual[k] || 0) - (afaAnnual[k] || 0) - (sonstigeAnnual[k] || 0) + ebit[k] = Math.round((umsatzAnnual[k] || 0) - (materialAnnual[k] || 0) - (personalAnnual[k] || 0) - (afaAnnual[k] || 0) - (sonstigeAnnual[k] || 0)) } await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(ebit), scenarioId, 'EBIT']) - await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(ebit), scenarioId, 'Ergebnis nach Steuern']) - await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(ebit), scenarioId, 'Jahresueberschuss']) + + // Steuerberechnung (nur auf Gewinne, mit Verlustvortrag) + // Stockach 78333: Hebesatz 350% + // Gewerbesteuer = 3,5% × 3,5 = 12,25% + // Körperschaftsteuer = 15% + 5,5% Soli = 15,825% + // Gesamt: ~28,075% + const GEWERBESTEUER_RATE = 0.035 * 3.5 // 12,25% + const KOERPERSCHAFTSTEUER_RATE = 0.15 * 1.055 // 15,825% (inkl. Soli) + + const gewerbesteuer: AnnualValues = {} + const koerperschaftsteuer: AnnualValues = {} + const steuernGesamt: AnnualValues = {} + const ergebnisNachSteuern: AnnualValues = {} + let verlustvortrag = 0 // kumulierter Verlustvortrag + + for (let y = 2026; y <= 2030; y++) { + const k = `y${y}` + const gewinn = ebit[k] || 0 + + if (gewinn <= 0) { + // Verlust: keine Steuern, Verlustvortrag aufbauen + verlustvortrag += Math.abs(gewinn) + gewerbesteuer[k] = 0 + koerperschaftsteuer[k] = 0 + steuernGesamt[k] = 0 + ergebnisNachSteuern[k] = Math.round(gewinn) + } else { + // Gewinn: Verlustvortrag verrechnen + // Bis 1 Mio EUR: 100% verrechenbar + // Über 1 Mio EUR: nur 60% verrechenbar (Mindestbesteuerung) + let verrechenbar = 0 + if (verlustvortrag > 0) { + if (gewinn <= 1000000) { + verrechenbar = Math.min(verlustvortrag, gewinn) + } else { + verrechenbar = Math.min(verlustvortrag, 1000000 + (gewinn - 1000000) * 0.6) + } + verlustvortrag -= verrechenbar + } + + const zuVersteuern = Math.max(0, gewinn - verrechenbar) + const gst = Math.round(zuVersteuern * GEWERBESTEUER_RATE) + const kst = Math.round(zuVersteuern * KOERPERSCHAFTSTEUER_RATE) + + gewerbesteuer[k] = gst + koerperschaftsteuer[k] = kst + steuernGesamt[k] = gst + kst + ergebnisNachSteuern[k] = Math.round(gewinn - gst - kst) + } + } + + await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(gewerbesteuer), scenarioId, 'Gewerbesteuer']) + await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(koerperschaftsteuer), scenarioId, 'Koerperschaftssteuer']) + await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(steuernGesamt), scenarioId, 'Steuern gesamt']) + await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(ergebnisNachSteuern), scenarioId, 'Ergebnis nach Steuern']) + await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(ergebnisNachSteuern), scenarioId, 'Jahresueberschuss']) + + // Steuern auch in Liquidität eintragen (monatlich = 1/12 des Jahresbetrags) + const liqGewSt = findLiq('Gewerbesteuer') + const liqKSt = findLiq('Koerperschaftsteuer') + if (liqGewSt) { + const v = emptyMonthly() + for (let y = 2026; y <= 2030; y++) { + const jahresBetrag = gewerbesteuer[`y${y}`] || 0 + if (jahresBetrag > 0) { + const monatlich = Math.round(jahresBetrag / 12) + const startM = (y - 2026) * 12 + 1 + for (let m = startM; m <= startM + 11 && m <= MONTHS; m++) { + v[`m${m}`] = monatlich + } + } + } + await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(v), liqGewSt.id]) + liqGewSt.values = v + } + if (liqKSt) { + const v = emptyMonthly() + for (let y = 2026; y <= 2030; y++) { + const jahresBetrag = koerperschaftsteuer[`y${y}`] || 0 + if (jahresBetrag > 0) { + const monatlich = Math.round(jahresBetrag / 12) + const startM = (y - 2026) * 12 + 1 + for (let m = startM; m <= startM + 11 && m <= MONTHS; m++) { + v[`m${m}`] = monatlich + } + } + } + await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(v), liqKSt.id]) + liqKSt.values = v + } return { personalkosten: { total_brutto: totalBrutto, total_sozial: totalSozial, total: totalPersonal, positions: personal, headcount },