---
name: datev-buchhalter
description: Automatisierte Buchhaltung in DATEV Unternehmen Online. Bankabgleich, Belegzuordnung, Buchungsvorschlaege, fehlende Belege per Gmail suchen.
argument-hint: "[leer fuer Menue] | scan | rechnungen-suchen | buchen | geschaeftspartner | duplikate | status | report | setup"
---

## Session-Init (IMMER zuerst ausfuehren!)

1. `config/mandant.json` lesen
2. Variablen setzen:
   - `CONSULTANT_ID` = mandant.consultant_id
   - `CLIENT_ID` = mandant.client_id
   - `MANDANT_NAME` = mandant.name
   - `DATEV_URL` = mandant.url (oder `https://webapps.datev.de/duo-start/{CONSULTANT_ID}-{CLIENT_ID}`)
   - `BANK_NAME` = bankkonten.aktiv.hauptkonto.name (falls vorhanden)
   - `IBAN` = bankkonten.aktiv.hauptkonto.iban (falls vorhanden)
3. Falls `config/mandant.json` NICHT existiert → `/datev-buchhalter setup` starten

## Previous Run Context
!`cat .claude/skills/datev-buchhalter/state.md 2>/dev/null || echo "First run -- no history"`

---

## PFLICHTLEKTUERE (VOR JEDER Aktion lesen!)

Vor jeder Session diese Dateien konsultieren. Subagents bekommen die relevanten Dateien im Prompt.

| Prio | Datei | Inhalt |
|------|-------|--------|
| 1 | `state.md` | Bekannte Probleme + Loesungen (36+ Eintraege) |
| 2 | `docs/workflow-guide.md` | 5-Phasen-Workflow (Bestandsaufnahme → QA) |
| 3 | `docs/gobd-checkliste.md` | GoBD-Regeln, §14 UStG Pflichtangaben |
| 4 | `docs/agent-team.md` | Agent-Rollen, Validierungskette, Eskalation |
| 5 | `docs/datev-navigation.md` | DATEV UI-Module, iframe, dijit, SlickGrid |
| 6 | `docs/browser-automation.md` | Playwright MCP Setup + Fallback CDP |
| 7 | `config/mandant.json` | Mandant-Daten (Session-Init Variablen) |
| 8 | `config/rules.json` | Validierungsregeln, Steuersaetze |
| 9 | `config/konten-mapping.json` | SKR03 Kontozuordnungen (27 Regeln) |
| 10 | `config/geschaeftspartner.json` | 140 Partner mit Aliases |

---

## Architektur: Pipeline mit Subagents

Jeder Scan-Step laeuft als eigener Subagent mit frischem Context (eigenes 1M-Token-Budget).
Die Hauptkonversation bleibt schlank (~50KB) und orchestriert nur.
`logs/scan-data-YYYY-MM-DD.json` ist das Bindeglied zwischen Steps.

```
Hauptkonversation (Orchestrator)
  │
  ├─ Subagent 1: DATEV Scan (Playwright) → schreibt JSON
  ├─ Subagent 2: Gmail Scan (Gmail MCP)  → ergaenzt JSON
  └─ Step 3: node scripts/generate-report.js JSON → HTML Report
```

**WICHTIG:** Subagents SEQUENTIELL starten (nicht parallel), damit jeder das JSON des Vorgaengers lesen kann.

---

## Menue-System (bei Aufruf OHNE Argument)

### Schritt 1: Quick-Scan (nur lokale Dateien, KEIN Browser)
Lies diese Dateien und extrahiere die Zahlen:

| Datei | Extrahieren |
|-------|-------------|
| `state.md` | Letzter Run (Datum+Ergebnis), Gesamtstatus, offene Items |
| `config/mandant.json` | Mandant-Name, IDs |
| `config/geschaeftspartner.json` | Partner-Anzahl (Laenge des Arrays) |
| `logs/scan-data-*.json` | Letzter Scan (wenn vorhanden): offene Txn, fehlende Belege |

### Schritt 2: Status-Header + AskUserQuestion

Kurzen Status im Chat:
```
DATEV Buchhalter — {MANDANT_NAME} ({CONSULTANT_ID}-{CLIENT_ID})
Letzter Run: {last_run_date} {last_run_status}
2026: {offen} offen | Belege: {fehlen} fehlen | Partner: {count}
```

Dann AskUserQuestion:
- "Scan + Report — Bestandsaufnahme DATEV + Gmail [{offen} offen]"
- "Rechnungen suchen — Gmail scannen + herunterladen [{fehlen} fehlen]"
- "Buchen — Belege zuordnen [{bereit} bereit]"
- "Geschaeftspartner — Stammdaten pflegen → OCR-Training + Auto-Zuordnung [{count} Partner, {nicht_angelegt} offen]"
- "Duplikate — Doppelte Belege finden + loeschen"
- "Report — Letzten Scan als Report neu generieren"
- "Status — Letzte Runs anzeigen"

**Hinweis im Chat vor dem Popup:**
> Tipp: Vor dem Hochladen neuer Belege immer erst "Scan + Report" oder "Duplikate" laufen lassen,
> um doppelte Uploads zu vermeiden. DATEV erkennt Duplikate NICHT automatisch.

### Schritt 3: Modus starten

| Auswahl | Modus | Section |
|---------|-------|---------|
| "Scan + Report" | scan | → scan |
| "Rechnungen suchen" | rechnungen-suchen | → rechnungen-suchen |
| "Buchen" | buchen | → buchen |
| "Geschaeftspartner" | geschaeftspartner | → geschaeftspartner |
| "Duplikate" | duplikate | → duplikate |
| "Report" | report | → report |
| "Status" | status | → status |
| "Setup" | setup | → setup |

**Shortcut:** `/datev-buchhalter scan` ueberspringt Menue.

### Schritt 4: Follow-up nach jedem Modus

| Modus | Follow-up |
|-------|-----------|
| scan | "{n} Belege fehlen, {m} in Gmail. 1=Buchen, 0=Fertig" |
| rechnungen-suchen | "{n} RE gefunden, {m} heruntergeladen. 1=Buchen, 0=Fertig" |
| buchen | "{n} ausgefuehrt, {m} offen. 1=Weiter, 0=Fertig" |
| geschaeftspartner | "{n} Partner angelegt, {m} noch offen. 1=Weiter (naechster Batch), 2=Scan, 0=Fertig" |
| duplikate | "{n} Duplikate gefunden. 1=Loeschen, 0=Fertig" |
| report | Report geoeffnet. 0=Fertig |
| status | Kein Follow-up |

---

## Modi

### scan (Default — DATEV + Gmail + Report)

3-Step Pipeline mit Subagents:

**Step 1: DATEV Scan (Subagent)**
→ Siehe "Subagent 1: DATEV Scan" unten

**Step 2: Gmail Abgleich (Subagent)**
→ Siehe "Subagent 2: Gmail Scan" unten

**Step 3: Report generieren (Bash)**
```bash
node scripts/generate-report.js logs/scan-data-YYYY-MM-DD.json
open logs/report-YYYY-scan-YYYY-MM-DD.html
```

**Ohne Gmail:** `scan --nur-datev` ueberspringt Step 2.

**Zeitraum-Abfrage** (nach Modus-Auswahl):
- "Aktuelles Steuerjahr (2026)" — Default
- "Letztes Jahr (2025)"
- "Bestimmte Monate"
- "Alles (2024-2026)" — Hinweis: dauert laenger

**Anonymisierung:** `--anonymize` Flag an Report-Script. Standard: echte Daten (Kunden-Nutzung). Nur fuer Demos/Showcases anonymisieren.

**Agenten:** Buchhalter (Step 1) + Belegmanager (Step 2) + Compliance-Monitor (Report)

### rechnungen-suchen (Gmail scannen + herunterladen)

Intelligente Rechnungssuche: Erst DATEV-Bestand pruefen, dann nur Fehlendes in Gmail suchen.

**Schritt 1: Zeitraum-Abfrage (AskUserQuestion)**
- "Aktuelles Steuerjahr (2026)" — Default
- "Letztes Jahr (2025)"
- "Bestimmte Monate"
- "Alles (2024-2026)"

**Schritt 2: Modus-Abfrage (AskUserQuestion)**
- "Nur scannen — Gmail durchsuchen, Ergebnis als Report"
- "Scannen + herunterladen — Gmail durchsuchen + fehlende RE herunterladen"

**Schritt 3: DATEV-Bestand pruefen (Subagent oder letzter Scan)**
1. Letztes `logs/scan-data-*.json` laden (falls <24h alt → wiederverwenden)
2. Falls kein aktueller Scan: DATEV Scan-Subagent starten (→ scan Step 1)
3. Aus Scan-Daten extrahieren:
   - Welche Transaktionen haben bereits einen Beleg? → SKIP
   - Welche Transaktionen haben KEINEN Beleg? → Gmail durchsuchen
   - Welche Belege sind hochgeladen aber NICHT zugeordnet? → Warnung (evtl. Duplikat-Risiko)

**Schritt 4: Gmail Scan (Subagent 2 — Belegmanager)**
Nur fuer Transaktionen OHNE Beleg:
1. `config/geschaeftspartner.json` laden (Aliases fuer Suche)
2. Pro fehlender Transaktion: `gmail_search_messages`
   - Query: Absender-Name (aus geschaeftspartner.json aliases)
   - Zeitraum: Transaktionsdatum ±7 Tage
   - Suche nach: invoice, receipt, Rechnung, Quittung
3. Ergebnis pro Txn: gmail_match Objekt (found, message_id, subject, typ)
4. **WICHTIG:** Invoice bevorzugen vor Receipt (§14 UStG)
5. scan-data JSON ergaenzen und speichern

**Schritt 5: Ergebnis-Report**
Zusammenfassung im Chat:
```
Gmail-Scan: {zeitraum}
Transaktionen ohne Beleg: {n}
Davon in Gmail gefunden: {m}
  - {m_invoice} Invoices (Rechnungen)
  - {m_receipt} Receipts (Zahlungsbestaetigungen)
Nicht gefunden (Portal-Download noetig): {p}
```

**Schritt 6: Download (nur bei Modus "Scannen + herunterladen")**
1. Pro gefundener Gmail-Rechnung:
   - **Duplikat-Check:** Dateiname gegen DATEV-Beleg-Inventar pruefen
   - Falls bereits in DATEV vorhanden → SKIP + Warnung
   - Falls neu: Attachment herunterladen via `gmail_read_message`
2. Ordnerstruktur: `belege/downloaded/{YYYY-MM}/{dateiname}`
3. Invoice vs Receipt filtern:
   - "invoice" im Dateinamen → primaerer Beleg (herunterladen)
   - "receipt" im Dateinamen → nur als Backup (User fragen)
4. Log: `logs/gmail-scan-{datum}.json`

**Schritt 7: Follow-up (AskUserQuestion)**
- "Belege in DATEV hochladen — {m} RE bereit"
- "Report generieren — Ergebnis als HTML"
- "Fertig — Nichts weiter"

**SICHERHEITSREGELN:**
- VOR jedem Download: Pruefen ob Beleg schon in DATEV existiert (Dateiname + Betrag + Datum)
- NIEMALS automatisch in DATEV hochladen ohne User-Freigabe
- Bei mehreren Attachments (Invoice + Receipt): Nur Invoice herunterladen, Receipt als Option anbieten
- Portal-Only Belege (Zoom, Skool, etc.) koennen NICHT per Gmail gefunden werden → klar kommunizieren

**Agenten:** Belegmanager (Gmail) + optional Buchhalter (DATEV-Bestand)
**Browser:** NUR fuer DATEV-Bestand-Check (falls kein aktueller Scan)

### buchen (MIT USER-APPROVAL)
Buchungen vorbereiten und ausfuehren:
1. Buchungsvorschlaege generieren
2. Wirtschaftspruefer validiert jeden Vorschlag
3. User reviewt und gibt frei
4. Buchhalter fuehrt aus (Screenshot vor/nach)
5. Audit-Log schreiben

**Agenten:** Buchhalter + Wirtschaftspruefer
**Browser:** JA

### geschaeftspartner
Geschaeftspartner-Stammdaten in DATEV anlegen und pflegen.

**WARUM das wichtig ist:**
- **OCR-Training:** DATEV lernt PRO Lieferant. Einmal "Anthropic" korrigiert → kuenftige Anthropic-Rechnungen werden automatisch erkannt (Konto, Betrag, USt)
- **ASR (Automatisierungsservice Rechnungen):** Vergleicht Belege mit Buchungshistorie pro Geschaeftspartner. Ohne Partner = kein Lerneffekt
- **ASB (Automatisierungsservice Bank):** KI-Buchungsvorschlaege fuer Kontoumsaetze basieren auf Geschaeftspartner-Zuordnung
- **Auto-Zuordnung Bank↔Beleg:** Matching-Kriterien (Betrag ±5%, Rechnungsnr min 3-stellig, Zeitfenster 2 Monate) funktionieren NUR mit sauberen Partnernamen
- **DATEV-Zitat:** "Investieren Sie Zeit in die korrekte Anlage von Geschaeftspartnern, um die Trefferquoten der Automatismen zu maximieren."

**Schritt 1: Bestandsaufnahme (lokal, kein Browser)**
1. `config/geschaeftspartner.json` lesen
2. Zaehlen: `datev_angelegt === true` vs `false`
3. Priorisierung nach Transaktionshaeufigkeit (haeufigste zuerst — mehr Lerneffekt)
4. Zusammenfassung im Chat:
   ```
   Geschaeftspartner: {total} gesamt
   In DATEV angelegt: {angelegt}
   Noch nicht angelegt: {nicht_angelegt}
   Priorisiert (>3 Transaktionen): {prio_count}
   ```

**Schritt 2: Modus-Abfrage (AskUserQuestion)**
- "Batch anlegen — Top {n} Partner in DATEV erstellen"
- "Neue erkennen — Unbekannte Partner aus letztem Scan identifizieren"
- "Status pruefen — Welche Partner fehlen noch?"

**Schritt 3: DATEV Navigation (Browser)**
1. `mcp__pw-extension__browser_tabs` — DATEV-Tab finden
2. Falls kein DATEV-Tab: `browser_navigate` zu `{DATEV_URL}`
   → User fragen ob SmartLogin erledigt
3. Navigation: Startseite → FAB-Menubutton (unten) → "Anwendungen verwalten" → "Stammdaten" → "Geschaeftspartner"
   - URL wird: `duo-master-data/business-partner/clients/{CONSULTANT_ID}-{CLIENT_ID}`
   - **KEIN iframe** — Angular SPA mit Shadow DOM im Header
4. `browser_snapshot` — Verifizieren dass Partner-Liste geladen ist

**Schritt 4: Batch-Erstellung (5er-Batches mit Page-Reload)**

Pro Partner:
1. Button "Hinzufuegen" klicken
2. Typ waehlen:
   - Kreditor → Label "Lieferant" klicken (mat-slide-toggle)
   - Debitor → Label "Kunde" klicken (mat-slide-toggle)
   - **WICHTIG:** `mat-slide-toggle` ist KEIN Checkbox! Immer via Label klicken
3. Name setzen via `browser_evaluate` mit nativeInputValueSetter:
   ```javascript
   () => {
     var inputs = document.querySelectorAll('input[type=text]');
     var nameInput = inputs[1]; // Index 1 = Name (0=Anrede)
     var setter = Object.getOwnPropertyDescriptor(
       window.HTMLInputElement.prototype, "value"
     ).set;
     setter.call(nameInput, "PARTNERNAME");
     nameInput.dispatchEvent(new Event("input", {bubbles: true}));
   }
   ```
   - **MUSS** nativeInputValueSetter verwenden — sonst erkennt Angular die Aenderung nicht und Speichern-Button bleibt disabled
   - **MUSS** per Input-Position (`inputs[1]`) suchen — IDs (`mat-input-X`) aendern nach jedem Hinzufuegen!
4. USt-Land: Leer lassen fuer nicht-EU (US, etc.) — sonst Validierungsfehler bei USt-IdNr
5. USt-IdNr: Nur fuer EU-Partner eintragen
6. Speichern-Button klicken (erst aktiv wenn Name + Typ gesetzt)
7. **Timing:** 2s nach Hinzufuegen, 1s nach Toggle, 1s nach Name, 2s nach Speichern

**Alle 5 Partner: Page-Reload!**
- URL komplett neu laden (Seite resetten)
- Verhindert gestapelte Dialoge und Input-ID-Drift
- Bewiesener Flow: 117 Partner in ~15 Minuten fehlerfrei

**Schritt 5: Verifikation**
1. Nach jedem 5er-Batch: `browser_snapshot` — Partner-Liste pruefen
2. Angelegte Partner in `geschaeftspartner.json` markieren: `datev_angelegt: true`
3. Optional: Batch-Nummer in JSON (`datev_batch: "YYYY-MM-DD-N"`)

**Schritt 6: Neue Partner erkennen (optional, bei Modus "Neue erkennen")**
1. Letztes `logs/scan-data-*.json` laden
2. Transaktions-Empfaenger gegen `geschaeftspartner.json` matchen
3. Unbekannte Empfaenger als neue Partner-Kandidaten vorschlagen
4. User-Freigabe pro Partner (Name, Typ, Kategorie)
5. In `geschaeftspartner.json` einfuegen + in DATEV anlegen (→ Schritt 3-5)

**Schritt 7: State + Log**
1. `geschaeftspartner.json` speichern (aktualisierte `datev_angelegt` Flags)
2. Log: `logs/geschaeftspartner-batch-YYYY-MM-DD.json`
3. state.md updaten

**Bekannte Risiken:**
- Input-IDs (`mat-input-X`) inkrementieren nach jedem Hinzufuegen → IMMER per Position suchen
- `mat-slide-toggle` ist KEIN Checkbox → per Label-Click ("Lieferant"/"Kunde")
- Ohne nativeInputValueSetter bleibt Speichern-Button disabled (Angular Change Detection)
- Gestapelte Dialoge bei schnellem Hinzufuegen → Page-Reload alle 5 Partner
- Kein iframe auf Stammdaten-Seite (anders als Bank/Belege) → direkt `document.querySelector`
- Shadow DOM im Header (`datev-duo-mydatev-header-element`) → ggf. `shadowRoot` fuer Navigation

**Agenten:** Buchhalter (Browser-Automation)
**Browser:** JA — Playwright MCP Extension

### duplikate
Doppelte Belege in DATEV finden und optional loeschen:

1. **Belege online** oeffnen (Playwright MCP)
2. **Filter:** Alle Belege, Bearbeitet-Status "Alle", Zeitraum auf gewuenschten Bereich
3. **Inventar extrahieren:** SlickGrid Scroll-Extraktion (browser_evaluate):
   - `viewport = doc.querySelector('.slick-viewport')`
   - Alle 350px scrollen, 200ms Pause, Zeilen per `.slick-row` → `.slick-cell` auslesen
   - Spalten: [checkbox, Eingangsdatum, Dateiname, Belegdatum, Partner, Betrag, WKZ, Ablageort, Belegtyp, Status]
4. **3-Stufen Duplikat-Erkennung:**
   - **Tier 1 (99%):** Exakt gleicher Dateiname → Duplikat
   - **Tier 2 (95%):** Dateiname mit `(1)`, `(2)` Suffix → OS-Kopie
   - **Tier 3 (70-90%):** Gleicher Partner + Betrag + Datum ±3 Tage → pruefen
5. **Visuelle Verifikation:** Pro Duplikat-Gruppe in Schnellsuche suchen, im Viewer pruefen
   - Schnellsuche: Placeholder-Div klicken (ref f10eXXX), dann textbox ref tippen
   - ACHTUNG: `invoice.pdf (1)` + `invoice.pdf (2)` koennen VERSCHIEDENE Rechnungen sein!
6. **User-Freigabe** via AskUserQuestion (pro Gruppe oder gesamt)
7. **Loeschen:** Beleg auswaehlen → "Alle ausgewaehlten Belege in den Papierkorb verschieben"
8. **Log:** `logs/duplikat-analyse-YYYY-MM-DD.json`

**Bekannte Risiken:**
- DATEV Schnellsuche-Placeholder blockiert Klick auf textbox → Placeholder-Div klicken
- Zeitraum-Filter "Letzte 12 Monate" erfasst nicht alles → explizit "Gesamt" setzen oder abfragen
- Zugeordnete Belege NICHT loeschen — immer die nicht-zugeordnete Kopie loeschen
- CSV-Export-Button (ref f10e215) triggert Download, aber Datei kommt nicht immer an

**Agenten:** Buchhalter (Browser) + Compliance-Monitor (Validierung)
**Browser:** JA

### report
Report aus bestehendem JSON neu generieren (kein Browser, kein Gmail):
```bash
node scripts/generate-report.js logs/scan-data-YYYY-MM-DD.json [--anonymize]
open logs/report-*.html
```

### setup (Ersteinrichtung — Baby-Step Wizard)

Fuehrt neue User durch die komplette Ersteinrichtung. Prueft ZUERST was schon installiert ist und ueberspringt Vorhandenes.

**Step 1: Pre-Flight Checks (automatisch, silent)**
```bash
node --version        # Node.js 18+ (fuer Browser-Automation)
python3 --version     # Python 3.9+ (fuer Eigenbeleg-Generator)
npm --version         # npm (fuer Dependencies)
# Chrome: /Applications/Google Chrome.app (Mac) oder Registry (Windows)
```

Ergebnis als Checklist im Chat:
```
Systemcheck:
[x] Node.js v22.14.0 — bereits installiert
[x] npm 10.9.2 — bereits installiert
[x] Python 3.12.8 — bereits installiert
[x] Google Chrome — bereits installiert
[ ] npm Dependencies — fehlt → npm install
[ ] Playwright MCP Bridge — Chrome Extension fehlt
[ ] Gmail Zugriff — nicht konfiguriert
[ ] Mandant-Konfiguration — config/mandant.json fehlt

4 Schritte noetig. Soll ich dich Schritt fuer Schritt durchfuehren?
```

Bereits installiertes wird mit [x] markiert und automatisch uebersprungen.

**Step 2: npm Dependencies installieren (automatisch)**
```bash
npm install
pip3 install fpdf2 2>/dev/null || true
```
Kurze Erklaerung: "fpdf2 wird fuer den Eigenbeleg-Generator benoetigt (Privateinlagen etc.)"

**Step 3: Playwright MCP Bridge installieren (interaktiv)**
Erklaerung fuer User:
> "Die Playwright MCP Bridge ist eine Chrome-Extension die der KI erlaubt, deinen echten Chrome-Browser zu steuern. Dadurch kann die KI in DATEV navigieren, Daten auslesen und Belege hochladen — alles in DEINEM Browser mit DEINEM Login. Deine Passwoerter und Extensions bleiben erhalten."
>
> **Offizieller Link:** https://github.com/anthropics/playwright-mcp
> **Chrome Extension:** https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm

AskUserQuestion: "Hast du die Extension installiert?" → Wenn ja:
1. Claude konfiguriert `~/.claude/settings.json` automatisch (pw-extension Namespace!)
2. VS Code Reload anweisen

**Step 4: Gmail MCP einrichten (optional, interaktiv)**
Erklaerung:
> "Der Gmail-Zugriff erlaubt der KI, deine Emails nach fehlenden Rechnungen und Belegen zu durchsuchen. So findet sie automatisch Rechnungen von deinen Anbietern."
>
> **Setup:** Claude.ai → Settings → Connected Apps → Google verbinden (OAuth2)
> **Link:** https://claude.ai/settings
>
> **Nicht nur Gmail:** Falls du einen anderen Email-Anbieter nutzt (Outlook, etc.), kann die KI alternativ per IMAP zugreifen. Details: `docs/gmail-beleg-download.md`

AskUserQuestion: "Gmail einrichten oder ueberspringen?" → Ueberspringen ist OK, Skill funktioniert auch ohne Gmail.

**Step 5: Mandant konfigurieren (interaktiv via AskUserQuestion)**

Nur 3 Pflichtfelder:
1. **Firmenname** — "Wie heisst deine Firma?" (Anzeige in Reports)
2. **Berater-Nr** — "Deine 6-stellige DATEV Beraternummer? (Findest du in deiner DATEV-URL nach duo-start/)"
3. **Mandant-Nr** — "Deine 5-stellige Mandantennummer? (Steht nach dem Bindestrich in der URL)"

Validierung: Berater-Nr muss 6 Ziffern, Mandant-Nr muss 4-5 Ziffern sein.
DATEV-URL automatisch konstruieren und dem User zur Bestaetigung zeigen.

Optionale Felder (koennen beim ersten Scan auto-erkannt werden):
- Bank-Name, IBAN, BIC → werden aus DATEV ausgelesen
- Rechtsform, USt-Status → aus DATEV Stammdaten ableitbar

Claude schreibt `config/mandant.json` mit den Pflichtfeldern.

**Step 6: DATEV Login + Verbindungstest (interaktiv)**
1. User oeffnet Chrome und loggt sich bei DATEV ein (SmartLogin 2FA)
2. Claude testet `mcp__pw-extension__browser_tabs` → Chrome erreichbar?
3. `browser_snapshot` → DATEV-Seite sichtbar?
4. Bei Fehler: Troubleshooting (VS Code Reload, Extension pruefen, Token-Modus)

**Step 7: Erster Mini-Scan (optional, letzter Monat)**
> "Ich mache einen kleinen Test-Scan der letzten 4 Wochen. Dabei lese ich nur — nichts wird geaendert."

Scan mit Zeitraum-Limit (1 Monat). So sieht der User schnell ein Ergebnis.

AskUserQuestion nach Scan:
- "Setup abgeschlossen! Starte /datev-buchhalter fuer das volle Menue."

**Agenten:** Keiner (Setup laeuft in der Hauptkonversation)
**Browser:** Nur in Step 6-7

### status
Letzte Runs + Patterns anzeigen (nur lokale Dateien)

---

## Subagent-Prompts

### Subagent 1: DATEV Scan

```
Du bist der DATEV Buchhalter-Agent fuer Mandant {CONSULTANT_ID}-{CLIENT_ID}.

PFLICHTLEKTUERE — lies ZUERST diese Dateien:
1. .claude/skills/datev-buchhalter/state.md
2. docs/workflow-guide.md
3. docs/gobd-checkliste.md
4. docs/datev-navigation.md
5. docs/browser-automation.md
6. config/mandant.json
7. config/rules.json
8. config/konten-mapping.json

SCHRITTE:
1. Pre-Flight: mcp__pw-extension__browser_tabs — DATEV-Tab finden
2. Falls kein DATEV-Tab: browser_navigate zu {DATEV_URL}
   → User fragen ob SmartLogin erledigt
3. Bank-Modul → Bankkonto ({BANK_NAME}) → "Kontoumsaetze pruefen"
4. Zeitraum auf "{zeitraum}" setzen (browser_evaluate mit dijit Script-Injection)
5. SlickGrid auslesen (browser_evaluate, 3 Scroll-Positionen: 0px, 800px, 1250px)
6. BELEG-CROSS-CHECK: "Belege online" oeffnen
   - Pro offener Transaktion nach Empfaenger suchen
   - Status bestimmen: zugeordnet | hochgeladen | fehlend
   - WARNUNG: "fehlend" NUR wenn wirklich KEIN Beleg in DATEV!
7. Ergebnis als JSON schreiben: logs/scan-data-{datum}.json

JSON-SCHEMA: siehe docs/scan-data-schema.md oder Plan-Datei.
Felder pro Transaktion: nr, datum, empfaenger, betrag, typ, geprueft, beleg_status, notiz

RETURN: Kurze Zusammenfassung (max 500 Zeichen).
NUR Mandant {CONSULTANT_ID}-{CLIENT_ID}. NIEMALS andere Mandanten!
```

### Subagent 2: Gmail Scan

```
Du bist der Belegmanager-Agent.

PFLICHTLEKTUERE:
1. .claude/skills/datev-buchhalter/state.md
2. config/geschaeftspartner.json (Partner-Namen + Aliases fuer Suche)

SCHRITTE:
1. logs/scan-data-{datum}.json lesen
2. Transaktionen mit beleg_status="fehlend" extrahieren
3. Pro fehlender Transaktion: mcp__claude_ai_Gmail__gmail_search_messages
   - Query: Absender-Name (aus geschaeftspartner.json aliases)
   - Zeitraum: Transaktionsdatum ±7 Tage
   - Suche nach: invoice, receipt, Rechnung, Quittung
4. Ergebnis pro Txn: gmail_match Objekt (found, message_id, subject, typ)
5. scan-data JSON ergaenzen und speichern
6. WICHTIG: Invoice bevorzugen vor Receipt (§14 UStG)

RETURN: X gefunden, Y nicht gefunden, Z portal-only.
```

---

## Browser-Verbindung (Playwright MCP Extension)

Playwright MCP verbindet sich automatisch mit Chrome ueber die Bridge Extension.
Namespace: `pw-extension` (NICHT `playwright`!)

**Tools:** browser_navigate, browser_click, browser_snapshot, browser_evaluate,
browser_fill_form, browser_type, browser_press_key, browser_take_screenshot,
browser_tabs, browser_wait_for

**DATEV-spezifisch:**
- iframe `#contentwp`: Content im Accessibility Snapshot sichtbar (refs mit `f1e`)
- dijit-Dropdowns: browser_click funktioniert direkt
- SlickGrid: browser_evaluate mit Scroll + Daten-Extraktion
- Fallback CDP: Nur fuer Cross-Origin iframes (Upload-Dialoge)

---

## Mandant (EINZIGER erlaubter Mandant!)
- **Name:** {MANDANT_NAME} (aus config/mandant.json)
- **Consultant:** {CONSULTANT_ID} | **Client:** {CLIENT_ID}
- **URL:** {DATEV_URL}
- **NIEMALS andere Mandanten bearbeiten!**

## Sicherheitsregeln (ABSOLUT!)
1. NUR den in config/mandant.json konfigurierten Mandant bearbeiten
2. Keine Buchung ohne Beleg (Ausnahme: Privateinlage Einzelunternehmer)
3. Keine Buchung ohne Wirtschaftspruefer-Approval
4. Keine Buchung ohne User-Freigabe
5. Nichts loeschen oder aendern (nur neu erstellen)
6. Bei Unsicherheit IMMER User fragen
7. Jede Aktion loggen in `logs/`
8. Belege NICHT doppelt hochladen (DATEV erkennt Duplikate NICHT)

## Best Practices: Duplikate vermeiden

DATEV Unternehmen Online hat **KEINE automatische Duplikat-Erkennung**. Belege die mehrfach hochgeladen werden, werden separat gespeichert ohne Warnung. Das ist die haeufigste Fehlerquelle.

### Wie Duplikate entstehen
| Ursache | Beispiel | Vermeidung |
|---------|----------|------------|
| **Mehrfach-Upload** | Gleiche PDF 2x per Drag&Drop oder Email-Weiterleitung | VOR Upload: Dateiname in DATEV Schnellsuche pruefen |
| **Invoice + Receipt** | US-Anbieter (Anthropic, Stripe) senden beides — sieht aehnlich aus, sind aber 2 Dokumente | NUR Invoice hochladen (§14 UStG). Receipt nur als Backup, NICHT in DATEV |
| **OS-Kopien** | Browser benennt Download `invoice (1).pdf` wenn `invoice.pdf` schon existiert | Dateinamen vor Upload pruefen — `(1)` Suffix = wahrscheinlich Duplikat |
| **Email-Weiterleitung** | Email mit 2 Attachments (Invoice + Receipt) → DATEV laedt BEIDE hoch | Email oeffnen, nur Invoice-PDF weiterleiten |
| **Wiederholter Gmail-Download** | `rechnungen-suchen` wird 2x ausgefuehrt, laedt gleiche RE nochmal | Immer Duplikat-Check gegen DATEV-Bestand VOR Download |

### Pflicht-Checks vor JEDEM Upload
1. **Dateiname** in DATEV Schnellsuche pruefen (Belege online)
2. **Betrag + Datum + Partner** gegen bestehende Belege abgleichen
3. **Invoice vs Receipt** unterscheiden — nur Invoice als primaerer Beleg
4. Bei Unsicherheit: **User fragen**, NIE einfach hochladen

### Beim Gmail-Scan (`rechnungen-suchen`)
- IMMER erst DATEV-Bestand pruefen (Schritt 3 im Workflow)
- Nur Transaktionen OHNE Beleg durchsuchen
- Vor Download: Dateiname gegen DATEV-Inventar pruefen
- `receipt` im Dateinamen → nur als Backup anbieten, nicht automatisch herunterladen

### Duplikate bereinigen
→ Modus `duplikate` nutzen: Scannt alle DATEV-Belege, erkennt Duplikate per 3-Stufen-Algorithmus, visuelle Verifikation, User-Freigabe vor Loeschung.

## Proven Patterns (aus 50+ Sessions extrahiert)

### DATEV UI-Architektur
- **iframe#contentwp:** Hauptcontent ist im iframe. Immer `document.getElementById("contentwp").contentDocument` verwenden
- **Geschaeftspartner-Seite:** KEIN iframe — Angular-SPA mit Shadow DOM im Header. `datev-duo-mydatev-header-element` → shadowRoot
- **dijit-Widgets:** DOM-Clicks funktionieren NICHT fuer Dropdowns. Script-Injection: `doc.createElement('script')` in iframe, dann `dijit.byId('widgetId').set('value', X)`
- **Zeitraum-Filter:** `dijit.byId('...chooseDateFilterType_select').set('value', 10)` = "Individueller Zeitraum". Datum: `dijit.byId('...fromDateTextBox').set('value', new Date(YYYY, M-1, D))`
- **SlickGrid (virtualisiert):** Nur sichtbare Zeilen gerendert. Scroll-Extraktion: `viewport.scrollTop = pos`, 200ms wait, collect rows, repeat. Bank: 3 Positionen. Belege: 22 Positionen bei 350px Steps
- **Angular Material Inputs:** IDs (`mat-input-X`) inkrementieren bei jedem Dialog. IMMER per Position: `querySelectorAll('input[type=text]')[1]` = Name
- **nativeInputValueSetter:** Angular erkennt DOM-Wertaenderungen NICHT. Pattern: `Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set.call(input, 'WERT')` + `input.dispatchEvent(new Event('input', {bubbles: true}))`
- **mat-slide-toggle:** Ist KEIN Checkbox. Per Label-Text klicken ("Lieferant" / "Kunde")
- **Geprüft-Toggle:** Angular CSS-Slider in `.verify-panel`, KEIN dijit-Widget

### Browser-Automation
- **Playwright MCP Extension:** `pw-extension` Namespace (NICHT `playwright` — Kollision mit built-in Plugin)
- **CDP DPR:** deviceScaleFactor MUSS 1 sein (nicht 2!). DPR 2 halbiert CSS-Breite, DATEV braucht min 1280px
- **Upload-iframe:** `upload-online.apps.datev.de` ist Cross-Origin. Kein JS-Zugriff aus Mandant-iframe. Upload ueber separaten Tab oder Drag&Drop
- **Screenshot-Debug:** Bei Haengern SOFORT Screenshot machen statt blind retry

### DATEV Business-Logik
- **OCR-Verarbeitung:** 24h+ Verzoegerung nach Upload. Belege im Posteingang werden evtl. nicht verarbeitet. Steuerberater muss ggf. aktivieren
- **Duplikate:** DATEV erkennt Duplikate NICHT automatisch. VOR jedem Upload: Dateiname + Betrag + Datum pruefen
- **Invoice vs Receipt:** US-Anbieter senden beides — NUR Invoice hochladen (§14 UStG Pflichtangaben)
- **"Ausfuehren und weiter":** Setzt Beleg-Zuordnung + Geprueft-Flag in EINEM Schritt
- **Zuordnungsvorschlaege:** Zeigen auch bereits zugeordnete Belege! IMMER Thumbnail visuell pruefen
- **Bank-Einstellungen (Auto-Zuordnung):** Nur in Kanzlei-Desktop sichtbar, nicht fuer Mandanten
- **Bankkonto-Wechsel:** "Kontoumsaetze pruefen" Table klicken (cursor=pointer), nicht Info-Table. ID: `dialogSelectBankkonto_navigateToReviewKontobewegungenBlatt_account{N}_link`

### Batch-Operationen
- **Geschaeftspartner-Batch:** Alle 5 Partner → Page-Reload (URL neu laden). Verhindert gestapelte Dialoge + ID-Drift. Timing: 2s Hinzufuegen, 1s Toggle, 1s Name, 2s Speichern
- **Belege online Schnellsuche:** Placeholder-Div klicken (nicht textbox direkt) — Placeholder blockiert Pointer Events
- **Gmail-Download:** Gmail MCP kann Emails suchen+lesen, aber KEINE Attachments herunterladen. Workaround: Stripe Invoice-Links per `curl` (auth-frei)

---

## State Update (IMMER am Ende ausfuehren)
1. `.claude/skills/datev-buchhalter/state.md` → Run + Fehler + Fix
2. Cross-Skill Learnings → MEMORY.md
