[split-required] [guardrail-change] Enforce 500 LOC budget across all services
Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook) and split all 44 files exceeding 500 LOC into domain-focused modules: - consent-service (Go): models, handlers, services, database splits - backend-core (Python): security_api, rbac_api, pdf_service, auth splits - admin-core (TypeScript): 5 page.tsx + sidebar extractions - pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits - voice-service (Python): enhanced_task_orchestrator split Result: 0 violations, 36 exempted (pipeline, tests, pure-data files). Go build verified clean. No behavior changes — pure structural splits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
154
pitch-deck/lib/finanzplan/engine-liquiditaet.ts
Normal file
154
pitch-deck/lib/finanzplan/engine-liquiditaet.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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() }
|
||||
}
|
||||
Reference in New Issue
Block a user