Files
craftvia/docs/craftvia/ABNAHME.md
T
msolarczekandClaude Opus 5 d3bc7f2d29 Doku: Abnahme-Matrix Craftvia MVP (Spec §40, US-001–012, §44)
17 Abnahmekriterien und 12 User Stories mit Status und Nachweis (Testskript, Route,
Lane-Bericht); Soll-/Kann-Funktionen; getroffene MVP-Annahmen zu den offenen
fachlichen Entscheidungen; bekannte Einschränkungen und nicht verifizierte Punkte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 18:59:47 +02:00

106 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Craftvia MVP – Abnahme-Matrix
> Stand: 2026-09-15 · Branch `feature/craftvia-mvp` @ `ef2f3f1` · Gate: tsc · lint · build · **65/65 Testskripte grün**; komplette Suite zusätzlich mit `RLS_ENFORCED=true` **65/65 grün**
> Quelle der Anforderungen: `docs/craftvia/SPEC-CRAFTVIA.md` §40 (Gesamt-Abnahme), §37 (User Stories), §44 (offene fachliche Entscheidungen).
> Status: **erfüllt** = umgesetzt und durch automatisierten Test belegt · **teilweise** = umgesetzt mit dokumentierter Lücke · **offen** = nicht umgesetzt.
> Nachweise verweisen auf Testskripte unter `scripts/` (Lauf: `npm run test`) und Lane-Berichte unter `docs/craftvia/lanes/`.
## 1. Gesamt-Abnahmekriterien (Spec §40)
| # | Kriterium | Status | Nachweis | Offene Punkte |
|---|---|---|---|---|
| 1 | Mehrere Mandanten mit sicher getrennter Datenhaltung | erfüllt | Tenant-Guard `src/server/db.ts` + RLS `enable_tenant_rls` für alle Fachtabellen; `test-tenant-isolation`, `test-rls-enforcement` (35+ Tabellen FORCE RLS), Mandantentrennungs-Fälle in allen Lane-Tests; `test-tenant-transaction` (Owner + RLS); **komplette Testsuite zusätzlich mit `RLS_ENFORCED=true` grün (52/52)** ; `test-e2e-*` Mandantentrennung über alle 35 Tenant-Modelle (direkt in Postgres + 67 Service-Aufrufe), `test-security-http` (Produktions-Build: fremde IDs in allen `/api/v1`- und Datei-Routen → 404) | RLS in Prod aktivieren (`RLS_ENFORCED=true`, Rolle `craftvia_app` LOGIN) – Betriebsschritt |
| 2 | Benutzer, Rollen und Montageteams verwaltbar | erfüllt | Fundament Nutzer/Rollen/Einladung (`test-invitation`, `test-tenant-users-authz`); Rollen tenant-admin/backoffice/team-lead/technician in `rbac.ts`; Teams L1 (`test-stammdaten-teams`) | |
| 3 | Auftragsbestätigungen als PDF importierbar | erfüllt | L3 `/imports`, `POST /api/v1/work-orders/import`; `test-import-flow`, `test-import-rules` | Live-Extraktion gegen Claude nur mit `ANTHROPIC_API_KEY` (`test-import-live`) |
| 4 | Kunden-, Objekt- und Auftragsdaten automatisiert extrahiert | teilweise | L3 Claude-Provider (PDF-/Bild-Block, JSON-Schema, Konfidenz je Feld), Plausibilitätsprüfung, Dubletten-/Objektkandidaten; Fake-Provider-Tests | Umgesetzt und mit Fake-Provider getestet; **Live-Extraktion mit Claude mangels `ANTHROPIC_API_KEY` nicht verifiziert** – an echten Pilotdokumenten validieren |
| 5 | Backoffice prüft und korrigiert erkannte Daten | erfüllt | Prüfmaske `/imports/[id]` (unsichere Felder markiert, Korrektur-Diff, keine Auftragsanlage ohne Bestätigung); `test-import-flow` K-Fälle | |
| 6 | Aufträge Teams zuweisbar | erfüllt | L2 `assignWorkOrder` + Event `work_order.assigned` → L6 Empfänger; `test-auftraege-core`, `test-benachrichtigungen-events` | |
| 7 | Monteure bearbeiten Aufträge auf Tablet/Smartphone | erfüllt | L4 `/m/**` (eigene Shell, Bottom-Nav, Touch ≥ 48 px); `test-einsatz-field`; authentifizierter Smoke `scripts/smoke-auth.ts` | `smoke-auth.ts` 94/94 Seiten-Prüfungen für alle Rollen + zweiten Mandanten; visuelle Prüfung 375/768/1024 px im Browser offen |
| 8 | Wesentliche Daten offline erfassbar | erfüllt | L7 Service Worker, IndexedDB-Outbox, `/m/offline`, `/m/sync`; `test-offline-core`, `test-offline-sync-e2e` (20 Ops → 1 Batch, Replay → duplicate) | Manueller DevTools-Offline-Test; Abschließen offline bewusst gesperrt (Pflichtprüfung server-seitig) |
| 9 | Arbeitszeiten, Tätigkeiten, Material, Fotos dokumentierbar | erfüllt | L4 Sessions/TimeEntry, Notizen, Material (Abweichungsgrund Pflicht), Fotos (Kompression, Pflichtfotos); `test-einsatz-field`, `test-einsatz-sync` | |
| 10 | Tagesberichte erzeugbar | erfüllt | L5 Tagesbericht (Content-Snapshot je Tag, Auftrag bleibt offen); `test-berichte-flow` | |
| 11 | Abschlussberichte inkl. Kundenunterschrift | erfüllt | L5 Abschlussbericht, Unterschrift inkl. Abwesend/Verweigert/Später mit Begründung; `test-berichte-flow` | |
| 12 | PDF-Arbeitsnachweis erstellt | erfüllt | L5 HTML→PDF (Playwright/Chromium im Worker), Checksumme, unveränderliche Version; `test-berichte-pdf` | Worker-Image `craftvia-worker` mit Chromium lokal gebaut und PDF erzeugt (L10b); Registry-Push/Prod-Deploy offen |
| 13 | Backoffice gibt abgeschlossene Aufträge zur Abrechnung frei | erfüllt | L2 `releaseForBilling` nur mit freigegebenem Abschlussbericht; `test-auftraege-status` | |
| 14 | Monteure legen selbstständig Notdiensteinsätze an | erfüllt | L8 `/m/emergency` (vorläufiger Kunde/Objekt in einer Transaktion, offline als Sync-Op); `test-notdienst-flow` | |
| 15 | Backoffice wird über Notdiensteinsätze informiert | erfüllt | L6 Pflichtmail + In-App bei `emergency.created/completed` (Format §19.4); `test-notdienst-flow`, `test-benachrichtigungen-events` | Echter SMTP-Versand in Prod konfigurieren |
| 16 | Frühere Einsätze und technische Dokumente pro Objekt abrufbar | erfüllt | L1 Objekt-Historie + Dokumente (Sichtbarkeit/Scope), L4 mobile Historie; `test-stammdaten-sites`, `test-stammdaten-documents` | |
| 17 | Relevante Änderungen revisionsfähig protokolliert | erfüllt | `writeAuditLog` (before/after, IP, User-Agent) in allen Mutationen, Audit-Viewer `/settings/audit`; `test-audit-mail-context`, `test-benachrichtigungen-inbox-audit` | Audit-Einträge in Transaktionen erst nach Commit (L10b); Tamper-Schutz/Log-Aggregation (Betrieb) offen |
## 2. User Stories (Spec §37)
| US | Titel | Status | Nachweis | Offene Punkte |
|---|---|---|---|---|
| US-001 | Mandant anlegen | erfüllt | Plattform-Konsole `/admin` (Fundament), `provisionTenant`; Isolationstests | |
| US-002 | Auftragsbestätigung importieren | teilweise | L3 (s. §40 #3–5); `test-import-flow`, `test-e2e-*` (Import → Abrechnung) | Live-Texterkennung/Extraktion mit Claude nicht verifiziert (kein API-Schlüssel) |
| US-003 | Bestehenden Kunden erkennen | erfüllt | L1 `findDuplicateCustomers` (keine Auto-Zusammenführung), L3 Kandidaten in Prüfmaske; `test-stammdaten-duplicates` | |
| US-004 | Auftrag einem Team zuweisen | erfüllt | L2 + L6 | |
| US-005 | Technische Unterlagen ansehen | erfüllt | L1/L4 Dokumente mit Sichtbarkeit (backoffice_only verborgen), freigegebene Historie; L7 Vorab-Download offline | |
| US-006 | Einsatz dokumentieren | erfüllt | L4 | |
| US-007 | Tagesbericht erstellen | erfüllt | L5 | |
| US-008 | Abschlussbericht erstellen | erfüllt | L5 (Pflichtangaben/-fotos geprüft, Status „Zur Prüfung") | |
| US-009 | Auftrag zur Abrechnung freigeben | erfüllt | L2 + L5 + L6 | |
| US-010 | Notdiensteinsatz anlegen | erfüllt | L8 + L6 | |
| US-011 | Objekt-Historie aufrufen | erfüllt | L1 `getSiteHistory` (offene Folgearbeiten hervorgehoben) | |
| US-012 | Offline arbeiten | erfüllt | L7 + L4 Sync-Server (Idempotenz, Konflikte nicht still überschrieben) | |
## 3. Soll-/Kann-Funktionen (Spec §36.2/§36.3)
| Funktion | Status | Nachweis / Anmerkung |
|---|---|---|
| Sprachnotizen + Transkription | erfüllt | L4 Aufnahme, L9 Whisper-kompatibler Provider (`test-lotse-transcription`); ohne `TRANSCRIPTION_API_KEY` Status `disabled` |
| KI-Berichtsentwurf (Lotse) | erfüllt | L9 Vorschlag in `content.lotse`, Prüfbestätigung serverseitig Pflicht, Datenminimierung (`test-lotse-draft`) |
| Konfigurierbare Vorlagen | erfüllt | L2 `/settings/order-types`, `/settings/checklists` |
| E-Mail-Versand von Berichten | erfüllt | L6 interne Benachrichtigungen; L11 „An Kunden senden“ auf `/reports/[id]` und `POST /api/v1/reports/{id}/send`: freigegebenes PDF als Anhang (Queue enthält nur Document-Referenz; Zustellung lädt Bytes mandantengebunden mit Prüfsummenprüfung), Recht `report:approve`, Audit, Dedupe je Adresse+Version; `test-report-customer-mail` (41 Prüfungen), realer Versand an Mailhog geprüft. Offen: kein erneutes Senden an dieselbe Adresse, ein Empfänger ohne CC, Sprache = Mandant |
| Berichtsversionierung | erfüllt | L5 `lineageId`/`version`, alte Version erst bei Freigabe der neuen `superseded` |
| MFA | erfüllt | Fundament TOTP + Passkeys, Mandanten-MFA-Pflicht |
| Kartenlink zur Einsatzadresse | erfüllt | L1 OpenStreetMap-Link, L4 Routenlink |
| Push-Benachrichtigungen | offen | nach MVP (Spec §20.2 „später") |
| Kalenderansicht | offen | optional (§11.2) |
| Standorterfassung beim Einsatzstart | erfüllt | L4 optional beim Session-Start |
| Barcode/QR-Code | offen | Version 2 |
| Kundenspezifische Statusmodelle | offen | Statusmodell global, Labels gruppiert (Brandbook §12.3) |
| Digitale Einsatzfreigabe durch Teamleiter | erfüllt | L5 `report:approve_team` |
## 4. Offene fachliche Entscheidungen (Spec §44) – getroffene MVP-Annahmen
| Thema | MVP-Annahme | Zu klären mit Pilotkunde |
|---|---|---|
| Berichtsvorlage | generische Vorlage L5 mit Mandantenlogo/-namen | Layout/Pflichtinhalte des Pilotkunden |
| Pflichtfelder je Auftragsart | über Checklisten-Vorlagen je Auftragsart konfigurierbar | konkrete Pflichtfelder |
| Fotoarten | Standard-Pflichtfotos §14.2 als Vorschläge | verbindliche Liste |
| Arbeitszeitkorrektur | nur mit `field:correct_time` (Teamleiter/Admin), Grund Pflicht, Audit | Genehmigungsprozess? |
| Aufbewahrungsfristen | Soft Delete, keine automatische Löschung; KI-Protokoll: `AI_GENERATION_RETENTION_DAYS` (Default 180): täglicher Worker-Job leert Ein-/Ausgaben und Personenbezug im KI-Protokoll; monatliches Token-Kontingent je Mandant (`/settings/lotse`) | Fristen je Dokumentart |
| Empfänger Abrechnungsbenachrichtigung | `/settings/email` Abrechnungsempfänger je Mandant | Adressen |
| OCR-/KI-Anbieter | Claude (Anthropic) für Extraktion + Lotse, Whisper-kompatibel für Transkription (Entscheidung 2026-09-14) | Vertrag/AVV, Region |
| E-Mail-Dienst | SMTP (Mail-Worker, Queue) | Anbieter, SPF/DKIM/DMARC |
| Hostingstandort | Coolify/Docker (aus Certvia übernommen) | Rechenzentrum/Region |
| MFA-Regelung | optional je Nutzer, Mandanten-Pflicht einschaltbar | Pflicht für Admins? |
| Standortdaten | optional beim Einsatzstart/Foto, kein Dauertracking | Einwilligung/Betriebsvereinbarung |
| Maximale Offline-Dauer | `OFFLINE_MAX_DAYS=7` | Wert |
| Maximale Dateigrößen | Bild 15 MB, PDF 25 MB, Audio 20 MB | Werte |
| Buchhaltungssoftware | keine Schnittstelle (MVP-Abgrenzung §35) | Zielsystem für V2 |
| Spätere Schnittstellen | versionierte API `/api/v1` + OpenAPI | Prioritäten |
| Branding der PWA | Craftvia (Brandbook), Mandantenlogo in Berichten | Mandanten-Branding in App? |
| Berichtssprache | Deutsch (EN-Kataloge vorbereitet) | |
| Lösch-/Archivierungskonzept | DSGVO-Export/-Löschung aus Fundament, PII-Liste um Fachfelder erweitert | Löschfristen |
## 5. Bekannte Einschränkungen vor Produktivbetrieb
- **Login-Drosselung:** Konto-Lockout nach 5 Fehlversuchen vorhanden; zusätzlich Drosselung je IP und je Konto (Scope `login`, 20 Versuche / 15 min, `LOGIN_RATE_LIMIT_PER_15_MIN`; `test-login-rate-limit`). Zähler im Speicher je App-Instanz.
- **Polyglot-Uploads:** gültiger Bildkopf mit angehängtem Inhalt passiert die Typprüfung (Auslieferung als Download mit erkanntem Typ + `nosniff`, keine Ausführung); echte Inhaltsprüfung nur mit ClamAV (`CLAMAV_HOST`).
- **Rate-Limit** `/api/v1` je App-Instanz im Speicher (bei mehreren Instanzen Redis-Zähler nötig); API nur mit Session-Cookie (kein Token-Zugang für Integrationen).
- **Mobile Listen** ohne Paginierung (max. 200 bzw. 100 heute) – für MVP-Größen ausreichend (Performance-Messung 5 020 Aufträge: Dashboard/Liste 30–53 ms, mobil ~110 ms im Produktions-Build).
- **`emitEvent` in Transaktionen** feuert sofort (bei späterem Rollback evtl. Benachrichtigung ohne Datenänderung).
- **Unterschrift offline:** Sync-Op `signature.capture` nicht registriert (Unterschrift erfordert Verbindung).
- **Live-KI** (Claude-Extraktion, Lotse, Transkription) ohne API-Schlüssel nicht verifiziert.
- **Deployment:** Images lokal gebaut (App 400 MB, Worker 2,68 GB inkl. Chromium), kein Registry-Push, CI-Testjob nicht auf Server ausgeführt.
- **Demo-Seed:** Daten relativ zum Seed-Zeitpunkt.
## 6. Nicht verifiziert in dieser Umgebung
- Live-Aufrufe gegen Claude (Extraktion, Lotse) und Transkription – keine API-Schlüssel gesetzt.
- Visuelle Prüfung mit echter Passwort-Anmeldung im Browser (Agenten geben keine Passwörter ein; angemeldete Prüfungen per `scripts/smoke-auth.ts`).
- Manueller Offline-Test mit Browser-DevTools.
- Deployment auf Zielumgebung (nur lokaler Docker-Build).