feat: Finanzplan Phase 1-4 — DB + Engine + API + Spreadsheet-UI

Phase 1: DB-Schema (12 fp_* Tabellen) + Excel-Import (332 Zeilen importiert)
Phase 2: Compute Engine (Personal, Invest, Umsatz, Material, Betrieblich, Liquiditaet, GuV)
Phase 3: API (/api/finanzplan/ — GET sheets, PUT cells, POST compute)
Phase 4: Spreadsheet-UI (FinanzplanSlide als Annex mit Tab-Leiste, editierbarem Grid, Jahres-Navigation)

Zusaetzlich:
- Gruendungsdatum verschoben: Feb→Aug 2026 (DB + Personalkosten)
- Neue Preisstaffel: Startup/<10 MA ab 3.600 EUR/Jahr (14-Tage-Test, Kreditkarte)
- Competition-Slide: Pricing-Tiers aktualisiert

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-26 19:26:46 +01:00
parent f514667ef9
commit a58cd16f01
16 changed files with 4589 additions and 5 deletions

View File

@@ -0,0 +1,246 @@
-- ============================================================================
-- BreakPilot ComplAI — Finanzplan Database Schema
-- Mirrors Excel: "Breakpilot ComplAI Finanzplan.xlsm" (10 Reiter)
-- Monthly granularity: Jan 2026 Dec 2030 (60 months)
-- Values stored as JSONB: {"m1": ..., "m2": ..., "m60": ...}
-- ============================================================================
-- Scenarios (extends existing pitch_fm_scenarios)
CREATE TABLE IF NOT EXISTS fp_scenarios (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL DEFAULT 'Base Case',
description TEXT,
is_default BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Insert default scenario
INSERT INTO fp_scenarios (name, description, is_default)
VALUES ('Base Case', 'Basisdaten aus Excel-Import', true)
ON CONFLICT DO NOTHING;
-- ============================================================================
-- KUNDEN (6 Segmente × ~20 Zeilen = ~120 Datenzeilen)
-- Each segment has: base customer count (editable) + module percentages
-- Formulas: module_count = ROUNDDOWN(base_count * percentage)
-- ============================================================================
CREATE TABLE fp_kunden (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
segment_name TEXT NOT NULL, -- 'Care (Privat)', 'Horse (Händler)', etc.
segment_index INT NOT NULL, -- 1-6
row_label TEXT NOT NULL, -- 'Modul 1', 'Modul 2', ...
row_index INT NOT NULL, -- position within segment
percentage NUMERIC(5,3), -- multiplier (e.g. 0.9, 0.25)
formula_type TEXT, -- 'literal', 'roundup_pct', 'rounddown_pct', 'cumulative', null
is_editable BOOLEAN DEFAULT false, -- true for base inputs (Modul 1 per segment)
values JSONB NOT NULL DEFAULT '{}', -- {m1: 0, m2: 0, ... m60: 0}
excel_row INT, -- original Excel row number
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Summary rows (aggregated across all 6 segments)
CREATE TABLE fp_kunden_summary (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
row_label TEXT NOT NULL, -- 'Modul 1', 'Modul 2', ...
row_index INT NOT NULL,
values JSONB NOT NULL DEFAULT '{}', -- computed: sum of 6 segments
excel_row INT,
sort_order INT NOT NULL
);
-- ============================================================================
-- UMSATZERLOESE (Revenue = Quantity × Price)
-- Section 1 (rows 3-23): Computed revenue per module
-- Section 2 (rows 27-46): Quantity (from Kunden)
-- Section 3 (rows 49-73): Prices (editable VK excl. MwSt.)
-- ============================================================================
CREATE TABLE fp_umsatzerloese (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
section TEXT NOT NULL, -- 'revenue', 'quantity', 'price'
row_label TEXT NOT NULL, -- 'Modul 1', 'Modul 2', ...
row_index INT NOT NULL,
is_editable BOOLEAN DEFAULT false, -- only prices are editable
values JSONB NOT NULL DEFAULT '{}',
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- MATERIALAUFWAND (sehr einfach: nur Mac Mini + Mac Studio)
-- Cost = Quantity × Unit Cost (EK)
-- Mac Mini EK: 3.200 EUR, VK: 4.800 EUR (50% Aufschlag)
-- Mac Studio EK: 13.000 EUR, VK: 19.500 EUR (50% Aufschlag)
-- ============================================================================
CREATE TABLE fp_materialaufwand (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
section TEXT NOT NULL, -- 'cost', 'quantity', 'unit_cost'
row_label TEXT NOT NULL,
row_index INT NOT NULL,
is_editable BOOLEAN DEFAULT false, -- only unit costs are editable
values JSONB NOT NULL DEFAULT '{}',
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- PERSONALKOSTEN (20 Positionen)
-- Structured: Name, Position, Start, End, Brutto, Raise%
-- Computed: monthly salary × AG-Sozialversicherung
-- ============================================================================
CREATE TABLE fp_personalkosten (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
person_name TEXT NOT NULL,
person_nr TEXT, -- '001', '002', ...
position TEXT, -- 'GF', 'Vertrieb', 'Entwicklung', ...
start_date DATE,
end_date DATE, -- null = permanent
brutto_monthly NUMERIC(10,2), -- Bruttogehalt/Monat
annual_raise_pct NUMERIC(5,2) DEFAULT 3.0,
ag_sozial_pct NUMERIC(5,2) DEFAULT 20.425, -- AG-Anteil Sozialversicherung
is_editable BOOLEAN DEFAULT true,
-- Computed monthly values
values_brutto JSONB NOT NULL DEFAULT '{}', -- monthly brutto
values_sozial JSONB NOT NULL DEFAULT '{}', -- monthly AG-Sozial
values_total JSONB NOT NULL DEFAULT '{}', -- brutto + sozial
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- BETRIEBLICHE AUFWENDUNGEN (~40 Kostenposten)
-- Most are fixed monthly values (editable)
-- Some are computed (Summen, Abschreibungen)
-- ============================================================================
CREATE TABLE fp_betriebliche_aufwendungen (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
category TEXT NOT NULL, -- 'raumkosten', 'versicherungen', 'marketing', etc.
row_label TEXT NOT NULL,
row_index INT NOT NULL,
is_editable BOOLEAN DEFAULT true,
is_sum_row BOOLEAN DEFAULT false, -- true for category subtotals
formula_desc TEXT, -- e.g. 'SUM(rows 9-18)', '=Personalkosten!Y4'
values JSONB NOT NULL DEFAULT '{}',
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- INVESTITIONEN (Anlagegüter mit AfA)
-- Each item: name, amount, purchase date, useful life, depreciation
-- ============================================================================
CREATE TABLE fp_investitionen (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
item_name TEXT NOT NULL,
category TEXT, -- 'gwg', 'ausstattung', etc.
purchase_amount NUMERIC(12,2) NOT NULL,
purchase_date DATE,
afa_years INT, -- useful life in years
afa_end_date DATE, -- computed end date
is_editable BOOLEAN DEFAULT true,
-- Computed: monthly investment amount (in purchase month) and depreciation
values_invest JSONB NOT NULL DEFAULT '{}', -- investment amount per month
values_afa JSONB NOT NULL DEFAULT '{}', -- monthly depreciation
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- SONST. BETRIEBLICHE ERTRAEGE (6 Kategorien × 3 Zeilen)
-- ============================================================================
CREATE TABLE fp_sonst_ertraege (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
category TEXT NOT NULL, -- '1-Interne Kostenstellen', '2-Entwicklung National', etc.
row_label TEXT,
row_index INT NOT NULL,
is_editable BOOLEAN DEFAULT true,
is_sum_row BOOLEAN DEFAULT false,
values JSONB NOT NULL DEFAULT '{}',
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- LIQUIDITAET (computed from all above)
-- ============================================================================
CREATE TABLE fp_liquiditaet (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
row_label TEXT NOT NULL,
row_type TEXT NOT NULL, -- 'einzahlung', 'auszahlung', 'ueberschuss', 'kontostand'
is_editable BOOLEAN DEFAULT false, -- only Eigenkapital, Fremdkapital, Entnahmen editable
formula_desc TEXT,
values JSONB NOT NULL DEFAULT '{}',
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- GUV JAHRESABSCHLUSS (annual summary, 5 years)
-- ============================================================================
CREATE TABLE fp_guv (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
row_label TEXT NOT NULL,
row_index INT NOT NULL,
is_sum_row BOOLEAN DEFAULT false,
formula_desc TEXT,
values JSONB NOT NULL DEFAULT '{}', -- {y2026: ..., y2027: ..., y2030: ...}
excel_row INT,
sort_order INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================================
-- CELL OVERRIDES (for scenario-specific manual edits)
-- ============================================================================
CREATE TABLE fp_cell_overrides (
id SERIAL PRIMARY KEY,
scenario_id UUID REFERENCES fp_scenarios(id) ON DELETE CASCADE,
sheet_name TEXT NOT NULL, -- 'kunden', 'personalkosten', etc.
row_id INT NOT NULL, -- references the id in the sheet table
month_key TEXT NOT NULL, -- 'm1', 'm2', ... 'm60' or 'y2026', etc.
override_value NUMERIC,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(scenario_id, sheet_name, row_id, month_key)
);
-- ============================================================================
-- INDEXES
-- ============================================================================
CREATE INDEX idx_fp_kunden_scenario ON fp_kunden(scenario_id);
CREATE INDEX idx_fp_kunden_summary_scenario ON fp_kunden_summary(scenario_id);
CREATE INDEX idx_fp_umsatz_scenario ON fp_umsatzerloese(scenario_id);
CREATE INDEX idx_fp_material_scenario ON fp_materialaufwand(scenario_id);
CREATE INDEX idx_fp_personal_scenario ON fp_personalkosten(scenario_id);
CREATE INDEX idx_fp_betrieb_scenario ON fp_betriebliche_aufwendungen(scenario_id);
CREATE INDEX idx_fp_invest_scenario ON fp_investitionen(scenario_id);
CREATE INDEX idx_fp_sonst_scenario ON fp_sonst_ertraege(scenario_id);
CREATE INDEX idx_fp_liquid_scenario ON fp_liquiditaet(scenario_id);
CREATE INDEX idx_fp_guv_scenario ON fp_guv(scenario_id);
CREATE INDEX idx_fp_overrides_lookup ON fp_cell_overrides(scenario_id, sheet_name, row_id);