feat(pitch-deck): live-compute sums for Liquidität + Kunden + Umsatz tabs
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m6s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 31s
CI / test-python-voice (push) Successful in 30s
CI / test-bqas (push) Successful in 32s

Extended live-compute to ALL tabs:
- Liquidität: "Summe ERTRÄGE" = sum of einzahlung rows,
  "Summe AUSZAHLUNGEN" = sum of auszahlung rows
- Kunden: GESAMT rows = sum of tier detail rows
- Umsatz: GESAMTUMSATZ = sum of all revenue rows
- Materialaufwand: SUMME = sum of cost rows

ÜBERSCHUSS rows kept from DB (complex multi-step formula).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-20 10:26:23 +02:00
parent 67ed5e542d
commit ec7326cfe1

View File

@@ -580,27 +580,47 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
// Live-compute sum rows from detail rows (like Excel formulas) // Live-compute sum rows from detail rows (like Excel formulas)
const computedRows = rows.map(row => { const computedRows = rows.map(row => {
const label = getLabel(row) const label = getLabel(row)
const cat = (row as Record<string, unknown>).category as string || '' const cat = row.category as string || ''
if (!row.is_sum_row) return row const rowType = (row as Record<string, unknown>).row_type as string || ''
const isSumLabel = row.is_sum_row || label.includes('Summe') || label.includes('SUMME') || label.includes('ÜBERSCHUSS') || label.includes('UEBERSCHUSS')
// Find detail rows for this category if (!isSumLabel) return row
const detailRows = rows.filter(r => {
const rCat = (r as Record<string, unknown>).category as string || ''
return rCat === cat && !r.is_sum_row
})
// For "Summe sonstige" or "SUMME Betriebliche" — sum all non-sum, non-personal, non-abschreibung let sourceRows: SheetRow[] = []
let sourceRows = detailRows
if (label.includes('Summe sonstige') || label.includes('SUMME Betriebliche')) { // === Betriebliche Aufwendungen: category-based sums ===
if (cat && cat !== 'summe') {
sourceRows = rows.filter(r => (r.category as string) === cat && !r.is_sum_row && getLabel(r) !== label)
} else if (label.includes('Summe sonstige')) {
sourceRows = rows.filter(r => { sourceRows = rows.filter(r => {
const rCat = (r as Record<string, unknown>).category as string || '' const rCat = r.category as string || ''
const rLabel = getLabel(r) const rLabel = getLabel(r)
if (r.is_sum_row) return false return !r.is_sum_row && rCat !== 'personal' && rCat !== 'abschreibungen' && rCat !== 'summe' &&
if (label.includes('Summe sonstige')) { !rLabel.includes('Personalkosten') && !rLabel.includes('Abschreibungen') && !rLabel.includes('Summe') && !rLabel.includes('SUMME')
return rCat !== 'personal' && rCat !== 'abschreibungen' && !rLabel.includes('Personalkosten') && !rLabel.includes('Abschreibungen') })
} } else if (label.includes('SUMME Betriebliche')) {
// SUMME Betriebliche = everything including personal + abschreibungen sourceRows = rows.filter(r => !r.is_sum_row && !getLabel(r).includes('Summe') && !getLabel(r).includes('SUMME') && (r.category as string) !== 'summe')
return true }
// === Liquidität: row_type-based sums ===
else if (label.includes('Summe') && label.includes('ERTR')) {
sourceRows = rows.filter(r => (r as Record<string, unknown>).row_type === 'einzahlung' && !getLabel(r).includes('Summe'))
} else if (label.includes('Summe') && label.includes('AUSZAHL')) {
sourceRows = rows.filter(r => (r as Record<string, unknown>).row_type === 'auszahlung' && !getLabel(r).includes('Summe'))
}
// === Liquidität: ÜBERSCHUSS = Erträge - Auszahlungen ===
else if (label.includes('ÜBERSCHUSS') || label.includes('UEBERSCHUSS')) {
// These are complex formulas — keep DB values for now
return row
}
// === Kunden/Umsatz GESAMT rows ===
else if (label.includes('GESAMT') || label.includes('Bestandskunden gesamt') || label.includes('GESAMTUMSATZ') || label.includes('SUMME Material')) {
// Sum all non-sum rows
sourceRows = rows.filter(r => {
const rLabel = getLabel(r)
return !rLabel.includes('GESAMT') && !rLabel.includes('Summe') && !rLabel.includes('SUMME') && !rLabel.includes('Bestandskunden gesamt') && !rLabel.includes('GESAMTUMSATZ')
}) })
} }
@@ -609,10 +629,7 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
const computed: Record<string, number> = {} const computed: Record<string, number> = {}
for (let m = 1; m <= 60; m++) { for (let m = 1; m <= 60; m++) {
const key = `m${m}` const key = `m${m}`
computed[key] = sourceRows.reduce((sum, r) => { computed[key] = Math.round(sourceRows.reduce((sum, r) => sum + (getValues(r)[key] || 0), 0))
const v = getValues(r)
return sum + (v[key] || 0)
}, 0)
} }
return { ...row, values: computed, values_total: computed } return { ...row, values: computed, values_total: computed }