Files
craftvia/docs/craftvia/ABNAHME.md
T
msolarczekandClaude Opus 5 bf92100661 Merge lane/planung in feature/craftvia-mvp
Konflikte gelöst (additiv): Benachrichtigungstexte (Zeiterfassung + Planung, JSON zusammengeführt),
Event-Typen, Navigation, Empfänger-Felder, handle-event (reason inkl. rejectionReason + Planungsfelder),
Smoke-Prüfungen (Teamleiter: Zeiten + Planung).

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

15 KiB
Raw Blame History

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 erfüllt (Backoffice) L13 Menüpunkt „Planung“ → /planning: Standard „Heute + nächste 4 Werktage“ (Teams = Kolonnen als Zeilen, heutige Spalte mit Live-Status je Kolonne), Umschalter Heute (Stundenraster 6–20 Uhr, Kolonnen als parallele Spalten) · 5 Tage · Woche · Nächste Woche; Auslastung der Kolonnenkapazität je Tag, Konflikte (überbucht, Überschneidung, Person doppelt eingeplant, außerhalb der Arbeitstage) mit roter Kante + Icon + Text, Hinweis „Kolonne unvollständig“, Drag & Drop mit Bestätigung und Tastatur-Alternative, ungeplante Aufträge in der Seitenleiste; Dashboard-Kacheln „Planung heute“ und „Konflikte diese Woche“; Teamleiter lesend für eigene Kolonnen; test-planung-core. Mobile Kalenderansicht für Monteure weiterhin offen (§11.2 optional)
Live-Lage der Monteure erfüllt (ohne GPS) L13 /planning/live: ein Marker/Eintrag je Kolonne am Objekt des laufenden Auftrags, Mitglieder mit individuellem Status aus ihrer WorkSession, Monteure ohne Team einzeln; Karte (OpenStreetMap) + Liste, 30-s-Aktualisierung; keine Geräte-Koordinaten; test-planung-live
Verzugswarnungen / früher fertig erfüllt (Hinweis) L13: erfasste Kolonnenzeit (vereinigte Arbeitssegmente) ≥ 80 % → „Verzug droht“, ≥ 100 % → Meldung planning.overrun (erneut je +50 %), gefährdeter Folgeauftrag (Restzeit + geschätzte Fahrzeit bzw. Tageskapazität) → Markierung + planning.followup_at_risk, früher fertig → „früher fertig“ + Vorzieh-/Umkreis-Vorschläge + planning.capacity_freed; Meldungen nur In-App an Backoffice + Teamleiter, dedupliziert, aus dem 5-Minuten-Job planning-watch; nie automatische Umplanung; test-planung-watch
Einsatz-Empfehlungen erfüllt (Vorschlag) L13: Teams mit Auftrag in der Nähe (Luftlinie) und freier Kapazität, Begründung als Text, Übernehmen nur mit Bestätigung; nahe ungeplante Aufträge; Geocoding über Nominatim-Job mit Cache; test-planung-recommend, test-planung-geocode. Keine Routen-/Fahrzeitberechnung (Spec §35 „automatische Tourenoptimierung“ bleibt außerhalb)
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 Entschieden (L12 Zeiterfassung, 2026-09-15): Monteure tragen eigene Zeiten der letzten 7 Tage nach bzw. schlagen Korrekturen vor (field:record_own_time), Begründung Pflicht, Kennzeichen „manuell“; Freigabe durch Teamleiter (eigene Teams) oder Backoffice (time:approve), nie durch die erfassende Person selbst; bis zur Freigabe zählen Nachträge nicht für Bericht/Summen, Korrekturvorschläge lassen den alten Wert gelten; Abrechnungsfreigabe gesperrt, solange Zeiten offen sind. Direktkorrektur/Nachtrag für Teammitglieder weiterhin mit field:correct_time (sofort freigegeben). Grund, Audit before/after, Events time.approval_requested/approved/rejected Freigabefrist/Eskalation, Export an Lohnabrechnung
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. Live-Lage (L13) nutzt keine Geräte-Standorte: angezeigt wird der Einsatzort (Objektadresse) des laufenden Auftrags, Status aus der Zeiterfassung; Start-Koordinaten der WorkSession werden weder gelesen noch ausgeliefert. Objektadressen werden serverseitig über OpenStreetMap Nominatim verortet (nur Adresse, keine Personendaten, max. 1 Anfrage/s, Ergebnis am Objekt gespeichert); Kartenkacheln lädt der Browser von tile.openstreetmap.org (IP-Adresse des Nutzers wird dabei an OSM übertragen) Einwilligung/Betriebsvereinbarung für die Anzeige „wer arbeitet wo“; Datenschutzhinweis zur Kachel-/Geocoding-Nutzung bzw. eigener Karten-/Geocoding-Dienst (EU-Hosting) für Produktion
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).