Automation
Vierzehn CSV-Exporte zu einem Bericht, mit Claude Code
Vierzehn Monatsexporte, dreimal umgebautes Format, ein Nachmittag Handarbeit. Wir machen einen Aufruf daraus — und zeigen die Fassung, die ohne Fehlermeldung eine um 26 Prozent zu hohe Zahl liefert.
- Stand

Einmal im Quartal zieht jemand die Umsatzlisten aus dem System, öffnet vierzehn CSV-Dateien und klebt sie in eine Tabelle. Das dauert einen Nachmittag, und am Ende steht eine Zahl, die niemand nachrechnet. Wir machen daraus einen Aufruf, der zehn Sekunden braucht — und zeigen dabei die Stelle, an der die erste Fassung eine Zahl liefert, die um sechsundzwanzig Prozent zu hoch ist.
Was du danach in der Hand hast:
- ein Skript, das die Exporte zusammenführt, egal wie oft das System sie zwischendurch umgebaut hat
- eine Prüfung, die falsche Zeilen findet, bevor sie in die Summe gehen
- die Formulierung, mit der man so eine Aufgabe übergibt
Voraussetzungen: Claude Code auf dem eigenen Rechner und ein Ordner mit den Dateien. Programmieren musst du nicht.
Was Claude Code eigentlich ist
Fast alle halten es für ein Werkzeug für Entwickler. Das ist der Grund, warum es in Betrieben ungenutzt herumliegt. Tatsächlich ist es etwas Einfacheres: ein Modell, das in einem Ordner sitzt, Dateien lesen und schreiben darf und eine Kommandozeile bedienen kann. Was daraus wird, hängt daran, was im Ordner liegt. Liegen dort CSV-Exporte, ist es ein Werkzeug für die Buchhaltung.
Der Unterschied zu einem Chatfenster ist genau dieser Zugriff. Du lädst keine Datei hoch und bekommst Text zurück. Du zeigst auf einen Ordner und sagst, was am Ende dastehen soll. Der Rest passiert dort, wo die Dateien liegen.
Die Aufgabe, und warum sie unangenehm ist
Vierzehn Monatsexporte, und das System wurde in der Zeit dreimal angefasst. Zwei Dateien nebeneinander sehen so aus:
umsatz_2025-08.csv
Kunde;Datum;Betrag
Möller & Sohn KG;19.08.2025;558,40
Westfalen Immobilien AG;17.08.2025;413,16
umsatz_2025-10.csv
Export vom 28.10.2025 - Umsatzliste
Kunde;Datum;Betrag
Sanitär Özdemir e.K.;06.10.2025;3.460,83
Bauer Elektrotechnik GmbH;19.10.2025;594,28Fünf Unterschiede stecken in den vierzehn Dateien, und keiner davon ist dokumentiert: die Kundenspalte heißt mal Kunde, mal Kundenname, mal Kd-Name. Die Beträge stehen mal als 1.234,56 und mal als 1234,56. Drei Dateien haben eine Zeile über der Kopfzeile. Drei andere haben unten eine Summenzeile. Und eine ist nicht in UTF-8 gespeichert.
Der erste Versuch, der abstürzt
Naheliegend, kurz, und nach zwei Sekunden vorbei:
import csv, glob
gesamt = 0.0
for pfad in sorted(glob.glob("testdaten/*.csv")):
with open(pfad, encoding="utf-8") as f:
for satz in csv.DictReader(f, delimiter=";"):
gesamt += float(satz["Betrag"].replace(".", "").replace(",", "."))
print("Gesamtumsatz %.2f EUR" % gesamt)Traceback (most recent call last):
File ".../bericht_v1.py", line 10, in <module>
gesamt += float(satz["Betrag"].replace(".", "").replace(",", "."))
~~~~^^^^^^^^^^
KeyError: 'Betrag'Das ist der angenehme Fehler. Er ist laut, er zeigt auf die Zeile, und er kostet nichts. Die Präambel hat dafür gesorgt, dass die erste Datenzeile als Kopfzeile gelesen wurde.
Der zweite Versuch, der eine Zahl liefert
Die Präambel überspringen, die drei Spaltennamen abfangen, beide Zahlenformate erkennen. Danach läuft es durch:
83 Zeilen, Gesamtumsatz 210715.32 EURDas ist der gefährliche Zustand. Kein Fehler, eine plausible Zahl, und wer jetzt aufhört, hat einen Bericht, der falsch ist. Genau hier trennt sich brauchbare Automatisierung von schneller.
Die Prüfung, die den Fehler findet
Statt die Summe anzusehen, zählen wir, welche Kundennamen überhaupt vorkommen. Acht Kunden hat der Betrieb. Das ist pruefe_bericht.py aus dem Paket; die Markierungen rechts setzt das Skript selbst, wenn ein Name wie eine Summenzeile aussieht, ein Ersatzzeichen enthält oder sich von einem häufigeren Namen nur in einem Zeichen unterscheidet:
12 Westfalen Immobilien AG
11 Lindgruber Bau GmbH
10 Möller & Sohn KG
10 Bauer Elektrotechnik GmbH
10 Sanitär Özdemir e.K.
10 Kern Hausverwaltung GmbH
9 Dr. Ruhnau Steuerberatung
7 Praxis am Domplatz
3 Gesamt <- Summenzeile, kein Kunde
1 M�ller & Sohn KG <- Ersatzzeichen: Datei nicht in utf-8; sieht aus wie 'Möller & Sohn KG'Zwei Zeilen zu viel, und beide sind ein Fehler. „Gesamt“ ist kein Kunde, sondern die Summenzeile aus drei Dateien — die Umsätze dieser drei Monate stehen dadurch doppelt in der Rechnung. Und der achte Kunde taucht zweimal auf, weil in der einen Datei in latin-1 das Umlaut-ö zerfällt; sein Jahresumsatz war auf zwei Zeilen verteilt.
Was am Ende dasteht
Umsatz je Monat
2025-08 6.772,55 EUR
2025-09 10.261,06 EUR
2025-10 10.323,02 EUR
2025-11 15.221,15 EUR
2025-12 8.647,60 EUR
2026-01 24.437,24 EUR
2026-02 9.063,31 EUR
2026-03 12.691,81 EUR
2026-04 15.188,43 EUR
2026-05 5.088,42 EUR
2026-06 5.866,37 EUR
2026-07 7.325,53 EUR
2026-08 10.169,51 EUR
2026-09 26.082,14 EUR
Umsatz je Kunde
Möller & Sohn KG 25.254,51 EUR
Westfalen Immobilien AG 25.180,25 EUR
Sanitär Özdemir e.K. 23.740,87 EUR
Kern Hausverwaltung GmbH 23.587,72 EUR
Lindgruber Bau GmbH 22.335,15 EUR
Bauer Elektrotechnik GmbH 19.814,70 EUR
Dr. Ruhnau Steuerberatung 16.409,46 EUR
Praxis am Domplatz 10.815,48 EUR
80 Buchungen, Gesamt 167.138,14 EUR
(3 Summenzeile(n) übersprungen)167.138,14 statt 210.715,32 Euro. Die zweite Fassung lag um 43.577,18 Euro daneben, das sind 26,1 Prozent. Das ist genau die Summe der drei Summenzeilen; der zerfallene Umlaut hat die Gesamtsumme nicht verändert, nur den Kunden auf zwei Zeilen verteilt. Eine Zahl, die in einer Quartalsbesprechung niemandem aufgefallen wäre.
Der Unterschied zwischen der falschen und der richtigen Fassung sind zwei Stellen. Die eine ist ein Filter vor der Summe:
SUMMENWOERTER = {"gesamt", "summe", "total", "gesamtsumme"}
def ist_buchung(satz):
return satz["kunde"].lower() not in SUMMENWOERTER and satz["datum"] != ""Die andere ist die Kodierung: erst utf-8 versuchen, und wenn die Datei sich weigert, latin-1 nehmen, statt die kaputten Zeichen still zu ersetzen. Beides sind drei Zeilen. Gefunden hat sie nicht das Skript, sondern die Liste mit den zehn Namen.
Wie man so eine Aufgabe übergibt
Der eigentliche Handgriff ist nicht technisch, sondern eine Frage der Formulierung. Drei Dinge gehören hinein, und eines ausdrücklich nicht:
- Wo die Dateien liegen. Ein Ordner, kein Anhang.
- Was am Ende dastehen soll. „Umsatz je Monat und je Kunde, als Text auf dem Bildschirm“ ist eine brauchbare Beschreibung. „Werte den Ordner aus“ ist keine.
- Wie geprüft wird. Der Satz, der den Unterschied macht, lautet sinngemäß: bevor du summierst, zeig mir alle vorkommenden Kundennamen mit Anzahl.
- Nicht hinein gehört, wie das Skript aussehen soll. Sobald man anfängt, Bibliotheken und Schleifen vorzugeben, schreibt man das Programm selbst, nur langsamer.
Beim Umbau gilt dasselbe: Man beschreibt, was am Ergebnis anders sein soll, nicht welche Zeile zu ändern ist.
Wann sich das nicht lohnt
- Bei drei Dateien, einmalig. Dann ist die Tabelle schneller, und das ist keine Schande.
- Wenn dasselbe jeden Monat automatisch laufen soll. Dann gehört es in einen Ablauf mit festem Zeitplan, nicht in ein Gespräch — dafür ist n8n da, und das Skript von hier ist der Baustein darin.
- Wenn die Dateien nicht das Haus verlassen dürfen. Dann entweder ein lokales Modell oder die Einstellung prüfen, wohin Dateiinhalte übertragen werden. Wer mit Personal- oder Mandantendaten arbeitet, klärt das vorher, nicht nachher.
Zum Nachbauen
Das Paket zu diesem Beitrag enthält die Testdaten, den Auftrag und alle drei Fassungen. Entpacken, dann:
- testdaten/: die vierzehn Exporte, mit denen die Ausgaben oben entstanden sind. erzeuge_exporte.py erzeugt sie neu, mit --seed auch andere Zahlen bei gleicher Struktur.
- AUFTRAG.md: die Aufgabenübergabe an Claude Code mit Ziel, Regeln, der Prüfung vor dem Summieren und den Verboten. Das ist der Text, den man als erste Nachricht gibt.
- bericht_v1.py und bericht_falsch.py: der Absturz und der gefährliche Zustand, zum Nachvollziehen.
- pruefe_bericht.py: die Liste der Kundennamen mit Anzahl und Markierungen. Beendet sich mit Fehlercode, wenn etwas nicht in die Summe gehört; so lässt sie sich vor bericht.py in einen Ablauf hängen.
- bericht.py: die richtige Fassung. Für eigene Exporte den Ordner als Argument angeben.
Vierzehn CSV-Exporte zu einem Bericht: Testdaten, Auftrag, drei Fassungen und die Prüfung
HerunterladenStand dieses Artikels
Gebaut und geprüft am 11. September 2026 mit Python 3.11.15; am 19. September 2026 mit Python 3.12.2 neu gelaufen, und alle Ausgaben in diesem Beitrag stammen aus diesem zweiten Lauf. Die vierzehn Exportdateien sind erzeugt, nicht echt; die Unregelmäßigkeiten darin sind aus echten Exporten übernommen, Kunden und Beträge sind erfunden. Der Startwert des Zufallsgenerators ist so gewählt, dass die drei Summenzeilen die Gesamtsumme um rund 26 Prozent verfälschen, wie im ersten Lauf; mit einem anderen Startwert ändert sich der Prozentsatz, die Fehler bleiben dieselben. Gegen echte Exporte aus einem Fakturasystem ist das Skript in dieser Fassung nicht geprüft; Summenzeilen, die anders heißen als Gesamt oder Summe, und Kodierungen jenseits von utf-8 und latin-1 fallen erst in der Prüfliste auf.
Häufige Fragen
Warum steht die falsche Fassung im Paket?
Weil der Fehler das Lehrstück ist. bericht_falsch.py läuft ohne Warnung durch und liefert eine Zahl, die um ein Viertel zu hoch ist; wer das einmal mit eigenen Augen gesehen hat, sieht sich die Summe nie wieder zuerst an. Für den Bericht nehmt ihr bericht.py.
Muss ich programmieren können, um Claude Code so zu nutzen?
Nein. Du musst beschreiben können, was am Ende dastehen soll, und du musst das Ergebnis prüfen können. Das Zweite ist der schwierigere Teil und der, den dir niemand abnimmt.
Woran merke ich, dass das Ergebnis falsch ist?
Nicht an der Summe, die sieht immer plausibel aus. Lass dir als Erstes die Kategorien mit Anzahl ausgeben — Kunden, Konten, Belegarten. Zeilen, die dort nicht hingehören, fallen sofort auf.
Was ist der Unterschied zu einem Chatfenster mit Dateiupload?
Der Zugriff auf den Ordner. Claude Code liest und schreibt Dateien dort, wo sie liegen, und kann Befehle ausführen. Bei vierzehn Dateien ist das der Unterschied zwischen einem Aufruf und vierzehn Uploads.

Belege an DATEV übergeben
Der Zugang ist niedriger, als sein Ruf vermuten lässt — eine Beraternummer braucht es nicht, die Sandbox kommt mit dem Abonnement. Und elf Stunden Token-Gültigkeit entscheiden über den ganzen Aufbau.

Ein Agent, der eure Ablage kennt
Das meiste daran ist Suche, nicht Modell. Eine Volltextsuche über die eigene Ablage mit Fundstelle und Begründung — und der Satz, der schlimmer ist als eine leere Trefferliste.

Fristen aus einem Vertragsstapel
Ein Skript liest sechs Vertragsklauseln und rechnet zurück, bis wann eine Kündigung zugehen müsste. Drei Verträge rechnet es, drei nicht — und die drei, die es liegen lässt, sind der interessantere Teil.