From d3bc7f2d29224cfc6d345d5fa70ecccf98f9d383 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 14 Sep 2026 18:59:47 +0200 Subject: [PATCH] =?UTF-8?q?Doku:=20Abnahme-Matrix=20Craftvia=20MVP=20(Spec?= =?UTF-8?q?=20=C2=A740,=20US-001=E2=80=93012,=20=C2=A744)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/craftvia/ABNAHME.md | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 docs/craftvia/ABNAHME.md diff --git a/docs/craftvia/ABNAHME.md b/docs/craftvia/ABNAHME.md new file mode 100644 index 0000000..e23bdeb --- /dev/null +++ b/docs/craftvia/ABNAHME.md @@ -0,0 +1,105 @@ +# 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).