/** * Liquiditaet — rolling cash balance computation * * Computes operative Einzahlungen/Auszahlungen sums, * Überschuss vor Investitionen/Entnahmen, and rolling Kontostand. */ import { Pool } from 'pg' import { MonthlyValues, MONTHS, emptyMonthly, FPLiquiditaet, } from './types' export interface LiquiditaetContext { totalRevenue: MonthlyValues totalMaterial: MonthlyValues totalPersonal: MonthlyValues totalSonstige: MonthlyValues totalInvest: MonthlyValues } /** * Compute all liquidity rows and rolling balance. * Writes computed values back to DB. */ export async function computeLiquiditaet( pool: Pool, liquid: FPLiquiditaet[], ctx: LiquiditaetContext, ): Promise<{ endstand: MonthlyValues }> { const findLiq = (label: string) => liquid.find(r => r.row_label === label) // Computed rows — link to computed totals const liqUmsatz = findLiq('Umsatzerlöse') if (liqUmsatz) { await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(ctx.totalRevenue), liqUmsatz.id]) liqUmsatz.values = ctx.totalRevenue } const liqMaterial = findLiq('Materialaufwand') if (liqMaterial) { await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(ctx.totalMaterial), liqMaterial.id]) liqMaterial.values = ctx.totalMaterial } const liqPersonal = findLiq('Personalkosten') if (liqPersonal) { await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(ctx.totalPersonal), liqPersonal.id]) liqPersonal.values = ctx.totalPersonal } const liqSonstige = findLiq('Sonstige Kosten') if (liqSonstige) { await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(ctx.totalSonstige), liqSonstige.id]) liqSonstige.values = ctx.totalSonstige } const liqInvest = findLiq('Investitionen') if (liqInvest) { await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(ctx.totalInvest), liqInvest.id]) liqInvest.values = ctx.totalInvest } // Compute sums and rolling balance 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') // Dynamically categorize rows by row_type const einzahlungenOperativ = ['Umsatzerlöse', 'Sonst. betriebl. Erträge', 'Anzahlungen'] const finanzierungRows = liquid.filter(r => r.row_type === 'einzahlung' && !einzahlungenOperativ.includes(r.row_label) && !r.row_label.includes('Summe') ) const auszahlungenOperativ = ['Materialaufwand', 'Personalkosten', 'Sonstige Kosten', 'Umsatzsteuer', 'Gewerbesteuer', 'Körperschaftsteuer'] const finanzAuszahlungRows = liquid.filter(r => r.row_type === 'auszahlung' && !auszahlungenOperativ.includes(r.row_label) && !r.row_label.includes('Summe') ) // Summe EINZAHLUNGEN = nur operativ 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) } await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), sumEin.id]) sumEin.values = s } // Summe AUSZAHLUNGEN = nur operativ 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) } await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), sumAus.id]) sumAus.values = s } // OPERATIVER ÜBERSCHUSS VOR INVESTITIONEN 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)) await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), uebVorInv.id]) uebVorInv.values = s } // ÜBERSCHUSS VOR ENTNAHMEN = Operativer Überschuss - 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)) await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), uebVorEnt.id]) uebVorEnt.values = s } // ÜBERSCHUSS = Überschuss vor Entnahmen - Entnahmen 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)) await pool.query('UPDATE fp_liquiditaet SET values = $1 WHERE id = $2', [JSON.stringify(s), ueberschuss.id]) ueberschuss.values = s } // Rolling Kontostand: Vormonat + Operativer Überschuss + Finanzierung if (kontostand && liquiditaet && ueberschuss) { 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}`]) lq[`m${m}`] = Math.round(ks[`m${m}`] + (ueberschuss.values[`m${m}`] || 0) + (finCF[`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]) kontostand.values = ks liquiditaet.values = lq } return { endstand: liquiditaet?.values || emptyMonthly() } }