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
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:
@@ -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 =>
|
||||
|
||||
Reference in New Issue
Block a user