Merge remote-tracking branch 'gitea/main'
Some checks failed
Build pitch-deck / build-push-deploy (push) Failing after 1m13s
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 49s
CI / test-python-voice (push) Successful in 38s
CI / test-bqas (push) Successful in 31s

# Conflicts:
#	pitch-deck/components/slides/MilestonesSlide.tsx
#	pitch-deck/lib/finanzplan/engine.ts
This commit is contained in:
Benjamin Admin
2026-04-27 13:14:54 +02:00
21 changed files with 624 additions and 354 deletions

View File

@@ -2,7 +2,8 @@
* Betriebliche Aufwendungen — formula-based rows + category sums
*
* Computes formula-driven operating expenses (Fortbildung, Reisekosten, etc.),
* Gewerbesteuer, category sums, and Gesamtkosten.
* Gewerbesteuer, marketing, Berufsgenossenschaft, category sums, and Gesamtkosten.
* Also computes Cloud-Hosting formula in Materialaufwand.
*/
import { Pool } from 'pg'
@@ -13,6 +14,8 @@ import {
} from './types'
import { sumRows } from './engine-sheets'
type FPMaterialaufwand = import('./types').FPMaterialaufwand
export interface BetriebContext {
totalBrutto: MonthlyValues
totalPersonal: MonthlyValues
@@ -23,6 +26,27 @@ export interface BetriebContext {
totalBestandskunden: MonthlyValues
}
/**
* Compute Cloud-Hosting formula in Materialaufwand.
*/
export async function computeCloudHosting(
pool: Pool,
matRows: FPMaterialaufwand[],
totalBestandskunden: MonthlyValues,
): Promise<void> {
const cloudRow = matRows.find(r => r.row_label.includes('Cloud-Hosting'))
if (cloudRow) {
const computed = emptyMonthly()
for (let m = FOUNDING_MONTH; m <= MONTHS; m++) {
const kunden = totalBestandskunden[`m${m}`] || 0
const extraKunden = Math.max(0, kunden - 10) // first 10 included in base
computed[`m${m}`] = Math.round(extraKunden * 100 + 1500)
}
await pool.query('UPDATE fp_materialaufwand SET values = $1 WHERE id = $2', [JSON.stringify(computed), cloudRow.id])
cloudRow.values = computed
}
}
/**
* Compute all formula-based betriebliche aufwendungen rows and sums.
* Writes computed values back to DB.
@@ -41,6 +65,7 @@ export async function computeBetrieblicheAufwendungen(
// Formula-based rows: derive from headcount (excl. founders) or customers
const formulaRows: { label: string; perUnit: number; source: MonthlyValues }[] = [
{ label: 'Fort-/Weiterbildungskosten (F)', perUnit: 300, source: hcWithoutFounders },
// KFZ costs are manual (from Jan 2028), not formula-based
{ label: 'Reisekosten (F)', perUnit: 75, source: ctx.headcount },
{ label: 'Bewirtungskosten (F)', perUnit: 50, source: ctx.totalBestandskunden },
{ label: 'Internet/Mobilfunk (F)', perUnit: 50, source: ctx.headcount },
@@ -58,7 +83,7 @@ export async function computeBetrieblicheAufwendungen(
}
}
// Berufsgenossenschaft (VBG IT/Büro): ~0.5% of total brutto payroll
// Berufsgenossenschaft (VBG IT/Buero): ~0.5% of total brutto payroll
const bgRow = betrieb.find(r => r.row_label.includes('Berufsgenossenschaft'))
if (bgRow) {
const computed = emptyMonthly()
@@ -95,6 +120,7 @@ export async function computeBetrieblicheAufwendungen(
}
// Gewerbesteuer (F): 12.25% of monthly profit (only when positive)
// Monthly profit = Revenue - Material - Personnel - AfA - other opex (excl. taxes)
const gewStRow = betrieb.find(r => r.row_label.includes('Gewerbesteuer'))
if (gewStRow) {
const nonTaxOpex = betrieb.filter(r =>