-- ============================================================================ -- 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);