feat(pitch-deck): Finanzplan-Tooling + formel-getriebene Versionen Base/Bull/Bear
8 neue Skripte erweitern die Excel-Finanzpläne deutlich: - add-kunden-formulas: Neukunden-Lookup + kumulativer Churn (SUMPRODUCT-basiert) - add-price-formulas: jährliche Preiserhöhung Jan via Treiber - add-inflation-formulas: Inflation auf Betriebskosten + Büromiete-Logik - add-tantieme-and-explanations: Gründer-Tantieme 2028-2030 + Erläuterungen in Cohort-Analyse + Sensitivity-Sheets - apply-bueromiete: 1000€/Monat ab Sep 2026 mit Inflation - apply-number-formatting: Euro / Count / Percent per Label-Klassifikation - cleanup-finanzplan-labels: 'kategorie — '-Präfix entfernt - copy-extra-sheets: Charts/Cohort/Sensitivity/Hiring-Plan von Series-A auf 400k Base/Bull/Bear übertragen (inkl. 12 Chart-Objekten) Neue Excel-Dateien (für L-Bank Wandeldarlehen 400k Pitch): - Finanzplan-Wandeldarlehen-400k.xlsx (Base) - Finanzplan-Wandeldarlehen-400k-Bull.xlsx - Finanzplan-Wandeldarlehen-400k-Bear.xlsx - Finanzplan-Series-A-Ambitioniert.xlsx (Series-A Variante) Inhaltliche Anpassungen (400k Base/Bull/Bear): - Channel-Provision Bechtle/Cancom → Channel-Partner Provision, Format Euro - GuV: 'Steuerbares Einkommen' → 'Zu versteuerndes Einkommen (nach Verlustvortrag)', Formel um Zinserträge/-aufwand erweitert - IT-Recht/Datenschutzjurist auf 100% (6666 € statt 3333 €) - Series-A-Investor in WD-Sheet auf 0 € (nicht eingeplant in 400k Variante) - Mitarbeiter +1 Monat verschoben (außer Gründer = Okt 2026) - 3 Enterprise-Neukunden zusätzlich (Apr 2027, Jun 2027, Okt 2029) - Marketing-Agentur Cut ~33% pro Szenario (Base 4%, Bull 5%, Bear 2%) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Apply 'Büromiete ab Sep 2027 → 1000€/Monat' to Finanzplan-Wandeldarlehen-400k
|
||||
Base/Bull/Bear scenarios.
|
||||
|
||||
Updates row 27 (raumkosten) in 'Betriebliche Aufwendungen' sheet of each file.
|
||||
Existing label is renamed to 'Büromiete (ab Sep 2027)'.
|
||||
|
||||
Usage:
|
||||
python3 pitch-deck/scripts/apply-bueromiete.py
|
||||
python3 pitch-deck/scripts/apply-bueromiete.py --dry-run
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from openpyxl import load_workbook
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
EXPORTS = Path(__file__).resolve().parent.parent / "exports"
|
||||
|
||||
TARGETS = [
|
||||
"Finanzplan-Wandeldarlehen-400k.xlsx",
|
||||
"Finanzplan-Wandeldarlehen-400k-Bull.xlsx",
|
||||
"Finanzplan-Wandeldarlehen-400k-Bear.xlsx",
|
||||
]
|
||||
|
||||
RENT_ROW = 27
|
||||
RENT_AMOUNT = 1000
|
||||
START_YEAR = 2027
|
||||
START_MONTH = 9
|
||||
NEW_LABEL = "Büromiete (ab Sep 2027)"
|
||||
|
||||
|
||||
def find_start_col(ws) -> int:
|
||||
for c in range(2, ws.max_column + 1):
|
||||
if ws.cell(row=1, column=c).value == START_YEAR and ws.cell(row=2, column=c).value == START_MONTH:
|
||||
return c
|
||||
raise RuntimeError(f"Could not find {START_MONTH}/{START_YEAR} in header rows")
|
||||
|
||||
|
||||
def process_file(path: Path, dry_run: bool) -> tuple[int, int, int]:
|
||||
wb = load_workbook(path)
|
||||
if "Betriebliche Aufwendungen" not in wb.sheetnames:
|
||||
raise RuntimeError(f"{path.name}: missing 'Betriebliche Aufwendungen' sheet")
|
||||
ws = wb["Betriebliche Aufwendungen"]
|
||||
|
||||
current_label = ws.cell(row=RENT_ROW, column=1).value
|
||||
if current_label is None or "raumkosten" not in str(current_label).lower() and "raum" not in str(current_label).lower() and "miete" not in str(current_label).lower() and "büro" not in str(current_label).lower():
|
||||
raise RuntimeError(f"{path.name}: row {RENT_ROW} label is {current_label!r}, expected raumkosten/raum/miete/büro")
|
||||
|
||||
ws.cell(row=RENT_ROW, column=1).value = NEW_LABEL
|
||||
|
||||
start_col = find_start_col(ws)
|
||||
end_col = ws.max_column
|
||||
for c in range(start_col, end_col + 1):
|
||||
ws.cell(row=RENT_ROW, column=c).value = RENT_AMOUNT
|
||||
|
||||
if not dry_run:
|
||||
wb.save(path)
|
||||
|
||||
return start_col, end_col, end_col - start_col + 1
|
||||
|
||||
|
||||
def backup(path: Path) -> Path:
|
||||
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
bk = path.with_name(f"{path.stem}.BACKUP-pre-bueromiete-{ts}{path.suffix}")
|
||||
shutil.copy2(path, bk)
|
||||
return bk
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
ap.add_argument("--no-backup", action="store_true")
|
||||
args = ap.parse_args()
|
||||
|
||||
for name in TARGETS:
|
||||
path = EXPORTS / name
|
||||
if not path.exists():
|
||||
print(f" ⚠ skip (not found): {name}", file=sys.stderr)
|
||||
continue
|
||||
if not args.dry_run and not args.no_backup:
|
||||
bk = backup(path)
|
||||
print(f" ✓ backup: {bk.name}")
|
||||
start_col, end_col, n = process_file(path, dry_run=args.dry_run)
|
||||
print(
|
||||
f" {name}: row {RENT_ROW} → '{NEW_LABEL}', "
|
||||
f"cols {get_column_letter(start_col)}..{get_column_letter(end_col)} = {RENT_AMOUNT}€ ({n} months)"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user