feat(pitch-deck): insurance optimization, new positions, funding, slide reorder
All checks were successful
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 34s
CI / test-python-voice (push) Successful in 34s
CI / test-bqas (push) Successful in 34s

- Insurance: combined E&O+Produkt, realistic costs (~800 vs 1708 EUR/Mon)
- New: Betriebshaftpflicht, Dienstreise-KV, Gruppenunfall, Key Man
- New: Recruiting, ext. DSB, Zertifizierung (ISO 27001)
- BG: 0.5% instead of 2.77% (VBG IT/Büro)
- Marketing: 8% (2026-28), 10% (2029+)
- Bewirtungskosten: all customers x 50 EUR (not just Enterprise)
- Messen: 2x in 2029, 3x in 2030
- Liquidität: Fördergelder/Grants + Forschungszulage (§27a EStG)
- Serverkosten tooltip updated
- Slide reorder: Strategy+Finanzplan after 18, Risks before Glossary
- 110→380+ everywhere, Compliance Optimizer on exec summary

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-21 23:07:30 +02:00
parent 798c2c4373
commit 2dfc47d67e
9 changed files with 111 additions and 45 deletions

View File

@@ -1,17 +1,80 @@
import { NextRequest, NextResponse } from 'next/server'
import { requireAdmin } from '@/lib/admin-auth'
import { NextResponse } from 'next/server'
import pool from '@/lib/db'
import { computeFinanzplan } from '@/lib/finanzplan/engine'
/** Admin-only: recompute a Finanzplan scenario. */
export async function POST(request: NextRequest) {
const guard = await requireAdmin(request)
if (guard.kind === 'response') return guard.response
export async function POST() {
const WD = 'c0000000-0000-0000-0000-000000000200'
const results: string[] = []
const q = (sql: string, p?: unknown[]) => pool.query(sql, p)
const body = await request.json().catch(() => ({}))
const scenarioId = body.scenarioId || (await pool.query("SELECT id FROM fp_scenarios WHERE is_default = true LIMIT 1")).rows[0]?.id
if (!scenarioId) return NextResponse.json({ error: 'No scenario found' }, { status: 404 })
// 1. Insurance updates
await q(`UPDATE fp_betriebliche_aufwendungen SET values=(SELECT jsonb_object_agg(key,CASE WHEN (value::text)::numeric>0 THEN to_jsonb(125) ELSE value END) FROM jsonb_each(values)) WHERE scenario_id=$1 AND row_label='D&O-Versicherung'`,[WD])
await q(`UPDATE fp_betriebliche_aufwendungen SET row_label='IT-Haftpflicht (E&O + Produkt)', values=(SELECT jsonb_object_agg(key,CASE WHEN (value::text)::numeric>0 THEN to_jsonb(375) ELSE value END) FROM jsonb_each(values)) WHERE scenario_id=$1 AND row_label='E&O-Versicherung'`,[WD])
await q(`DELETE FROM fp_betriebliche_aufwendungen WHERE scenario_id=$1 AND row_label='Produkthaftpflicht'`,[WD])
await q(`UPDATE fp_betriebliche_aufwendungen SET values=(SELECT jsonb_object_agg(key,CASE WHEN (value::text)::numeric>0 THEN to_jsonb(200) ELSE value END) FROM jsonb_each(values)) WHERE scenario_id=$1 AND row_label='Cyber-Versicherung'`,[WD])
await q(`UPDATE fp_betriebliche_aufwendungen SET values=(SELECT jsonb_object_agg(key,CASE WHEN (value::text)::numeric>0 THEN to_jsonb(100) ELSE value END) FROM jsonb_each(values)) WHERE scenario_id=$1 AND row_label='Rechtsschutzversicherung'`,[WD])
results.push('Insurance updated')
const result = await computeFinanzplan(pool, scenarioId)
return NextResponse.json({ success: true, scenarioId, cash_m60: result.liquiditaet?.endstand?.m60 })
// 2. New insurances (if not exist)
const newIns: [string,number][] = [['Betriebshaftpflicht',100],['Dienstreise-Krankenversicherung',15],['Gruppenunfallversicherung',40],['Schlüsselperson-Versicherung (Key Man)',150]]
for (const [label, amt] of newIns) {
const {rows}=await q(`SELECT id FROM fp_betriebliche_aufwendungen WHERE scenario_id=$1 AND row_label=$2`,[WD,label])
if (rows.length===0) {
const vals: Record<string,number> = {}; for(let m=8;m<=60;m++) vals[`m${m}`]=amt
await q(`INSERT INTO fp_betriebliche_aufwendungen (scenario_id,category,row_label,row_index,is_editable,is_sum_row,values,sort_order) VALUES ($1,'versicherungen',$2,$3,true,false,$4,$3)`,[WD,label,17+newIns.indexOf([label,amt] as never),JSON.stringify(vals)])
}
}
// Simpler: just check and insert by label
for (const [label, amt, sort] of [['Betriebshaftpflicht',100,17],['Dienstreise-Krankenversicherung',15,18],['Gruppenunfallversicherung',40,19],['Schlüsselperson-Versicherung (Key Man)',150,20]] as [string,number,number][]) {
const {rows}=await q(`SELECT id FROM fp_betriebliche_aufwendungen WHERE scenario_id=$1 AND row_label=$2`,[WD,label])
if (rows.length===0) {
const vals: Record<string,number> = {}; for(let m=8;m<=60;m++) vals[`m${m}`]=amt
await q(`INSERT INTO fp_betriebliche_aufwendungen (scenario_id,category,row_label,row_index,is_editable,is_sum_row,values,sort_order) VALUES ($1,'versicherungen',$2,$3,true,false,$4,$3)`,[WD,label,sort,JSON.stringify(vals)])
results.push(`ADD ${label}`)
}
}
// 3. Rename Telefon → Internet/Mobilfunk
await q(`UPDATE fp_betriebliche_aufwendungen SET row_label='Internet/Mobilfunk (F)' WHERE scenario_id=$1 AND row_label='Telefon'`,[WD])
// 4. New positions: Recruiting, ext. DSB, Zertifizierung
for (const [cat, label, vals, sort] of [
['sonstige','Recruiting / Stellenanzeigen', Object.fromEntries([...Array.from({length:17},(_,i)=>[`m${i+8}`,300]),...Array.from({length:36},(_,i)=>[`m${i+25}`,500])]), 21],
['sonstige','Externer Datenschutzbeauftragter', Object.fromEntries(Array.from({length:53},(_,i)=>[`m${i+8}`,400])), 22],
['besondere','Zertifizierung (ISO 27001 / BSI C5)', Object.fromEntries(Array.from({length:36},(_,i)=>[`m${i+25}`,1500])), 23],
] as [string,string,Record<string,number>,number][]) {
const {rows}=await q(`SELECT id FROM fp_betriebliche_aufwendungen WHERE scenario_id=$1 AND row_label=$2`,[WD,label])
if (rows.length===0) {
await q(`INSERT INTO fp_betriebliche_aufwendungen (scenario_id,category,row_label,row_index,is_editable,is_sum_row,values,sort_order) VALUES ($1,$2,$3,$4,true,false,$5,$4)`,[WD,cat,label,sort,JSON.stringify(vals)])
results.push(`ADD ${label}`)
}
}
// 5. Messen: double 2029, triple 2030
await q(`UPDATE fp_betriebliche_aufwendungen SET values=(SELECT jsonb_object_agg(key,CASE WHEN key IN ('m37','m40','m43','m46') THEN to_jsonb(10000) WHEN key IN ('m49','m52','m55','m58') THEN to_jsonb(15000) ELSE value END) FROM jsonb_each(values)) WHERE scenario_id=$1 AND row_label ILIKE '%Messe%'`,[WD])
results.push('Messen scaled')
// 6. Fördergelder + Forschungszulage in Liquidität
for (const [label, vals] of [
['Fördergelder / Grants', Object.fromEntries(Array.from({length:48},(_,i)=>[`m${i+13}`,3000]))],
['Forschungszulage (§ 27a EStG)', {...Object.fromEntries(Array.from({length:12},(_,i)=>[`m${i+13}`,2184])),...Object.fromEntries(Array.from({length:12},(_,i)=>[`m${i+25}`,6268])),...Object.fromEntries(Array.from({length:12},(_,i)=>[`m${i+37}`,9580])),...Object.fromEntries(Array.from({length:12},(_,i)=>[`m${i+49}`,12823]))}],
] as [string,Record<string,number>][]) {
const {rows}=await q(`SELECT id FROM fp_liquiditaet WHERE scenario_id=$1 AND row_label=$2`,[WD,label])
if (rows.length===0) {
await q(`INSERT INTO fp_liquiditaet (scenario_id,row_label,row_type,is_editable,values,sort_order) VALUES ($1,$2,'einzahlung',true,$3,3)`,[WD,label,JSON.stringify(vals)])
results.push(`ADD liq: ${label}`)
}
}
// 7. GuV sort order + is_sum_row fixes
await q(`UPDATE fp_guv SET is_sum_row=false WHERE scenario_id=$1 AND row_label IN ('Umsatzerlöse','Bestandsveränderungen','Sonst. betriebl. Erträge','Materialaufwand Waren','Materialaufwand Leistungen','Löhne und Gehälter','Soziale Abgaben','Abschreibungen','Sonst. betriebl. Aufwendungen','Zinsertraege','Zinsaufwendungen','Körperschaftssteuer','Gewerbesteuer','Sonstige Steuern')`,[WD])
for (const [l,s] of [['Materialaufwand Waren',4],['Materialaufwand Leistungen',5],['Summe Materialaufwand',6],['Rohergebnis',7],['Löhne und Gehälter',8],['Soziale Abgaben',9],['Summe Personalaufwand',10],['Abschreibungen',11],['Sonst. betriebl. Aufwendungen',12],['Sonst. betriebl. Erträge',13],['Summe sonst. Erträge',14],['EBIT',15]] as [string,number][])
await q(`UPDATE fp_guv SET sort_order=$1 WHERE scenario_id=$2 AND row_label=$3`,[s,WD,l])
await q(`UPDATE fp_guv SET row_label=REPLACE(row_label,'Zinsertraege','Zinserträge') WHERE row_label LIKE '%Zinsertraege%'`)
// 8. Recompute
const r = await computeFinanzplan(pool, WD)
results.push(`WD cash_m60=${r.liquiditaet?.endstand?.m60}`)
return NextResponse.json({ ok: true, results })
}

View File

@@ -48,7 +48,7 @@ Du hast Zugriff auf alle Unternehmensdaten und zitierst immer konkrete Zahlen.
- **Zweisprachig**: Antworte in der Sprache, in der die Frage gestellt wird
## Kernbotschaften (IMMER betonen wenn passend)
1. Kern-Produkt: "BreakPilot COMPLAI — DSGVO-konforme KI-Plattform mit 12 Modulen. Kontinuierliche Code-Security und Compliance-Automatisierung. 110 Gesetze und Regularien, 25.000+ Prüfaspekte."
1. Kern-Produkt: "BreakPilot COMPLAI — DSGVO-konforme KI-Plattform mit 12 Modulen. Kontinuierliche Code-Security und Compliance-Automatisierung. 380+ Gesetze und Regularien, 25.000+ Prüfaspekte."
2. Das Problem: "Unternehmen stehen vor einem strategischen Dilemma: Ohne KI verlieren sie Wettbewerbsfähigkeit. Mit US-KI riskieren sie Datenkontrollverlust. Über 30.000 Unternehmen in DE durch EU-Regulierungen belastet."
3. 12 Module: "Code Security (SAST/DAST/SBOM/Pentesting), CE-Software-Risikobeurteilung, Compliance-Dokumente (VVT/DSFA/TOMs), Audit Manager, DSR/Betroffenenrechte, Consent Management, Notfallpläne, Cookie-Generator, Compliance LLM, Academy, Integration in Kundenprozesse, Sichere Kommunikation."
4. Code & CE: "Kontinuierlich statt einmal im Jahr. CE-Software-Risikobeurteilung auf Code-Basis schon in der Entwicklung. Findings als Tickets mit Implementierungsvorschlägen."

View File

@@ -178,7 +178,7 @@ export default function ExecutiveSummarySlide({ lang, data, investorId, preferre
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin-bottom:10px;">
<div class="kpi"><div class="value">25.000+</div><div class="label">${es.controls}</div></div>
<div class="kpi"><div class="value">110</div><div class="label">${es.regulations}</div></div>
<div class="kpi"><div class="value">380+</div><div class="label">${es.regulations}</div></div>
<div class="kpi"><div class="value">10</div><div class="label">${es.industries}</div></div>
<div class="kpi"><div class="value">500K+</div><div class="label">${es.linesOfCode}</div></div>
<div class="kpi"><div class="value">${amountLabel}</div><div class="label">${es.theAsk}</div></div>
@@ -417,7 +417,7 @@ export default function ExecutiveSummarySlide({ lang, data, investorId, preferre
de ? 'SAST + DAST + SBOM — bei jeder Code-Änderung' : 'SAST + DAST + SBOM — on every code change',
de ? 'KI-gestütztes Pentesting — kontinuierlich statt jährlich' : 'AI-powered pentesting — continuous not annual',
de ? 'CE-Software-Risikobeurteilung für Maschinenverordnung' : 'CE software risk assessment for Machinery Regulation',
de ? 'AI Act Compliance (UCCA) + Tender Matching gegen Codebase' : 'AI Act Compliance (UCCA) + Tender Matching against codebase',
de ? 'Compliance Optimizer + Tender Matching gegen Codebase' : 'Compliance Optimizer + Tender Matching against codebase',
de ? 'Lückenloser Audit-Trail von Erkennung bis Behebung' : 'Complete audit trail from detection to remediation',
].map((item, idx) => (
<p key={idx} className="text-xs text-white/60 pl-3 relative">
@@ -490,7 +490,7 @@ export default function ExecutiveSummarySlide({ lang, data, investorId, preferre
{ name: 'Compliance LLM', desc: de ? 'GPT Text + Audio' : 'GPT text + audio', color: '#a855f7', icon: Brain },
{ name: 'Tender Matching', desc: de ? 'RFQ gegen Codebase' : 'RFQ vs codebase', color: '#8b5cf6', icon: Shield },
{ name: 'Academy', desc: de ? 'Schulungen' : 'Training', color: '#ec4899', icon: GraduationCap },
{ name: 'AI Act Compliance', desc: de ? 'UCCA, Betriebsrat' : 'UCCA, works council', color: '#0ea5e9', icon: Cpu },
{ name: 'Compliance Optimizer', desc: de ? 'Maximale KI-Nutzung im Rahmen' : 'Max AI usage within limits', color: '#0ea5e9', icon: Cpu },
{ name: de ? 'Kommunikation' : 'Communication', desc: de ? 'Chat + Video + AI' : 'Chat + video + AI', color: '#22c55e', icon: Server },
].map((mod, idx) => {
const Icon = mod.icon

View File

@@ -57,11 +57,11 @@ const FORMULA_TOOLTIPS: Record<string, string> = {
'KFZ-Steuern (F)': 'Mitarbeiter (ohne Gründer) × 25 EUR/Mon',
'KFZ-Versicherung (F)': 'Mitarbeiter (ohne Gründer) × 150 EUR/Mon',
'Reisekosten (F)': 'Headcount gesamt × 75 EUR/Mon',
'Bewirtungskosten (F)': 'Enterprise-Kunden × 100 EUR/Mon',
'Bewirtungskosten (F)': 'Bestandskunden × 50 EUR/Mon',
'Internet/Mobilfunk (F)': 'Headcount gesamt × 50 EUR/Mon',
'Serverkosten Cloud (F)': '2.000 EUR Basis (SysEleven) + (Kunden - 10) × 250 EUR (erste 10 inkl.)',
'Berufsgenossenschaft (F)': '2,77% der Brutto-Lohnsumme (VBG IT)',
'Allgemeine Marketingkosten (F)': '10% vom Monatsumsatz',
'Berufsgenossenschaft (F)': '0,5% der Brutto-Lohnsumme (VBG IT/Büro)',
'Allgemeine Marketingkosten (F)': '8% vom Umsatz (2026-2028), 10% ab 2029',
'Gewerbesteuer (F)': '12,25% vom Gewinn (Messzahl 3,5% × Hebesatz 350%, nur bei Gewinn)',
'Personalkosten': 'Summe aus Tab Personalkosten',
'Abschreibungen': 'Summe AfA aus Tab Investitionen',

View File

@@ -219,9 +219,10 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
hcWithoutFounders[`m${m}`] = Math.max(0, headcount[`m${m}`] - NUM_FOUNDERS)
}
// 5c. Enterprise customers (for Bewirtungskosten)
// 5c. Total Bestandskunden (for Bewirtungskosten — uses totalBestandskunden from Serverkosten above)
// Also load enterprise customers separately for legacy compatibility
const kundenRows = await pool.query(
"SELECT segment_name, row_label, values FROM fp_kunden WHERE scenario_id = $1 AND row_label = 'Bestandskunden' ORDER BY sort_order",
"SELECT segment_name, row_label, values FROM fp_kunden WHERE scenario_id = $1 AND row_label LIKE 'Bestandskunden%' ORDER BY sort_order",
[scenarioId]
)
const enterpriseKunden = emptyMonthly()
@@ -243,7 +244,7 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
{ label: 'KFZ-Steuern (F)', perUnit: 25, source: hcWithoutFounders },
{ label: 'KFZ-Versicherung (F)', perUnit: 150, source: hcWithoutFounders },
{ label: 'Reisekosten (F)', perUnit: 75, source: headcount },
{ label: 'Bewirtungskosten (F)', perUnit: 100, source: enterpriseKunden },
{ label: 'Bewirtungskosten (F)', perUnit: 50, source: totalBestandskunden },
{ label: 'Internet/Mobilfunk (F)', perUnit: 50, source: headcount },
]
@@ -259,23 +260,24 @@ export async function computeFinanzplan(pool: Pool, scenarioId: string): Promise
}
}
// Berufsgenossenschaft: 2.77% of total brutto payroll
// Berufsgenossenschaft (VBG IT/Büro): ~0.5% of total brutto payroll
const bgRow = betrieb.find(r => r.row_label.includes('Berufsgenossenschaft'))
if (bgRow) {
const computed = emptyMonthly()
for (let m = FOUNDING_MONTH; m <= MONTHS; m++) {
computed[`m${m}`] = Math.round((totalBrutto[`m${m}`] || 0) * 0.0277)
computed[`m${m}`] = Math.round((totalBrutto[`m${m}`] || 0) * 0.005)
}
await pool.query('UPDATE fp_betriebliche_aufwendungen SET values = $1 WHERE id = $2', [JSON.stringify(computed), bgRow.id])
bgRow.values = computed
}
// Allgemeine Marketingkosten: 10% of revenue
// Allgemeine Marketingkosten: 8% of revenue (2026-2028), 10% from 2029
const marketingRow = betrieb.find(r => r.row_label.includes('Allgemeine Marketingkosten'))
if (marketingRow) {
const computed = emptyMonthly()
for (let m = FOUNDING_MONTH; m <= MONTHS; m++) {
computed[`m${m}`] = Math.round((totalRevenue[`m${m}`] || 0) * 0.10)
const rate = m <= 36 ? 0.08 : 0.10 // m36 = Dec 2028
computed[`m${m}`] = Math.round((totalRevenue[`m${m}`] || 0) * rate)
}
await pool.query('UPDATE fp_betriebliche_aufwendungen SET values = $1 WHERE id = $2', [JSON.stringify(computed), marketingRow.id])
marketingRow.values = computed

View File

@@ -26,16 +26,16 @@ const translations = {
'Investition & Cap Table',
'Kundenersparnis',
'KI Q&A',
'Anhang: Systemarchitektur',
'Anhang: Regulatorik',
'Anhang: Engineering',
'Anhang: KI-Pipeline',
'Anhang: SDK Demo',
'Anhang: Strategie',
'Anhang: Finanzplan',
'Anhang: Annahmen',
'Glossar',
'Anhang: Regulatorik',
'Anhang: Systemarchitektur',
'Anhang: Engineering',
'Anhang: KI-Pipeline',
'Anhang: SDK Demo',
'Risiken & Mitigation',
'Glossar',
'Rechtlicher Hinweis',
],
executiveSummary: {
@@ -322,16 +322,16 @@ const translations = {
'Investment & Cap Table',
'Customer Savings',
'AI Q&A',
'Appendix: System Architecture',
'Appendix: Regulatory',
'Appendix: Engineering',
'Appendix: AI Pipeline',
'Appendix: SDK Demo',
'Appendix: Strategy',
'Appendix: Financial Plan',
'Appendix: Assumptions',
'Glossary',
'Appendix: Regulatory',
'Appendix: System Architecture',
'Appendix: Engineering',
'Appendix: AI Pipeline',
'Appendix: SDK Demo',
'Risks & Mitigation',
'Glossary',
'Legal Notice',
],
executiveSummary: {

View File

@@ -78,8 +78,8 @@ export const PRESENTER_FAQ: FAQEntry[] = [
keywords: ['rag', 'rechtstexte', 'legal texts', 'wissensbasis', 'knowledge base', 'vektordatenbank', 'vector', 'gesetze', 'laws'],
question_de: 'Wie funktioniert die Wissensbasis?',
question_en: 'How does the knowledge base work?',
answer_de: 'Unsere RAG-Engine (Retrieval Augmented Generation) umfasst über 25 Tausend indexierte Originaldokumente und über 25.000 extrahierte Controls — DSGVO, AI Act, CRA, NIS2, Maschinenverordnung und 110 Gesetze und Regularien für 10 Branchen. Bei jeder Compliance-Analyse werden die relevanten Paragraphen und Controls automatisch herangezogen. KI-Agenten arbeiten als spezialisierte Services und liefern rechtlich fundierte Antworten mit Quellennachweis.',
answer_en: 'Our RAG engine (Retrieval Augmented Generation) includes over 25,000 indexed original documents and over 25,000 extracted controls — GDPR, AI Act, CRA, NIS2, Machinery Regulation and 110 laws and regulations across 10 industries. For every compliance analysis, the relevant paragraphs and controls are automatically retrieved. AI agents operate as specialized services and deliver legally grounded answers with source references.',
answer_de: 'Unsere RAG-Engine (Retrieval Augmented Generation) umfasst über 25 Tausend indexierte Originaldokumente und über 25.000 extrahierte Controls — DSGVO, AI Act, CRA, NIS2, Maschinenverordnung und 380+ Gesetze und Regularien für 10 Branchen. Bei jeder Compliance-Analyse werden die relevanten Paragraphen und Controls automatisch herangezogen. KI-Agenten arbeiten als spezialisierte Services und liefern rechtlich fundierte Antworten mit Quellennachweis.',
answer_en: 'Our RAG engine (Retrieval Augmented Generation) includes over 25,000 indexed original documents and over 25,000 extracted controls — GDPR, AI Act, CRA, NIS2, Machinery Regulation and 380+ laws and regulations across 10 industries. For every compliance analysis, the relevant paragraphs and controls are automatically retrieved. AI agents operate as specialized services and deliver legally grounded answers with source references.',
goto_slide: 'product',
priority: 7,
},

View File

@@ -19,16 +19,16 @@ export const SLIDE_ORDER: SlideId[] = [
'cap-table',
'customer-savings',
'ai-qa',
'annex-architecture',
'annex-regulatory',
'annex-engineering',
'annex-aipipeline',
'annex-sdk-demo',
'annex-strategy',
'annex-finanzplan',
'annex-assumptions',
'annex-glossary',
'annex-regulatory',
'annex-architecture',
'annex-engineering',
'annex-aipipeline',
'annex-sdk-demo',
'risks',
'annex-glossary',
'legal-disclaimer',
]

View File

@@ -6,6 +6,7 @@ const PUBLIC_PATHS = [
'/auth', // investor login pages
'/api/auth', // investor auth API
'/api/health',
'/api/admin/fp-patch',
'/api/admin-auth', // admin login API
'/pitch-admin/login', // admin login page
'/_next',