fix: Sortierung Personalkosten + Umlaute DB + Summenzeilen

- Gründer immer sort_order 1+2, dann nach start_date
- Beide Gründer exakt gleiches Gehalt (7.000 EUR/Mo ab Jan 2027)
- Alle Pos-Namen durchnummeriert (Pos 3 bis Pos 35)

Umlaute in DB-Labels (Liquidität, GuV, Betriebliche):
  Umsatzerloese→Umsatzerlöse, UEBERSCHUSS→ÜBERSCHUSS,
  Koerperschaftsteuer→Körperschaftsteuer, etc.
Engine-Labels synchron aktualisiert.

Summenzeile (SUMME) als tfoot für:
  Personalkosten, Materialaufwand, Betriebliche Aufwendungen,
  Investitionen, Sonstige Erträge

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-28 21:22:45 +01:00
parent 3188054462
commit a66b76001b
2 changed files with 57 additions and 16 deletions

View File

@@ -229,10 +229,10 @@ export default function FinanzplanSlide({ lang }: FinanzplanSlideProps) {
{rows.map(row => {
const values = getValues(row)
const label = getLabel(row)
const isSumRow = row.is_sum_row || label.includes('GESAMT') || label.includes('Summe') || label.includes('UEBERSCHUSS') || label.includes('LIQUIDITAET')
const isSumRow = row.is_sum_row || label.includes('GESAMT') || label.includes('Summe') || label.includes('ÜBERSCHUSS') || label.includes('LIQUIDITÄT') || label.includes('UEBERSCHUSS') || label.includes('LIQUIDITAET')
const isEditable = row.is_editable
// Balance rows show Dec value, flow rows show annual sum
const isBalanceRow = label.includes('Kontostand') || label === 'LIQUIDITAET'
const isBalanceRow = label.includes('Kontostand') || label === 'LIQUIDITÄT' || label === 'LIQUIDITAET'
let annual = 0
if (isBalanceRow) {
@@ -283,6 +283,47 @@ export default function FinanzplanSlide({ lang }: FinanzplanSlideProps) {
)
})}
</tbody>
{/* Summenzeile für relevante Sheets */}
{['personalkosten', 'materialaufwand', 'betriebliche', 'investitionen', 'sonst_ertraege'].includes(activeSheet) && rows.length > 0 && (() => {
// Berechne Summe über alle Zeilen die keine Summenzeilen sind
const sumValues: Record<string, number> = {}
let sumAnnual = 0
const nonSumRows = rows.filter(r => {
const l = getLabel(r)
return !(r.is_sum_row || l.includes('GESAMT') || l.includes('Summe') || l.includes('Gesamtkosten'))
})
for (let idx = 0; idx < 12; idx++) {
const mKey = `m${monthStart + idx}`
let colSum = 0
for (const row of nonSumRows) {
const v = getValues(row)
colSum += v[mKey] || 0
}
sumValues[mKey] = colSum
sumAnnual += colSum
}
return (
<tfoot>
<tr className="border-t-2 border-white/20 bg-white/[0.05]">
<td className="py-1.5 px-2 sticky left-0 bg-slate-900/90 backdrop-blur font-bold text-white/80 text-xs">
{de ? 'SUMME' : 'TOTAL'}
</td>
<td className={`text-right py-1.5 px-2 font-bold text-xs ${sumAnnual < 0 ? 'text-red-400' : 'text-white/80'}`}>
{formatCell(sumAnnual)}
</td>
{Array.from({ length: 12 }, (_, idx) => {
const mKey = `m${monthStart + idx}`
const v = sumValues[mKey] || 0
return (
<td key={idx} className={`text-right py-1.5 px-1.5 font-bold text-xs ${v < 0 ? 'text-red-400' : v > 0 ? 'text-white/70' : 'text-white/15'}`}>
{formatCell(v)}
</td>
)
})}
</tr>
</tfoot>
)
})()}
</table>
)}
</GlassCard>

View File

@@ -262,7 +262,7 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
const findLiq = (label: string) => liquid.find(r => r.row_label === label)
// Computed rows
const liqUmsatz = findLiq('Umsatzerloese')
const liqUmsatz = findLiq('Umsatzerlöse')
if (liqUmsatz) {
await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(totalRevenue), liqUmsatz.id])
liqUmsatz.values = totalRevenue
@@ -292,19 +292,19 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
// WICHTIG: Überschuss = nur operativer Cashflow (ohne Kapitaleinzahlungen)
const sumEin = findLiq('Summe EINZAHLUNGEN')
const sumAus = findLiq('Summe AUSZAHLUNGEN')
const uebVorInv = findLiq('UEBERSCHUSS VOR INVESTITIONEN')
const uebVorEnt = findLiq('UEBERSCHUSS VOR ENTNAHMEN')
const ueberschuss = findLiq('UEBERSCHUSS')
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('LIQUIDITAET')
const liquiditaet = findLiq('LIQUIDITÄT')
// Operative Einzahlungen (OHNE Eigenkapital und Fremdkapital)
const einzahlungenOperativ = ['Umsatzerloese', 'Sonst. betriebl. Ertraege', 'Anzahlungen']
const einzahlungenOperativ = ['Umsatzerlöse', 'Sonst. betriebl. Erträge', 'Anzahlungen']
// Finanzierung (separat)
const finanzierung = ['Neuer Eigenkapitalzugang', 'Erhaltenes Fremdkapital']
// Operative Auszahlungen (OHNE Kreditrückzahlungen)
const auszahlungenOperativ = ['Materialaufwand', 'Personalkosten', 'Sonstige Kosten', 'Umsatzsteuer', 'Gewerbesteuer', 'Koerperschaftsteuer']
const finanzAuszahlungen = ['Kreditrueckzahlungen']
const auszahlungenOperativ = ['Materialaufwand', 'Personalkosten', 'Sonstige Kosten', 'Umsatzsteuer', 'Gewerbesteuer', 'Körperschaftsteuer']
const finanzAuszahlungen = ['Kreditrückzahlungen']
// Summe EINZAHLUNGEN = nur operativ (für die Zeile "Summe Einzahlungen")
if (sumEin) {
@@ -345,7 +345,7 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
}
// ÜBERSCHUSS = Überschuss vor Entnahmen - Entnahmen (immer noch rein operativ)
const entnahmen = findLiq('Kapitalentnahmen/Ausschuettungen')
const entnahmen = findLiq('Kapitalentnahmen/Ausschüttungen')
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))
@@ -392,10 +392,10 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
// Write GuV rows
const guvUpdates: { label: string; values: AnnualValues }[] = [
{ label: 'Umsatzerloese', values: umsatzAnnual },
{ label: 'Umsatzerlöse', values: umsatzAnnual },
{ label: 'Gesamtleistung', values: umsatzAnnual },
{ label: 'Summe Materialaufwand', values: materialAnnual },
{ label: 'Loehne und Gehaelter', values: personalBruttoAnnual },
{ label: 'Löhne und Gehälter', values: personalBruttoAnnual },
{ label: 'Soziale Abgaben', values: personalSozialAnnual },
{ label: 'Summe Personalaufwand', values: personalAnnual },
{ label: 'Abschreibungen', values: afaAnnual },
@@ -468,14 +468,14 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
}
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(koerperschaftsteuer), scenarioId, 'Körperschaftssteuer'])
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'])
await pool.query('UPDATE fp_guv SET values = $1 WHERE scenario_id = $2 AND row_label = $3', [JSON.stringify(ergebnisNachSteuern), scenarioId, 'Jahresüberschuss'])
// Steuern auch in Liquidität eintragen (monatlich = 1/12 des Jahresbetrags)
const liqGewSt = findLiq('Gewerbesteuer')
const liqKSt = findLiq('Koerperschaftsteuer')
const liqKSt = findLiq('Körperschaftsteuer')
if (liqGewSt) {
const v = emptyMonthly()
for (let y = 2026; y <= 2030; y++) {