feat(pitch-deck): live-computed sum rows in Finanzplan (like Excel formulas)
Some checks failed
Build pitch-deck / build-push-deploy (push) Successful in 1m11s
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 29s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Has been cancelled

Sum rows (is_sum_row=true) are now computed live in the frontend from
their detail rows, not read from stale DB values. This means:
- Category sums (Versicherungen, Marketing, Sonstige etc.) always match
- "Summe sonstige" = all non-personal, non-AfA rows
- "SUMME Betriebliche" = all rows including personal + AfA
- No more manual recompute needed after DB changes

Also: chart labels increased from 7-8px to 11px for readability.

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

View File

@@ -39,6 +39,7 @@ interface SheetRow {
position?: string
start_date?: string
purchase_amount?: number
[key: string]: unknown
}
const MONTH_LABELS = [
@@ -575,7 +576,50 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
</tr>
</thead>
<tbody>
{rows.map(row => {
{(() => {
// Live-compute sum rows from detail rows (like Excel formulas)
const computedRows = rows.map(row => {
const label = getLabel(row)
const cat = (row as Record<string, unknown>).category as string || ''
if (!row.is_sum_row) return row
// Find detail rows for this category
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 = detailRows
if (label.includes('Summe sonstige') || label.includes('SUMME Betriebliche')) {
sourceRows = rows.filter(r => {
const rCat = (r as Record<string, unknown>).category as string || ''
const rLabel = getLabel(r)
if (r.is_sum_row) return false
if (label.includes('Summe sonstige')) {
return rCat !== 'personal' && rCat !== 'abschreibungen' && !rLabel.includes('Personalkosten') && !rLabel.includes('Abschreibungen')
}
// SUMME Betriebliche = everything including personal + abschreibungen
return true
})
}
if (sourceRows.length === 0) return row
const computed: Record<string, number> = {}
for (let m = 1; m <= 60; m++) {
const key = `m${m}`
computed[key] = sourceRows.reduce((sum, r) => {
const v = getValues(r)
return sum + (v[key] || 0)
}, 0)
}
return { ...row, values: computed, values_total: computed }
})
return computedRows
})().map(row => {
const values = getValues(row)
const label = getLabel(row)
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')