# DATEV-Scharfschalt-Plan (KAR-638 Option A) — DRAFT, await Kais "go"

Status: prepared 2026-05-29 while Kais runs Option B (real-receipt extraction tests).
Sensitive paths touched → plan-mode mandatory (CLAUDE.md §1: #3 migrations, #4 packages/datev).
Do NOT implement before Kais's explicit "go". Do NOT flip `stable` flag (05-DATEV §1.1 step 4).

## Current state (verified)
- Export route `apps/web/src/app/(authed)/belege/export/[space]/route.ts` = DRAFT:
  - consultantNumber/clientNumber hardcoded '00000'
  - no XSD validation
  - exports ALL booked receipts (no period scope)
  - no datev_exports audit row, only a logger.info
- packages/datev: csv/xml/zip/skr builders exist + tested. No XSD validation. No period filter.

## Scope (maps to 05-DATEV.md)
1. **XSD vendoring + validation (§1.1 steps 1–2)**
   - Vendor official DATEV document XSD → `/vendor/datev/xsd/` + `VERSION.md` (download date + DATEV release, version-pinned).
   - `packages/datev/src/xml.ts`: add `validateDocumentXml(xml): {ok, errors}` using a pinned XSD validator dep (e.g. libxmljs2 or xsd-schema-validator — pin exact version per CLAUDE.md §7).
   - Export refuses (runtime + CI) on schema mismatch.
   - Add xml.validation.test.ts (valid passes, malformed fails).
2. **advisor_config table (§3, §12) — MIGRATION (sensitive)**
   - `advisor_config(space_id PK→spaces, consultant_number, client_number, skr_mode CHECK in('SKR03','SKR04') default 'SKR04', advisor_email, created/updated audit cols)`.
   - ENABLE+FORCE RLS, space-scoped (is_member_of_space) + family_admin; insert/update family_admin or business_owner.
   - Auto-fill tenant_id/created_by trigger. audit.log_event at end. `[plan-approved]` token in PR body.
   - Export reads advisor_config for the space; **refuses if absent** (§12: no export without consultant+client number).
3. **datev_exports audit table (§11) — MIGRATION (sensitive)**
   - `datev_exports(id, space_id, tenant_id, period_year, period_month, document_count, total_cents, manifest jsonb, signature, created_at, created_by)`.
   - RLS space-scoped. Insert one row per generated export. Emit `datev.export_generated` audit event (period, business, count, total).
4. **Period scoping (§2, §11)**
   - Export by `?period=YYYY-MM` (or path) → filter booked receipts by booking_date month. Filename `durrani-<YYYY-MM>.zip`. UI: month picker on the export button.
5. **Pre-finalize validation gate (§9, partial)**
   - Refuse export if: no advisor_config; XSD invalid; any receipt in period still status 'review'/'extracted' (FLAGGED). 
   - MISSING-receipt detection (§6) needs banking module → OUT OF SCOPE (defer, note in plan).
   - Hallucination-guard 5% sample (§9.6) → OUT OF SCOPE (defer).
6. **Stop-gate discipline (§1.1 steps 3–4)**
   - Round-trip with real Steuerberater stays a MANUAL gate. `stable` flag stays false. Document in PR: export stays "Entwurf" in UI until Kais signs off the gate PR after a clean advisor round-trip for one full month.

## Security/privacy
- Tier 3 (ADR-007 supplier receipts). consultant/client numbers Tier-3 business data → advisor_config RLS-protected, not logged in clear.
- No service_role from app. XSD validation pure/offline.
- Capability tokens: export is human-confirmed download (§12), no LLM dispatch in this slice.

## Tests
- xml XSD validation (valid/invalid), advisor_config RLS contract test (packages/rls), datev_exports RLS, period-filter unit, export-refusal cases (no advisor_config, flagged items).

## Suggested PR slicing
- PR-A1: advisor_config migration + RLS test + export reads it (refuse if absent).
- PR-A2: XSD vendoring + validateDocumentXml + wire into export.
- PR-A3: datev_exports table + audit event + period scoping + UI month picker.
- PR-A4 (gate, later): stop-criteria doc + manifest stable flag (only after advisor round-trip; needs Kais signature).

## Open questions for Kais
- SKR03 oder SKR04 für Durrani? (Default SKR04; Einzelunternehmen oft SKR03.)
- Berater-/Mandanten-Nummer hat er die schon vom Steuerberater? (sonst bleibt's Entwurf bis er sie hat)
- Soll der Export-Button erst nach advisor_config sichtbar/aktiv sein?
