Laufzettel läuft jetzt in zwei Betriebsmodi, aber nicht in zwei Codepfaden. Jede kundenbezogene Zeile trägt ohnehin schon eine Kunden-Zuordnung; im Modus INTERN ist das die eine Organisation, die in der Oberfläche nirgends erscheint. Der Wechsel ist deshalb eine Datenänderung und keine Migration — dieselbe Zeile bleibt, nichts hängt um. src/server/mandant.ts ist die eine Stelle, an der die Mandantenregel steht: kundeErmitteln verwirft im Modus INTERN eine mitgeschickte kundeId, verlangt im Modus DIENSTLEISTUNG eine Wahl und prüft, ob der Benutzer den Kunden überhaupt sehen darf. Die Sichtbarkeit steckte zunächst in einer zweiten Funktion daneben, an die jeder künftige Aufrufer selbst hätte denken müssen; da #009 diese Signatur übernimmt, ist sie jetzt eingebaut. Der unabhängige Review fand drei Blocker, alle behoben. Die Lesemodelle wandten die Mandantensicht nicht an. kundenListe gab jedem alle Kunden zurück, und sichtbareKundenIds hatte in der Produktion keinen einzigen Aufrufer — ein gebundener Techniker hätte Namen, Notizen und Bestandszahlen fremder Kunden gesehen. Die Funktionen verlangen jetzt den Benutzer und filtern selbst, wie CLAUDE.md es verlangt. Mein Kommentar "Alles in einer Transaktion" war falsch: der Protokolleintrag stand dahinter. Schlug er fehl, war der Modus gewechselt und der Vorgang stand nirgends. Und Prüfung und Vollzug lagen auseinander, sodass zwei gleichzeitige Wechsel beide durchliefen und ein zwischendurch angelegter Kunde zu Modus INTERN bei zwei Kunden führte. Beides reproduziert, beides geschlossen: alles in einer Transaktion, mit pg_advisory_xact_lock als erster Anweisung. Dazu sechs kleinere Befunde: ASCII-Faltung in Texten, die Menschen lesen (die Regel gilt für Bezeichner, nicht für Fließtext — steht jetzt so in CLAUDE.md); eine Erfolgsmeldung, die nach dem Wechsel unsichtbar blieb; eine Zusage im Dialog, die der Code nicht durchsetzt; fehlende Fehlerbehandlung beim Archivieren; ein Handy-Screenshot, der das Navigationsmenü statt des Dialogs zeigte. Der Wechsel wurde nicht nur im Service geprüft, sondern per Playwright durch die Oberfläche gefahren: anmelden, Dialog aufklappen, bestätigen, und danach steht die Navigation auf Kunden. 336 Tests. Closes #8 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.3 KiB
5.3 KiB
Abnahmekriterien für den MVP (v0.1.0)
Diese Liste wird von #019 abschließend durchgegangen. Jeder Punkt ist entweder abgehakt oder hat ein Folge-Issue — nichts bleibt stillschweigend offen.
Betrieb
docker compose upstartet App und Datenbank, Migrationen laufen automatisch- Seed läuft per dokumentiertem Befehl, nicht automatisch
- Die Datenbank ist vom Host aus nicht erreichbar (
nc -z localhost 5432schlägt fehl), auswebschon - Der Proxmox-One-Liner rollt einen LXC aus, in dem die App läuft und PostgreSQL nur am Unix-Socket hört
- Erneuter Aufruf des Skripts aktualisiert, ohne Daten oder Geheimnisse zu verlieren
shellcheck --severity=warningüber alle Skripte ohne Befund- Läuft hinter einem Reverse Proxy ohne fest verdrahtete Adressen
Anmeldung und Rechte
- Login per lokalem Konto funktioniert
- OIDC ist konfigurierbar und für authentik dokumentiert, inkl. Gruppen für Rollen
- Bei
AUTH_LOCAL_ENABLED=falseist der lokale Login weg und der Endpunkt lehnt ab - Rollenrechte: jede Rolle gegen jede Aktion getestet, Prüfung ausschließlich serverseitig
- Eine Rollenänderung greift ohne neue Anmeldung; ein deaktivierter Benutzer wird abgewiesen
- Passwörter stehen nur als argon2-Hash in der Datenbank, nie in einer API-Antwort
Betriebsmodus
- Hauptablauf läuft in
INTERNohne jede Kundenanzeige (Ausnahme: die Einstellungsseite und der Wechsel-Dialog zeigen die eigene Organisation — dort ist sie zu pflegen) - Hauptablauf läuft in
DIENSTLEISTUNGmit Kundenwahl - Wechsel
INTERN → DIENSTLEISTUNGübernimmt alle Daten auf den ersten Kunden - Wechsel zurück wird bei zwei nicht archivierten Kunden abgelehnt
- Im Modus
INTERNignoriert die Service-Schicht eine mitgeschicktekundeId - Der Modus ist ohne Neustart umschaltbar, jeder Wechsel steht im
Protokoll
Vorlagen
- Veröffentlichte Version ist unveränderlich; „Bearbeiten" erzeugt eine tiefe Kopie als Entwurf
- Höchstens ein Entwurf je Vorlage
- Veröffentlichen einer neuen Version verändert bestehende Läufe nicht
- Export und anschließender Import ergeben eine inhaltsgleiche Vorlage
- Unbekannte Platzhalter werden im Editor als Warnung angezeigt
Läufe und Archiv
- Lauf ohne Mitarbeiter und ohne Gerät lässt sich starten, abschließen und archivieren
- Abschließen scheitert, solange ein Pflichtschritt offen ist
NICHT_ZUTREFFENDundFEHLGESCHLAGENverlangen eine Notiz- „Abschnitt als nicht zutreffend markieren" setzt alle Schritte mit einer Notiz
- Archiv-Schreibschutz greift für Kopfdaten, Schrittergebnisse und Kommentare
- Nur
ADMINholt einen archivierten Lauf zurück, mitProtokoll-Eintrag - Im Archiv wird über Titel, Notizen, Werte und Kommentare gefunden
- Druckansicht eines Laufs ist sauber und hell
Mitarbeiter, Geräte, QR
- Mitarbeitersuche liefert standardmäßig nur aktive, Umschalter zeigt auch deaktivierte
- Ein von der Geräteseite gestarteter Lauf hat den Besitzer vorbelegt
/t/LZ-7K3F9Qohne Anmeldung führt nach dem Login auf die Geräteseite- „Vorlage zuweisen" erzeugt einen
OFFEN-Lauf, der unter „Zugewiesen" erscheint; „Starten" setztIN_ARBEIT - Unregistrierter Tag → Registrierformular → Geräteseite; unbekannter Tag → Hinweis
tagCode: richtiges Alphabet, eindeutig, unveränderlich, nie wiederverwendet- Etikettendruck in beiden Layouts sauber; QR maschinell zurücklesbar
Platzhalter und Befehle
- Onboarding: Vorname und Nachname eingeben → UPN,
samund Mail werden vorgeschlagen - Abteilung „Buchhaltung" wählen →
-Path "OU=Buchhaltung,…"und GruppenG-Buchhaltung, G-Alle - Kopieren liefert exakt diesen Text, zeichengenau
- „Jörg Müller-Lüdenscheid" ergibt gültige Vorschläge (
joerg.mueller-luedenscheid@…,jmuellerluedensch) {{generiertes_passwort}}erscheint im Befehl, steht aber nicht in Datenbank, Druckansicht oderProtokoll- Werte mit Anführungszeichen werden für die jeweilige Shell korrekt maskiert
- „Alle Befehle als Skript kopieren" liefert die Schritte in richtiger Reihenfolge
- Unbekannte Platzhalter bleiben stehen; ersetzte Werte sind HTML-escaped
Verzeichnis (LDAP)
- Erster Abgleich legt Mitarbeiter an, zweiter aktualisiert sie ohne Duplikate
- Entfernte oder deaktivierte Konten werden
aktiv = false, nie gelöscht {{ou}}wird korrekt aus demdnabgeleitet- Das Bind-Passwort erscheint in keiner API-Antwort und keinem Log
- Zwei gleichzeitige Abgleiche derselben Quelle: der zweite wird von der Sperre abgewiesen
Oberfläche
- Am Handy kein horizontales Scrollen — bei 390 px auf allen Hauptseiten geprüft
- Gestaltung nach Vorgabe: dunkel, ein Akzent, keine Emojis, keine Schatten, keine Verläufe
- Version mit Git-SHA unten links
Projektorganisation
- Issues für alle Phasen und MVP-Meilensteine existieren
CHANGELOG.mdunddocs/devlog.mdsind gepflegtv0.1.0ist getaggt, Gitea-Release mit Changelog-Auszugdocs/anleitung/vollständig mit Screenshots und als PDFdocs/screenshots/enthält die Meilenstein-Bilderpnpm lint && pnpm typecheck && pnpm test && pnpm pruefe:dokugrün