Zum Inhalt springen

Arbeitssicherheit · Unterweisung · PSA-Check · BG-Unfallmeldung

Live in Produktion

Feinkonzept · Arbeitssicherheit, Unterweisung & Unfallmeldung (§4.10)

Abschnitt betitelt „Feinkonzept · Arbeitssicherheit, Unterweisung & Unfallmeldung (§4.10)“

Feature-ID: handwerk/10-arbeitssicherheit · Stand: 2026-04-19 · Autor: Senior-Berater-Rolle Arbeitsschutz / DGUV / BG BAU Verwandte Feinkonzepte: 01-bautagebuch (PSA-Check ist eigener Eintragstyp im Bautagebuch; Unfall-Foto-Pipeline nutzt dieselbe S3/Geo-Stempel-Strecke), 11-fuehrerschein-zertifikate (Qualifikations-/DGUV-Scheine laufen im selben Ampel-Register, Planungs-Gate an derselben Stelle), 03-dienstplanung (Unterweisungs-Fälligkeit blockiert Schichtzuweisung), 08-audit-log (jede Unterweisung, jeder Unfall, jede PSA-Verweigerung schreibt eine Hash-verkettete Audit-Zeile).


feature_id: handwerk/10-arbeitssicherheit
title: Arbeitssicherheit · Unterweisung · PSA-Check · BG-Unfallmeldung
funktionsumfang_ref: §4.10
roadmap_horizont: V1.5
plattformen:
mobile: vollständig # Unterweisung, PSA-Check, Unfallmeldung → Feld-Realität
web: mit-Bulk # HSSE/SiFa-Dashboard, Fälligkeits-Liste, BG-F1000-Export
desktop: aus # Desktop bringt keinen Mehrwert gegenüber Web
owner_rolle: SiFa # Sicherheitsfachkraft (ASiG §5) ist Eigentümer des Moduls
modul_gate_flag: module.handwerk.arbeitssicherheit
compliance_flags:
gobd: true # Unterweisungsnachweise und Unfall-Akten = GoBD-relevant, §147 AO 10 Jahre
arbzg: false # Zeit bleibt in §3.1
vob: false
dsgvo: true # DSGVO Art. 9 Gesundheitsdaten bei Unfall
bfsg: false # Kein öffentlicher ESS-Flow, interne Pflichtunterweisung
betrvg: true # Unterweisungs-Tracking ist mittelbare Leistungskontrolle
stvg: false
weitere:
- "DGUV Vorschrift 1 §4 (Grundsätze der Prävention, jährliche Unterweisung)"
- "DGUV Vorschrift 1 §26 (Ersthelfer-Quote)"
- "DGUV Information 211-005 (Unterweisungsthemen)"
- "DGUV Vorschrift 2 (Betriebsärzte/SiFa)"
- "ArbSchG §5 + §6 (Gefährdungsbeurteilung)"
- "ArbSchG §12 (Unterweisungspflicht)"
- "§193 SGB VII (Unfallanzeigepflicht ab 3 Tagen AU)"
- "§8 Abs. 1 SGB VII (Arbeitsunfall-Begriff)"
- "PSA-Benutzungsverordnung (PSA-BV)"
referenzkunde:
status: TBD
name: "zu klären mit Sales — Kandidat: SHK-Gebrüder-Schmidt (NRW, 80 MA), heute Excel-Liste Unterweisungen + WhatsApp-Fotos bei Unfall; Sales-Call 2026-02 'BG-Meldung ist unser Horror, wir verpassen die 3-Tage-Frist regelmäßig'"
quelle: Sales-Call + Referenz-Interview SiFa Thomas Schmidt
estimate_eng_tage: 42 # 16 Mobile (Unterweisungs-Player, PSA, Unfall) + 12 Backend + 8 Web-Dashboard + 3 F1000-Adapter + 3 Test/Compliance
abhängigkeiten:
- kern/03-projekte-baustellen
- kern/08-audit-log
- kern/09-foto-upload-pipeline # Unfall-Fotos über dieselbe S3-Multipart-Strecke
- kern/11-dsgvo-toolkit # Gesundheitsdaten-Kapselung, getrennter KMS-Key
- handwerk/01-bautagebuch # PSA-Check als Eintragstyp
- handwerk/11-fuehrerschein-zertifikate # Gemeinsamer Ablauf-/Ampel-Mechanismus

Marktrealität (DE-Handwerk). Arbeitssicherheit im Handwerk läuft heute bei den meisten 10–80-MA-Betrieben über einen Ordner im Büro, eine Excel-Liste mit „Unterweisung am 12.03. erledigt“ und eine Papiertüte im SiFa-Büro. Die rechtlichen Schrauben sind aber deutlich enger angezogen, als dieses Bild vermuten lässt:

  • DGUV Vorschrift 1 §4 („Unterweisung der Versicherten“) verlangt eine Unterweisung vor Aufnahme der Tätigkeit, bei Veränderung des Aufgabenbereichs, bei Einführung neuer Arbeitsmittel oder Arbeitsverfahren und mindestens einmal jährlich. Sie muss arbeitsplatzspezifisch sein (eine allgemeine „Büro-Sicherheitsunterweisung“ erfüllt den §4 bei einem Dachdecker nicht).
  • ArbSchG §12 spiegelt das staatlich-arbeitsrechtlich: Unterweisung in „ausreichender und angemessener Form“, nachweispflichtig.
  • ArbSchG §5 + §6 verlangen eine Gefährdungsbeurteilung pro Tätigkeit/Arbeitsplatz, schriftlich dokumentiert, fortgeschrieben, mit Unterschrift der verantwortlichen Person. Für Baustellen heißt das: pro Baustelle, nicht pro Firma.
  • PSA-Benutzungsverordnung §2: Arbeitgeber muss PSA bereitstellen und sicherstellen, dass sie benutzt wird. Benutzungs-Nachweis ist ein weicher Punkt — aber spätestens nach einem Unfall fragt die BG, und dann ist Papier schlecht.
  • §193 SGB VII: Anzeige des Versicherungsfalls bei der BG sofort, wenn ein Arbeitsunfall zum Tode geführt hat oder mehr als drei Tage Arbeitsunfähigkeit erwarten lässt — spätestens am 3. Werktag nach Kenntnisnahme. Formular: BG BAU F1000 (Online-Formular über serviceportal.bgbau.de), parallel verfügbar als PDF-Import.
  • DGUV Vorschrift 1 §26: bestellte Ersthelfer-Quote — in Betrieben mit 2–20 Versicherten mindestens ein Ersthelfer, über 20 Versicherten mindestens 10 % (Verwaltungs-/Handelsbetriebe 5 %). Bauhandwerk fällt in die 10 %-Quote.

Heute bedeutet das in den Zielbetrieben: Der SiFa hat ein Ordner mit Unterweisungs-Unterschriften, keiner weiß verlässlich, wer im Dezember 2025 unterwiesen wurde, wer diese Woche schon überfällig ist. Die BG-Meldung F1000 wird per Kugelschreiber ausgefüllt, gescannt, per E-Mail an die BG geschickt — oft nach der 3-Werktage-Frist, weil der Monteur erst am Donnerstag nachkommt und der Unfall am Freitag war. Auf der Baustelle selbst macht keiner einen PSA-Check, weil der Check keine wirksame Konsequenz hat: wer keinen Helm hat, arbeitet trotzdem.

Schmerzpunkt der Alt-App. Die Alt-App (zeitapp) hat das Thema komplett nicht adressiert. In den ~69 Screens fand sich kein einziger mit Bezug auf DGUV / BG / ArbSchG — obwohl sich die Zielbranche jede Woche daran messen lassen muss. Für ein Feature-Portfolio, das „Whistleblower-Portal“ proaktiv aktivierte, aber Pflicht-Unterweisung nicht lieferte, war die Abstimmung mit dem Markt bereits verloren (vgl. LESSONS-LEARNED §5). Daraus die Schärfung:

  1. Arbeitsschutz ist kein Add-on, sondern Kern-Handwerk-V1.5, damit der Design-Partner SHK-Schmidt seine nächste BG-Meldung binnen 72 Stunden digital liefern kann.
  2. PSA-Check ist ein Gate, keine Info-Meldung — ohne Check keine Zeit-Stempelung an Gewerke-Typ „Abbruch / Dach / Gerüst“. Konsequenz vor Komfort.
  3. Unterweisungsnachweis ist Append-only + Hash-Chain + Signatur, nicht nur ein PDF-Anhang an einen Stammdaten-Satz.
  4. Unfall-Workflow bedient F1000 direkt, nicht „wir exportieren was, was du ausfüllen kannst“ — die Felder müssen im F1000-Format rausgehen, mit Fristen-Überwachung.

Erwarteter Outcome (messbar am Referenzkunden SHK-Gebrüder-Schmidt, 80 MA):

KPI Heute Ziel nach 90 Tagen Werkszeit
Anteil der MA mit gültiger Jahresunterweisung ~65 % (Schätzung SiFa) >98 %
BG-F1000-Frist-Einhaltung (≤3 Werktage) ~60 % 100 %
PSA-Compliance auf Abbruch-/Dach-Baustellen (Stichprobe) nicht gemessen ≥95 % (objektiv über PSA-Check)
Manuelle SiFa-Stunden pro Monat (Listenpflege) 8–12 h <2 h

Rolle Aktion Scope Plattform
Mitarbeiter Unterweisung absolvieren, PSA-Check machen, Unfall melden (eigenen/Kollegen) own 📱
Vorarbeiter / Polier PSA-Check-Übersicht vor Schichtbeginn, Unfall-Ersterfassung für Team team 📱
SiFa (Sicherheitsfachkraft, ASiG §5) Unterweisungs-Inhalte pflegen, Gefährdungsbeurteilung erstellen, BG-Meldung finalisieren, Ersthelfer-Quote sichern all 🌐 (📱 nur Erfassung Gefährdungsbeurteilung auf Baustelle)
Ersthelfer (bestellt, DGUV V1 §26) Unfall-Erstversorgung dokumentieren (Verbandbuch-Eintrag) team 📱
Manager Status-Dashboard Tenant, Eskalation bei überfälliger Unterweisung team/all 🌐
HR Bestellungen (Ersthelfer, Sicherheitsbeauftragter) verwalten, PDF-Bescheinigungen all 🌐
BG (extern, DAV-Schnittstelle) F1000 empfangen — Werkszeit ist Client, nicht Server extern

Persona-Skizzen:

  • Mehmet Yılmaz (Maler-Helfer, 24, Tenant musterbetrieb-maler) — steigt Leitern, Absturzrisiko ab 2 m. Spricht Türkisch-Deutsch, Deutsch-Lesekompetenz B1. Unterweisung Absturzsicherung Leitern fällig am 18.04.2026; Fälligkeits-Push kommt am 18., 11., 4., 1. Tag vorher. Er öffnet die App morgens um 07:08 im Auto, spielt 6-min-Video ab, beantwortet 4 Quiz-Fragen, unterschreibt auf dem Phone.
  • Thomas Schmidt (SiFa, Tenant shk-gebruder-schmidt) — bestellte Sicherheitsfachkraft nach ASiG §5, 60 % Stelle. Pflegt Unterweisungs-Inhalte im Web, erstellt Gefährdungsbeurteilung pro Baustelle auf iPad vor Ort. Hat eine persönliche Haftung — wenn er einen Mitarbeiter ohne gültige Unterweisung auf eine Leiter lässt, trägt er mit.
  • Frau Brehm (Ersthelferin, Sekretärin, Tenant musterbetrieb-maler) — DGUV-Lehrgang gemacht, bekommt bei jedem Unfall automatisch Push zur Mitunterschrift des Verbandbuch-Eintrags.
  • Hannes Krüger (Monteur, 38, Tenant musterbetrieb-maler) — stürzt von der Leiter, 4 Tage AU. Unfall-Meldung wird von Polier ausgelöst, Hannes signiert nachträglich seine Zeugenaussage.

US-01 [V1.5] Als Mitarbeiter möchte ich meine Jahresunterweisung als Video +
4 Quizfragen in der App absolvieren (6–10 min), damit ich nicht
zum Schulungstermin ins Büro reisen muss und trotzdem DGUV V1 §4
erfüllt ist.
US-02 [V1.5] Als Mitarbeiter möchte ich vor Schichtbeginn in 10 Sekunden einen
PSA-Check abhaken (Helm, Schuhe, Warnweste, je nach Tätigkeit),
damit der Polier sieht, dass ich einsatzbereit bin.
US-03 [V1.5] Als Polier möchte ich einen Arbeitsunfall direkt auf der Baustelle
erfassen (Fotos, Zeugen, GPS, Verletzungsart), damit die BG-Meldung
binnen der 3-Werktage-Frist rausgehen kann.
US-04 [V1.5] Als SiFa möchte ich eine Gefährdungsbeurteilung pro Baustelle
strukturiert erstellen (Gewerke, Gefährdungen, Schutzmaßnahmen,
Restrisiko) und unterschreiben, damit ArbSchG §5/§6 belegbar sind.
US-05 [V1.5] Als SiFa möchte ich im Dashboard sofort sehen, welche Mitarbeiter
in den nächsten 30/14/7 Tagen unterwiesen werden müssen und wer
bereits überfällig ist, damit niemand durchs Netz fällt.
US-06 [V1.5] Als Manager möchte ich ab dem Moment, in dem ein Monteur mit
überfälliger Unterweisung in einen Dienstplan kommt, eine harte
Blockade sehen (nicht nur einen Hinweis), damit Haftung klar ist.
US-07 [V1.5] Als SiFa möchte ich die BG-BAU F1000-Meldung aus der Unfall-Erfassung
auto-generieren und direkt an die BG übermitteln (oder als PDF zum
Upload ins Serviceportal herunterladen), damit ich nicht doppelt
tippe.
US-08 [V2] Als HR möchte ich die Ersthelfer-Quote pro Tenant tracken und
einen Hinweis bekommen, wenn wir unter 10 % fallen (DGUV V1 §26),
damit wir rechtzeitig einen neuen Ersthelfer ausbilden lassen.
US-09 [V2] Als Mitarbeiter mit Sprache != Deutsch möchte ich die Unterweisung
in meiner Muttersprache (TR/PL/RO) erhalten, weil eine Unterweisung,
die ich nicht verstehe, rechtlich keine Unterweisung ist (BAG-Rechtsprechung
zu Sprach-Barrieren in DGUV-Unterweisungen).

  • F-M-01 Unterweisung abspielen. Video (MP4/HLS, bis 15 min) + 3–8 Quiz-Fragen (Single/Multiple Choice). Mindestens 80 % Video angeschaut + ≥75 % Quiz-richtig sind Pflicht — sonst keine Abschluss-Signatur. Abspielzeit wird serverseitig via segment_watched_seconds-Heartbeats validiert, damit der Mitarbeiter nicht „skippen“ kann (Anti-Klick-Mechanik).
  • F-M-02 Unterweisungs-Signatur. Abschluss per Touch-Signatur oder 4-stelligem PIN (aus E-Mail-Link für Betriebe ohne Signatur-Kultur). Signatur wird mit tenant_id, user_id, unterweisung_id, signed_at_utc, device_id, video_completion_pct, quiz_score gebündelt SHA-256-gehasht und als Hash-Chain-Nachfolger in unterweisungs_nachweise gespeichert.
  • F-M-03 Sprachwahl Unterweisung. Wenn das Tenant-Profil mehrsprachige Inhalte hinterlegt hat, bietet die App die vom Mitarbeiter im Profil gewählte Sprache (DE/EN/TR/PL/RO) an. Ohne hinterlegte Übersetzung: DE mit Warn-Banner „keine Übersetzung verfügbar — bitte bei SiFa melden, falls Verständnis unklar“. Rechtliche Grundlage: BAG 9 AZR 475/19 zur Sprach-Barriere.
  • F-M-04 PSA-Check vor Schichtbeginn. Checkliste pro Tätigkeits-Profil:
    • Profil „Maler-Helfer Leiter“: Helm ☐, Sicherheitsschuhe S3 ☐, Warnweste ☐, Schutzbrille ☐.
    • Profil „Dachdecker“: + Auffanggurt ☐, Helm mit Kinnriemen ☐.
    • Profil „Abbruch“: + Atemschutz FFP3 ☐, Gehörschutz ☐. Jedes ☐ hat drei Zustände: ✓ (OK), ✕ (fehlt), ⚠ (beschädigt — öffnet Foto-Pflichtfeld „Beleg“). PSA-Check wird pro Schicht maximal einmal erfragt (Cache 12 h), bei Baustellen-Wechsel erneut.
  • F-M-05 PSA-Check-Gate. Bei unvollständigem PSA-Check (mindestens ein ✕) wird der Stempel-Button blockiert für Gewerke-Profile mit Pflicht-PSA. Alternativ: Mitarbeiter wählt „PSA verweigert — Grund“ (freier Text), was einen Audit-Eintrag psa.denied und eine Push-Benachrichtigung an den Polier erzeugt. Der Stempel bleibt gesperrt.
  • F-M-06 Arbeitsunfall melden. Pflichtfelder auf Mobile:
    • Verletzte Person (eigene Auswahl oder Kollege aus Team-Liste),
    • Zeitpunkt (Datum+Uhrzeit, default now()),
    • Unfallort (GPS + Baustellen-Auswahl, default nächste Baustelle),
    • Verletzungsart (Multi-Select nach DGUV-Schlüssel: Schnitt, Quetschung, Fraktur, Sturz, Verätzung, Verbrennung, Elektro, Sonstiges),
    • Körperteil (DGUV-Diagramm, Tap auf Silhouette),
    • Hergang (Freitext 200–2000 Zeichen, Voice-Dictation verfügbar),
    • Zeugen (Multi-Select MA + Freitext-Name externe Zeugen),
    • Fotos 0–12 (Geo-Stempel-Pipeline aus handwerk/01-bautagebuch),
    • Erwartete AU-Dauer (0 / 1–3 Tage / >3 Tage / tödlich). Bei >3 Tage oder tödlich wird ein Countdown-Banner aktiv: „BG-Meldung muss bis [Datum] 23:59 übermittelt werden (§193 SGB VII)“.
  • F-M-07 Verbandbuch-Eintrag. Separater, leichter Flow für kleine Vorfälle ohne Meldepflicht (DGUV-Empfehlung: Verbandbuch 5 Jahre aufbewahren). Felder: Datum, Person, Art der Erste-Hilfe-Leistung, Ersthelfer. Keine Fotos, keine BG-Pflicht — aber append-only und hash-verkettet.
  • F-M-08 Gefährdungsbeurteilung vor Ort. SiFa oder Bauleiter kann auf der Baustelle eine Gefährdungsbeurteilung nach ArbSchG §5 starten: Tätigkeiten auswählen, zugehörige Gefährdungen aus TRBS/BGI-Katalog filtern, Schutzmaßnahmen aus Standard-Katalog oder Freitext, Restrisiko (niedrig/mittel/hoch). Abschluss per Signatur der verantwortlichen Person (SiFa oder Vorgesetzter).
  • F-M-09 Offline-Profil. Voll-offline für Unterweisungs-Videos (vorab geladen in Kolonne downloaded_for_offline bei WLAN-Verbindung, 7-Tage-Frische-Check), PSA-Check, Unfall-Erfassung (inkl. Fotos in App-Sandbox), Gefährdungsbeurteilung. Sync-Priorität: Unfall > PSA-Verweigerung > Unterweisungs-Signatur > Gefährdungsbeurteilung > Verbandbuch.
  • F-W-01 SiFa-Fälligkeits-Dashboard. Tabelle aller MA × Unterweisungs-Themen mit Ampel: 🟢 >90 Tage gültig, 🟡 30–90 Tage, 🔴 ≤30 Tage oder abgelaufen, ⚫ noch nie absolviert. Filter: Themen-Typ, Team/Kolonne, Baustelle, Status. Bulk-Aktion „Jahresunterweisung 2026 zuweisen“ (Batch-Push an ausgewählte MA).
  • F-W-02 Unterweisungs-Content-Verwaltung. CRUD für Themen: Titel, Beschreibung, Video-Upload (S3, direkt), Quiz-Fragen (JSON-Struktur), Gültigkeits-Intervall (Default 12 Monate, konfigurierbar — etwa Staplerschein DGUV G25 ebenfalls jährlich, Ersthelfer 2 Jahre), Ziel-Gruppen (Tätigkeits-Profil). Themen-Katalog kommt mit 24 vorbereiteten DGUV-211-005-Standard-Unterweisungen out-of-box (Absturzsicherung, Elektro, Leitern/Tritte, Gefahrstoff, Gerüst, PSA, Brandschutz, Erste Hilfe, Lärm, Hautschutz, …).
  • F-W-03 Gefährdungsbeurteilung-Register. Pro Baustelle: alle Beurteilungen chronologisch, PDF-Export, Fortschreibungs-Pflicht-Check (ältere als 12 Monate werden rot markiert).
  • F-W-04 BG-Unfall-Akte und F1000-Export. Pro Unfall: alle Felder, Fotos, Zeugen, Audit-Trail. Aktion „F1000 generieren“: Werkszeit erstellt das PDF nach aktuellem BG-BAU-F1000-Layout (PDF/A-2b, ausfüllbar); optional Direkt-Upload ins BG-Serviceportal via bgbau_api-Adapter (falls BG-BAU-API-Zugang freigeschaltet — heute noch manuell). Frist-Ticker an oberster Stelle.
  • F-W-05 Ersthelfer-Register + Quoten-Check. Liste aller bestellten Ersthelfer pro Tenant mit Lehrgangs-Datum, Gültigkeit (2 Jahre nach DGUV), Zuordnung Team/Filiale. KPI-Kachel „Ist-Quote 8,7 % / Soll 10,0 % — Unterdeckung“.
  • F-W-06 SiFa-Bestellung-PDF. Generiert Bestellungsurkunde nach ASiG §5 für den SiFa (oder externen Dienstleister), versioniert.
  • F-W-07 Monatsabschluss-Arbeitsschutz. PDF-Report für die Geschäftsführung: Unterweisungs-Quote, offene Gefährdungsbeurteilungen, Unfälle mit AU-Tagen, Near-Misses, Ersthelfer-Quote.
  • F-X-01 Append-only mit Hash-Chain. unterweisungs_nachweise, unfaelle, psa_checks, gefaehrdungsbeurteilungen, verbandbuch_eintraege sind alle append-only mit hash_prev/hash_self. Korrektur = neuer Eintrag mit corrects_id + Begründung ≥ 20 Zeichen.
  • F-X-02 Status-Maschinen.
    • Unterweisung: zugewiesen → gestartet → video_abgeschlossen → quiz_bestanden → signiert → guelt_bis_<datum> (auto-abgelaufen via Scheduler).
    • Unfall: erfasst → ersthilfe_geleistet → an_bg_gemeldet → bg_bestaetigt → abgeschlossen; paralleler Flag meldepflichtig (>3 AU-Tage oder tödlich).
    • PSA-Check: offen → bestaetigt oder verweigert (kein Rückwärts-Übergang).
    • Gefährdungsbeurteilung: entwurf → signiert → aktiv → veraltet (nach 12 Monaten auto-veraltet).
  • F-X-03 Cross-Link Dienstplanung. Der Planer-Web-UI (kern/03-dienstplanung) liest aus unterweisungs_fristen_view, ob der MA eine gültige, zur Tätigkeit passende Unterweisung hat. Blockade bei Fehlen (hart) oder Ablauf in <7 Tagen (weich, mit Override-Button; Override erzeugt Audit-Eintrag planung.override.unterweisungsfrist).
  • F-X-04 Push-Scheduler. 90/30/14/7/1-Tage-Erinnerungs-Push vor Ablauf einer Unterweisung, 3/2/1-Tage-Ticker vor BG-Meldefrist, sofort-Push bei PSA-Verweigerung im Team an den Polier.
  • F-A-01 Tätigkeits-Profile. Admin definiert welche Rolle (Maler-Helfer, Dachdecker, SHK-Monteur, Abbruch-Facharbeiter, Elektriker) welche PSA-Items erfordert und welche Unterweisungen pflicht sind.
  • F-A-02 Unterweisungs-Intervalle. Default 12 Monate; Ausnahme-Intervalle konfigurierbar (z. B. „Erste Hilfe Auffrischung alle 2 Jahre“, „Gefahrstoff alle 12 Monate“).
  • F-A-03 BR-Freigabe Unterweisungs-Tracking. Default: ein — aber nur „Pflicht-Unterweisung wurde absolviert ja/nein“-Sicht. Detaillierte Video-Seek-Heatmap / Quiz-Antwort-Analyse ist standardmäßig aus (BetrVG §87(1)6) und braucht dokumentierte BR-Zustimmung.
  • F-A-04 BG-Zuordnung. Pro Tenant wird die zuständige Berufsgenossenschaft gepflegt (BG BAU, BG ETEM, BG Holz+Metall — bestimmt Formular-Schema und Meldungs-Endpoint).
  • F-A-05 Gefährdungsbeurteilung-Templates. Admin lädt Tenant-eigene Beurteilungs-Vorlagen hoch (Word → Parsed) oder wählt aus Bibliothek BGI 579, BGI 5088, …
Anforderung-ID MVP V1 V1.5 V2
F-M-01 Unterweisung Player
F-M-04 PSA-Check
F-M-05 PSA-Gate Stempel
F-M-06 Unfall-Erfassung
F-M-07 Verbandbuch
F-M-08 Gefährdungsbeurteilung Mobile
F-W-01 SiFa-Dashboard
F-W-02 Content-CRUD
F-W-04 F1000-PDF-Export
F-W-04 F1000-API-Direktlieferung BG ✅ (bei BG-Zugang)
F-M-03 Mehrsprachige Unterweisung
F-W-05 Ersthelfer-Quoten-Check
KI-Vorschlag Gefährdung (opt-in) ❔ (bei Referenzkunde)

HTML-Hero-Mockup: 10-arbeitssicherheit.html — Mobile + Web Side-by-Side. Mobile-Persona: Mehmet Yılmaz auf Baustelle „Lehrer Allee 7, München“ am 18.04.2026 (Jahresunterweisung „Absturzsicherung Leitern“). Web-Persona: Thomas Schmidt (SiFa) im SHK-Schmidt-Büro am 20.04.2026 mit offener BG-Unfallmeldung für Hannes Krüger.

┌────────────────────────────────┐
│ ← Unterweisung ⚙ │
├────────────────────────────────┤
│ 🛡 DGUV V1 §4 · GoBD · DSGVO │
├────────────────────────────────┤
│ ABSTURZSICHERUNG LEITERN 🔴 │
│ Jahres-Unterweisung 2026 │
│ Fällig heute · Sa 18.04.2026 │
│ │
│ ╭──────────────────────────╮ │
│ │ │ │
│ │ ▶ 6:42 min │ │
│ │ │ │
│ │ ▓▓▓▓▓▓░░░░░░░░░░░░ 38%│ │
│ ╰──────────────────────────╯ │
│ │
│ Fortschritt: 38 % (mind. 80 %) │
│ Sprache: Deutsch ▼ │
│ │
│ Quiz kommt nach Video │
│ ☐ Frage 1 / 4 │
│ ☐ Frage 2 / 4 │
│ ☐ Frage 3 / 4 │
│ ☐ Frage 4 / 4 │
│ │
│ ┌────────────────────────────┐ │
│ │ Video pausieren (speichert │ │
│ │ Fortschritt offline) │ │
│ └────────────────────────────┘ │
├────────────────────────────────┤
│ [Zeit] [Plan] [Doku] [Mehr] │
└────────────────────────────────┘
┌────────────────────────────────┐
│ ← Unterweisung ✓ │
├────────────────────────────────┤
│ Video abgeschlossen (92 %) ✅ │
│ Quiz bestanden (4/4) ✅ │
│ │
│ FRAGE 4 / 4 │
│ Ab welcher Absturzhöhe ist │
│ PSA gegen Absturz Pflicht? │
│ │
│ ( ) 1,00 m │
│ ( ) 2,00 m │
│ (•) 3,00 m │ ← richtig
│ ( ) 5,00 m │
│ │
│ ✅ Richtig (ArbStättV Anh. 2.1)│
│ │
│ SIGNATUR-NACHWEIS 🔴 │
│ Ich bestätige, die Unterweisung│
│ vollständig erhalten zu haben. │
│ │
│ Name: Mehmet Yılmaz │
│ Rolle: Maler-Helfer │
│ Datum: Sa 18.04.2026 · 07:18 │
│ │
│ ╭───────────────────────────╮ │
│ │ M̳. Y̳ı̳l̳m̳a̳z̳ │ │
│ │ │ │
│ ╰───────────────────────────╯ │
│ [✕ leeren] │
│ │
│ ┌────────────────────────────┐ │
│ │ UNTERWEISUNG ABSCHLIESSEN│ │ ← Primary
│ └────────────────────────────┘ │
│ │
│ Gültig bis: So 18.04.2027 │
└────────────────────────────────┘
┌────────────────────────────────┐
│ ← PSA-Check vor Schicht │
├────────────────────────────────┤
│ 🛡 PSA-BV · DGUV V1 §30 │
├────────────────────────────────┤
│ Baustelle: Lehrer Allee 7 │
│ Profil: Maler-Helfer Leiter │
│ Datum: Mo 20.04.2026 06:58 │
│ │
│ PFLICHT-PSA │
│ ┌────────────────────────────┐ │
│ │ 🪖 Schutzhelm │ │
│ │ (•) ✓ ( ) ✕ ( ) ⚠ │ │
│ ├────────────────────────────┤ │
│ │ 👟 Sicherheitsschuhe S3 │ │
│ │ (•) ✓ ( ) ✕ ( ) ⚠ │ │
│ ├────────────────────────────┤ │
│ │ 🦺 Warnweste │ │
│ │ ( ) ✓ (•) ✕ ( ) ⚠ │ │
│ │ 🔴 Fehlt — Grund: │ │
│ │ ▼ im Bus vergessen │ │
│ ├────────────────────────────┤ │
│ │ 🥽 Schutzbrille │ │
│ │ (•) ✓ ( ) ✕ ( ) ⚠ │ │
│ └────────────────────────────┘ │
│ │
│ 🔴 PSA unvollständig │
│ Schichtbeginn-Stempel blockiert│
│ bis Warnweste vorhanden ODER │
│ Polier die Abweichung freigibt.│
│ │
│ ┌────────────────────────────┐ │
│ │ POLIER BENACHRICHTIGEN │ │
│ └────────────────────────────┘ │
│ [ Unbewusst weiter (Audit) ] │ ← danger-text
└────────────────────────────────┘
┌────────────────────────────────┐
│ ← Unfall erfassen (2/3) ⋯ │
├────────────────────────────────┤
│ 🛡 §193 SGB VII · DSGVO Art.9 │
├────────────────────────────────┤
│ 🔴 Meldefrist läuft │
│ Di 21.04.2026 · 23:59 │
│ (Unfall Mo 20.04. · 11:42) │
│ │
│ VERLETZTE PERSON 🔴 │
│ ▼ Hannes Krüger │
│ │
│ VERLETZUNGSART 🔴 │
│ [✓ Sturz] [✓ Fraktur] [ ]... │
│ │
│ KÖRPERTEIL 🔴 │
│ ╭─────────╮ │
│ │ ○ │ ← Kopf │
│ │ ─┼─ │ │
│ │ │ │ │
│ │ ─┤─ │ │
│ │ │● │ ← Knöchel rechts │
│ │ ─┘ └─ │ (angetippt) │
│ ╰─────────╯ │
│ │
│ HERGANG 🔴 │
│ ┌────────────────────────────┐ │
│ │ Sturz von Leiter, │ │
│ │ 3. Sprosse, Verdreh. │ │
│ │ Leiter nicht ausreichend │ │
│ │ gesichert (weicher Boden) │ │
│ │ 🎤 │ │
│ └────────────────────────────┘ │
│ │
│ ZEUGEN │
│ [✓ Mehmet Y.] [✓ Frank D.] │
│ │
│ FOTOS (2/12) │
│ ┌──┐┌──┐┌──┐ │
│ │📷││📷││ +│ │
│ └──┘└──┘└──┘ │
│ 🟢 Geo-Stempel aktiv │
│ │
│ ERWARTETE AU 🔴 │
│ ( ) keine ( ) 1–3 Tage │
│ (•) >3 Tage — meldepflichtig! │
│ ( ) tödlich │
│ │
│ ┌────────────────────────────┐ │
│ │ WEITER zu Schritt 3/3 │ │
│ └────────────────────────────┘ │
└────────────────────────────────┘

6.5 Web — SiFa-Dashboard Unterweisungs-Fälligkeit

Abschnitt betitelt „6.5 Web — SiFa-Dashboard Unterweisungs-Fälligkeit“
┌────────────────────────────────────────────────────────────────────────────────┐
│ Werkszeit · shk-gebruder-schmidt Thomas Schmidt · SiFa [▾] │
├────────────────────────────────────────────────────────────────────────────────┤
│ Sidebar │ Arbeitssicherheit · Fälligkeit Unterweisungen Stand 20.04.26 │
│ ───────── │ ───────────────────────────────────────────────────────────── │
│ Zeit │ KPI │
│ Plan │ ┌──────────┬──────────┬──────────┬──────────┐ │
│ Bautage │ │ Gültig │ 🟢 67 │ 🟡 9 │ 🔴 4 │ │
│ Mängel │ │ MA gesamt│ 90 Tage+ │ 30–90 d │ ≤30 d │ │
│ ▶ Safety │ └──────────┴──────────┴──────────┴──────────┘ │
│ Zertif. │ Ersthelfer-Quote: 8,7 % / Soll 10 % 🔴 Unterdeckung │
│ Projekte │ │
│ Audit │ Filter: [Thema ▼ Absturzsicherung] [Team ▼ alle] [🔍 Suche] │
│ │ │
│ │ ┌─────────────┬──────────────┬──────────┬──────────┬───────┐ │
│ │ │ Mitarbeiter │ Thema │ Letzte │ Fällig │ Ampel │ │
│ │ ├─────────────┼──────────────┼──────────┼──────────┼───────┤ │
│ │ │ Yılmaz, M. │ Absturz/Ltr. │ — │ 18.04.26 │ 🔴 │ │
│ │ │ Krüger, H. │ Absturz/Ltr. │ 14.03.25 │ 14.03.26 │ 🔴 │ │
│ │ │ Demir, F. │ Absturz/Ltr. │ 22.05.25 │ 22.05.26 │ 🟡 │ │
│ │ │ Wirth, A. │ Gefahrstoff │ 11.07.25 │ 11.07.26 │ 🟢 │ │
│ │ │ Schmidt, L. │ Erste Hilfe │ 09.04.24 │ 09.04.26 │ 🔴 │ │
│ │ │ (Ersthelferin · Bestellung läuft 09.04.26 aus) │ │ │
│ │ │ Maier, S. │ Brandschutz │ 02.11.25 │ 02.11.26 │ 🟢 │ │
│ │ └─────────────┴──────────────┴──────────┴──────────┴───────┘ │
│ │ │
│ │ [ Bulk · 4 MA „Absturz" zuweisen ] [⤓ PDF-Report] │
│ │ │
└────────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────┐
│ Werkszeit · shk-gebruder-schmidt SiFa Thomas Schmidt [Ausloggen] │
├────────────────────────────────────────────────────────────────────────────────┤
│ ← Unfälle · Hannes Krüger · Unfall #2026-0017 │
│ │
│ 🔴 MELDEFRIST §193 SGB VII · Di 21.04.2026 23:59 · verbleiben 37:18 h │
│ │
│ ┌─ Sachverhalt ─────────────────────────────────────────────────────────────┐ │
│ │ Person: Hannes Krüger, 38, Monteur, Maler-Betrieb │ │
│ │ Zeitpunkt: Mo 20.04.2026 · 11:42 Uhr │ │
│ │ Unfallort: Lehrer Allee 7, 80331 München (Baustelle) │ │
│ │ Verletzung: Sturz · Fraktur · Knöchel rechts │ │
│ │ Hergang: Sturz von Leiter, 3. Sprosse, Verdrehen des Knöchels. │ │
│ │ Leiter nicht ausreichend gesichert (weicher Boden, keine │ │
│ │ Leiterfußverbreiterung). │ │
│ │ Erwartete AU: >3 Tage — meldepflichtig │ │
│ │ Zeugen: Mehmet Yılmaz · Frank Demir │ │
│ │ Ersthelfer: Frau Brehm (Erstversorgung, Rettungsdienst gerufen 11:43) │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Fotos (3) ───────────────────────────────────────────────────────────────┐ │
│ │ [📷] [📷] [📷] │ │
│ │ Geo 48.1374 · 11.5755 · 20.04.26 11:44 · SHA-256 7a8c...ef2 │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ BG-Meldung F1000 ────────────────────────────────────────────────────────┐ │
│ │ Vorschau: BG BAU F1000 (PDF/A-2b, ausfüllbar) │ │
│ │ Status: Entwurf — noch nicht übermittelt │ │
│ │ Felder vorausgefüllt: │ │
│ │ ✓ Unternehmensdaten (aus Tenant-Profil) │ │
│ │ ✓ Versicherte Person (aus User-Profil) │ │
│ │ ✓ Unfalldaten (aus Erfassung) │ │
│ │ ⚠ Frage 4.3 „Maßnahmen zur Verhütung?" — frei formulieren │ │
│ │ │ │
│ │ [ F1000-PDF herunterladen ] [ Direkt an BG BAU senden (Serviceportal) ] │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ Audit-Chain (Hash-verkettet): │
│ 20.04. 11:43 · unfall.erfasst · Thomas Schmidt · 7a8c…ef2 │
│ 20.04. 11:45 · unfall.foto_hinzugefuegt · Thomas Schmidt · b4d1…fe7 │
│ 20.04. 14:02 · unfall.ersthilfe_dokumentiert · Fr. Brehm · 0c9e…4aa │
└────────────────────────────────────────────────────────────────────────────────┘

Pseudo-Drizzle-Schema (Postgres 17 + RLS + pgcrypto).

apps/api/src/db/schema/arbeitssicherheit.ts
export const unterweisungsThemenTable = pgTable('unterweisungs_themen', {
id: uuid('id').primaryKey().defaultRandom(), // UUID v7
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
code: text('code').notNull(), // z.B. 'DGUV-211-005.ABSTURZ'
titel: text('titel').notNull(), // 'Absturzsicherung Leitern'
beschreibung: text('beschreibung'),
videoS3Key: text('video_s3_key'), // S3-Multipart
videoDurationSec:integer('video_duration_sec'),
quizSchema: jsonb('quiz_schema').notNull(), // Array von Frage-Objekten
mindVideoPct: integer('mind_video_pct').notNull().default(80),
mindQuizPct: integer('mind_quiz_pct').notNull().default(75),
intervallMonate: integer('intervall_monate').notNull().default(12),
zielProfileCodes:text('ziel_profile_codes').array().notNull(), // ['maler_helfer_leiter', 'dachdecker']
sprachen: text('sprachen').array().notNull().default(['de']),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
createdBy: uuid('created_by').notNull().references(() => usersTable.id),
archiviertAm: timestamp('archiviert_am', { withTimezone: true }),
}, (t) => ({
tenantCodeIdx: uniqueIndex('unterw_themen_tenant_code_uq').on(t.tenantId, t.code),
}));
export const unterweisungsNachweiseTable = pgTable('unterweisungs_nachweise', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
themaId: uuid('thema_id').notNull().references(() => unterweisungsThemenTable.id),
userId: uuid('user_id').notNull().references(() => usersTable.id),
sprache: text('sprache').notNull(), // 'de' | 'tr' | 'pl' | 'ro'
videoWatchedPct: integer('video_watched_pct').notNull(),
quizScorePct: integer('quiz_score_pct').notNull(),
quizAntwortenJson:jsonb('quiz_antworten_json').notNull(), // nur mit BR-Freigabe F-A-03 gefüllt; sonst nur aggregiertes Score
signedAtUtc: timestamp('signed_at_utc', { withTimezone: true }).notNull(),
signatureBlobId: uuid('signature_blob_id').references(() => blobsTable.id),
signatureKind: signatureKind('signature_kind').notNull(), // 'touch_vector' | 'pin_token'
deviceFingerprint:text('device_fingerprint').notNull(),
gueltigBis: date('gueltig_bis').notNull(), // signedAt + intervall_monate
status: unterweisungsStatus('status').notNull(),
// 'zugewiesen' | 'gestartet' | 'video_abgeschlossen' | 'quiz_bestanden' | 'signiert' | 'abgelaufen'
hashPrev: bytea('hash_prev'),
hashSelf: bytea('hash_self').notNull(),
idempotencyKey: uuid('idempotency_key').notNull().unique(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
correctsId: uuid('corrects_id').references(() => unterweisungsNachweiseTable.id),
correctionReason:text('correction_reason'),
}, (t) => ({
tenantUserIdx: index('unterw_nachw_tenant_user_idx').on(t.tenantId, t.userId, t.gueltigBis.desc()),
themaUserIdx: index('unterw_nachw_thema_user_idx').on(t.themaId, t.userId, t.signedAtUtc.desc()),
}));
export const psaProfileTable = pgTable('psa_profile', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
code: text('code').notNull(), // 'maler_helfer_leiter'
titel: text('titel').notNull(),
pflichtItems: jsonb('pflicht_items').notNull(), // [{code:'helm',label:'Schutzhelm'}, …]
}, (t) => ({
tenantCodeIdx: uniqueIndex('psa_profile_tenant_code_uq').on(t.tenantId, t.code),
}));
export const psaChecksTable = pgTable('psa_checks', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
userId: uuid('user_id').notNull().references(() => usersTable.id),
baustelleId: uuid('baustelle_id').notNull().references(() => baustellenTable.id),
profilId: uuid('profil_id').notNull().references(() => psaProfileTable.id),
itemsState: jsonb('items_state').notNull(), // [{code:'helm',state:'ok'|'fehlt'|'beschaedigt',fotoBlobId?:uuid,grund?:string}]
ergebnis: psaErgebnis('ergebnis').notNull(), // 'bestaetigt' | 'verweigert' | 'bedingt_mit_freigabe'
verweigerungsGrund:text('verweigerungs_grund'),
freigabeDurchUserId:uuid('freigabe_durch_user_id').references(() => usersTable.id),
capturedAtUtc: timestamp('captured_at_utc', { withTimezone: true }).notNull().defaultNow(),
gpsLat: decimal('gps_lat', { precision: 9, scale: 6 }),
gpsLon: decimal('gps_lon', { precision: 9, scale: 6 }),
hashPrev: bytea('hash_prev'),
hashSelf: bytea('hash_self').notNull(),
idempotencyKey: uuid('idempotency_key').notNull().unique(),
}, (t) => ({
tenantUserBsIdx: index('psa_check_tenant_user_bs_idx')
.on(t.tenantId, t.userId, t.baustelleId, t.capturedAtUtc.desc()),
}));
export const unfaelleTable = pgTable('unfaelle', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
laufnummerJahr: text('laufnummer_jahr').notNull(), // '2026-0017'
verletztUserId: uuid('verletzt_user_id').references(() => usersTable.id),
verletztFreitextPerson:text('verletzt_freitext_person'), // wenn externer (Sub/Bauherr)
baustelleId: uuid('baustelle_id').references(() => baustellenTable.id),
unfallAt: timestamp('unfall_at', { withTimezone: true }).notNull(),
gpsLat: decimal('gps_lat', { precision: 9, scale: 6 }),
gpsLon: decimal('gps_lon', { precision: 9, scale: 6 }),
verletzungsArten:text('verletzungs_arten').array().notNull(), // ['sturz','fraktur']
koerperteil: text('koerperteil').notNull(), // 'knoechel_rechts'
hergang: text('hergang').notNull(), // GESUNDHEITSDATEN — siehe §10.4
zeugenUserIds: uuid('zeugen_user_ids').array(),
zeugenFreitext: text('zeugen_freitext'),
erwarteteAuKategorie:auErwartung('erwartete_au_kategorie').notNull(),
// 'keine' | '1_bis_3' | 'ueber_3' | 'toedlich'
meldepflichtig: boolean('meldepflichtig').notNull(), // berechnet aus erwarteteAuKategorie
meldefrist: timestamp('meldefrist', { withTimezone: true }),// unfallAt + 3 Werktage 23:59
bgKey: text('bg_key').notNull(), // 'BG_BAU' | 'BG_ETEM' | 'BG_HM'
bgStatus: bgStatus('bg_status').notNull().default('entwurf'),
// 'entwurf' | 'pdf_generiert' | 'serviceportal_uebermittelt' | 'bg_bestaetigt' | 'abgeschlossen'
bgMeldungAt: timestamp('bg_meldung_at', { withTimezone: true }),
f1000PdfBlobId: uuid('f1000_pdf_blob_id').references(() => blobsTable.id),
ersthelferUserId:uuid('ersthelfer_user_id').references(() => usersTable.id),
ersthilfeProtokoll:text('ersthilfe_protokoll'),
hashPrev: bytea('hash_prev'),
hashSelf: bytea('hash_self').notNull(),
idempotencyKey: uuid('idempotency_key').notNull().unique(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
createdBy: uuid('created_by').notNull().references(() => usersTable.id),
// Gesundheitsdaten-Key: getrenntes KMS-Feld (envelope encryption)
gesundheitsdatenKmsKeyId:text('gesundheitsdaten_kms_key_id').notNull(),
}, (t) => ({
tenantJahrIdx: uniqueIndex('unfaelle_tenant_laufnr_uq').on(t.tenantId, t.laufnummerJahr),
meldefristIdx: index('unfaelle_meldefrist_idx').on(t.meldepflichtig, t.meldefrist),
}));
export const unfallFotosTable = pgTable('unfall_fotos', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
unfallId: uuid('unfall_id').notNull().references(() => unfaelleTable.id),
s3Key: text('s3_key').notNull(), // Object Lock Compliance 10 Jahre
contentSha256: bytea('content_sha256').notNull(),
geoStampSha256: bytea('geo_stamp_sha256').notNull(),
gpsLat: decimal('gps_lat', { precision: 9, scale: 6 }),
gpsLon: decimal('gps_lon', { precision: 9, scale: 6 }),
capturedAtUtc: timestamp('captured_at_utc', { withTimezone: true }).notNull(),
});
export const gefaehrdungsbeurteilungenTable = pgTable('gefaehrdungsbeurteilungen', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
baustelleId: uuid('baustelle_id').notNull().references(() => baustellenTable.id),
taetigkeiten: text('taetigkeiten').array().notNull(),
gefaehrdungen: jsonb('gefaehrdungen').notNull(), // [{code, beschreibung, schutzmassnahmen[], restrisiko}]
erstelltVonSifaId:uuid('erstellt_von_sifa_id').notNull().references(() => usersTable.id),
signedAtUtc: timestamp('signed_at_utc', { withTimezone: true }),
signatureBlobId: uuid('signature_blob_id').references(() => blobsTable.id),
gueltigBis: date('gueltig_bis').notNull(), // signedAt + 12 Monate
status: gbStatus('status').notNull().default('entwurf'),
hashPrev: bytea('hash_prev'),
hashSelf: bytea('hash_self').notNull(),
idempotencyKey: uuid('idempotency_key').notNull().unique(),
}, (t) => ({
tenantBsIdx: index('gb_tenant_bs_idx').on(t.tenantId, t.baustelleId, t.signedAtUtc.desc()),
}));
export const verbandbuchEintraegeTable = pgTable('verbandbuch_eintraege', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
verletztUserId: uuid('verletzt_user_id').references(() => usersTable.id),
zeitpunkt: timestamp('zeitpunkt', { withTimezone: true }).notNull(),
art: text('art').notNull(), // Freitext
ersthelferUserId:uuid('ersthelfer_user_id').references(() => usersTable.id),
hashPrev: bytea('hash_prev'),
hashSelf: bytea('hash_self').notNull(),
});
export const ersthelferBestellungenTable = pgTable('ersthelfer_bestellungen', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
userId: uuid('user_id').notNull().references(() => usersTable.id),
lehrgangDatum: date('lehrgang_datum').notNull(),
gueltigBis: date('gueltig_bis').notNull(), // lehrgangDatum + 2 Jahre
lehrgangAnbieter:text('lehrgang_anbieter'),
urkundeBlobId: uuid('urkunde_blob_id').references(() => blobsTable.id),
});

RLS-Policy-Sketch:

ALTER TABLE unterweisungs_nachweise ENABLE ROW LEVEL SECURITY;
CREATE POLICY unterw_nachw_tenant ON unterweisungs_nachweise
USING (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY unterw_nachw_scope ON unterweisungs_nachweise FOR SELECT
USING (
tenant_id = current_setting('app.tenant_id')::uuid
AND (
current_setting('app.scope') = 'all'
OR (current_setting('app.scope') = 'team'
AND user_id IN (SELECT uid FROM user_team_membership
WHERE manager_id = current_setting('app.user_id')::uuid))
OR (current_setting('app.scope') = 'own' AND user_id = current_setting('app.user_id')::uuid)
)
);
-- Append-only: UPDATE nur auf status (für auto-'abgelaufen'-Übergang). Inhalt eingefroren.
CREATE POLICY unterw_nachw_no_content_update ON unterweisungs_nachweise FOR UPDATE
USING (tenant_id = current_setting('app.tenant_id')::uuid)
WITH CHECK (
video_watched_pct = (SELECT video_watched_pct FROM unterweisungs_nachweise u WHERE u.id = unterweisungs_nachweise.id)
AND quiz_score_pct = (SELECT quiz_score_pct FROM unterweisungs_nachweise u WHERE u.id = unterweisungs_nachweise.id)
AND signed_at_utc = (SELECT signed_at_utc FROM unterweisungs_nachweise u WHERE u.id = unterweisungs_nachweise.id)
);
REVOKE DELETE ON unterweisungs_nachweise FROM PUBLIC;
-- Unfaelle: Gesundheitsdaten → separate Policy, strenger Scope (nur SiFa, HR, Unfall-Eigentümer):
ALTER TABLE unfaelle ENABLE ROW LEVEL SECURITY;
CREATE POLICY unfaelle_scope ON unfaelle FOR SELECT
USING (
tenant_id = current_setting('app.tenant_id')::uuid
AND (
current_setting('app.role') IN ('sifa','hr','admin')
OR verletzt_user_id = current_setting('app.user_id')::uuid
OR current_setting('app.user_id')::uuid = ANY(zeugen_user_ids)
)
);
REVOKE DELETE ON unfaelle FROM PUBLIC;

ER-Bezug. Referenzierte Tabellen: tenants, users, baustellen, blobs, audit_log, teams, und Views user_team_membership, unterweisungs_fristen_view (Aggregation für Dashboard und Dienstplanung-Gate).


Pfad-Konvention: /v1/handwerk/arbeitssicherheit/.... Alle hinter Hono-Modul-Gate module.handwerk.arbeitssicherheit.

Methode Pfad Auth-Scope Rate-Limit Idempotenz Beschreibung
GET /v1/handwerk/arbeitssicherheit/unterweisungen/faellig safety:read:<scope> Standard Fälligkeiten für Dashboard / eigene Sicht
POST /v1/handwerk/arbeitssicherheit/unterweisungen/{themaId}/start safety:write:own Standard Pflicht Unterweisung starten
POST /v1/handwerk/arbeitssicherheit/unterweisungen/{themaId}/progress safety:write:own Hoch Pflicht (Heartbeat-Key) Video-Fortschritts-Heartbeat
POST /v1/handwerk/arbeitssicherheit/unterweisungen/{themaId}/quiz safety:write:own Standard Pflicht Quiz-Antworten submitten
POST /v1/handwerk/arbeitssicherheit/unterweisungen/{themaId}/sign safety:write:own Privileged Pflicht Signatur → Nachweis final
POST /v1/handwerk/arbeitssicherheit/psa-checks safety:write:own Standard Pflicht PSA-Check anlegen
POST /v1/handwerk/arbeitssicherheit/unfaelle safety:write Standard Pflicht Unfall erfassen
PATCH /v1/handwerk/arbeitssicherheit/unfaelle/{id} safety:write Standard Pflicht Ergänzen (erzeugt Korrektur-Eintrag)
POST /v1/handwerk/arbeitssicherheit/unfaelle/{id}/fotos/presign safety:write Standard Pflicht Pre-Signed URL
POST /v1/handwerk/arbeitssicherheit/unfaelle/{id}/f1000/pdf safety:approve Heavy Pflicht F1000-PDF erzeugen
POST /v1/handwerk/arbeitssicherheit/unfaelle/{id}/f1000/submit safety:approve Heavy Pflicht Direkt-Upload BG-Serviceportal
POST /v1/handwerk/arbeitssicherheit/gefaehrdungsbeurteilungen safety:write:team Standard Pflicht Gefährdungsbeurteilung anlegen
POST /v1/handwerk/arbeitssicherheit/gefaehrdungsbeurteilungen/{id}/sign safety:approve Privileged Pflicht Signatur SiFa
GET /v1/handwerk/arbeitssicherheit/ersthelfer/quote safety:read:all Standard Quoten-KPI
POST /v1/handwerk/arbeitssicherheit/themen safety:admin Standard Pflicht Unterweisungs-Thema anlegen (Admin)

OpenAPI-Schema-Skizze (gekürzt):

paths:
/v1/handwerk/arbeitssicherheit/unfaelle:
post:
operationId: createUnfall
x-werkszeit-scope: safety:write
x-werkszeit-module-gate: module.handwerk.arbeitssicherheit
x-werkszeit-rate-limit: standard
x-werkszeit-compliance: [gobd, dsgvo-art9, sgb-vii-193]
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/UnfallCreateInput' }
responses:
'201': { content: { application/json: { schema: { $ref: '#/components/schemas/Unfall' } } } }
'409': { description: 'Idempotency conflict' }
components:
schemas:
UnfallCreateInput:
type: object
required: [idempotencyKey, unfallAt, verletzungsArten, koerperteil, hergang, erwarteteAuKategorie]
properties:
idempotencyKey: { type: string, format: uuid }
verletztUserId: { type: string, format: uuid, nullable: true }
baustelleId: { type: string, format: uuid, nullable: true }
unfallAt: { type: string, format: date-time }
verletzungsArten: { type: array, items: { type: string, enum: [sturz,schnitt,quetschung,fraktur,veraetzung,verbrennung,elektro,sonstiges] } }
koerperteil: { type: string }
hergang: { type: string, minLength: 200, maxLength: 2000 }
erwarteteAuKategorie: { type: string, enum: [keine, 1_bis_3, ueber_3, toedlich] }

Webhook-Events (AsyncAPI 3.0):

  • safety.unterweisung.signed
  • safety.unterweisung.abgelaufen (Scheduler)
  • safety.psa.denied
  • safety.psa.override_granted
  • safety.unfall.erfasst
  • safety.unfall.meldefrist_droht (T-48h vor Frist, wenn bg_status != 'serviceportal_uebermittelt')
  • safety.unfall.bg_meldung_uebermittelt
  • safety.ersthelfer.quote_unterdeckung
  • safety.gefaehrdungsbeurteilung.signed

Profil Begründung Mechanik
Voll-offline (alle Flows außer F1000-Submit) Unterweisungen müssen auf Baustellen ohne LTE abschließbar sein. Ein Unfall passiert dort, wo gerade kein Empfang ist. PSA-Check muss morgens in der Tiefgarage funktionieren. Outbox-Pattern mit UUID-v7-Idempotency-Key. Videos werden bei WLAN (z. B. abends im Betriebsparkplatz) in App-Sandbox vorgeladen (downloaded_for_offline, 7-Tage-Frische). Fotos aus Unfall gehen über S3-Multipart nach presigned URL.
Online-only für F1000-Direkt-Submit an BG BG-Serviceportal ist Online-Service, keine Queue möglich (Bestätigung zurück an Tenant). UI blockiert submit-Button bei connectivity == none mit Banner „PDF-Export möglich, Direkt-Submit braucht Netz“.

Konflikt-Strategie. Append-only — Korrektur = neuer Eintrag mit corrects_id. Einzige Ausnahme: PSA-Check-Override durch Polier („Abweichung freigegeben“) wird als separater Status-Eintrag gespeichert, der auf den ursprünglichen Check verweist, nicht diesen überschreibt.

Foto-Sync. S3-Multipart, identisch zu handwerk/01-bautagebuch §9. Unfall-Fotos haben zusätzlich KMS-Envelope-Encryption auf S3-Objekt-Ebene (Gesundheitsdaten-Kontext), separater KMS-Key pro Tenant (gesundheitsdatenKmsKeyId).

Unterweisungs-Videos offline. Flutter lädt bei WLAN-Detektion und explizitem User-Opt-in („Videos für Offline laden?“) alle fälligen Themen herunter. Cache wird nach 7 Tagen als „möglicherweise veraltet” markiert, nach 30 Tagen gelöscht — neue Version muss über Netz geholt werden, weil SiFa sonst nicht sicher sein kann, dass der Mitarbeiter die aktuelle Version gesehen hat (Audit-Anforderung DGUV V1 §4).


  • Append-only-Tabelle. Alle Kern-Tabellen (unterweisungs_nachweise, psa_checks, unfaelle, gefaehrdungsbeurteilungen, verbandbuch_eintraege) mit hash_prev / hash_self. Hash ist SHA-256 über kanonisierte Feld-Reihenfolge einschließlich aller Foreign-Key-Hashes (Signatur-Blob, Foto-Hashes).
  • Aufbewahrung. 10 Jahre gem. §147 AO, Fotos in S3 Object Lock Compliance Mode. Unterweisungsnachweise als Teil der Personalakte — aufbewahrungsrechtlich ebenfalls 10 Jahre (LAG-Rechtsprechung). Gefährdungsbeurteilungen: DGUV empfiehlt „solange Arbeitsbedingungen bestehen + Verjährung (30 Jahre bei Berufskrankheit)“ — Werkszeit hält daher 30 Jahre ab Inaktiv-Setzung der Baustelle.
  • Nachvollziehbarkeit. Jede Mutation → Audit-Log-Eintrag (safety.unterweisung.*, safety.unfall.*, safety.psa.*, safety.gb.*) mit Hash-Chain aus kern/08-audit-log.
  • Verfahrensdokumentation. Textbaustein in gobd-verfahrensdoku.md mit Modul-Abschnitt „Arbeitssicherheit“.

— nicht zutreffend, weil das Feature keine Arbeitszeit erfasst. Die „Dauer der Unterweisung“ zählt arbeitsrechtlich zwar als Arbeitszeit (BAG 6 AZR 519/98), wird aber über die normale Zeiterfassung (kern/01-zeiterfassung) mit der Zeitart „Schulung“ erfasst — nicht im Safety-Modul selbst.

— nicht zutreffend, weil der Unterweisungsnachweis kein Bauvertragsartefakt ist. Lose Kopplung: In der VOB-Abrechnung (handwerk/04-rechnung-vob) kann ein SiFa-Stunden-Satz als Nebenleistung auftauchen, das bleibt aber außerhalb dieses Feinkonzepts.

  • Art. 5 Datenminimierung. Der Unfall-Datensatz enthält besondere Kategorien personenbezogener Daten nach Art. 9 Abs. 1 (Gesundheitsdaten). Werkszeit speichert ausschließlich die für §193 SGB VII und F1000 geforderten Felder. Kein Diagnose-Feld, keine Krankenkassen-Nummer, kein Arzt-Name im Hauptflow (kommt erst im BG-Prozess nach Kenntnisnahme).
  • Art. 6 + Art. 9 Rechtsgrundlage. Art. 9 Abs. 2 lit. b DSGVO (Arbeitsrecht) i. V. m. § 26 BDSG — Erforderlich zur Erfüllung der arbeitsschutzrechtlichen Pflichten des Verantwortlichen. Für Ersthelfer-Dokumentation zusätzlich Art. 9 Abs. 2 lit. c DSGVO (lebenswichtige Interessen).
  • Technisch-organisatorische Maßnahme. Unfall-Hergangs-Text und Fotos werden mit separatem KMS-Key je Tenant verschlüsselt (gesundheitsdatenKmsKeyId), Zugriff nur für Rolle sifa, hr, admin und den Verletzten selbst sowie benannte Zeugen (RLS-Policy). Für normalen Manager: kein Zugriff auf Hergangs-Text — er sieht nur den Status-Rumpf (Person, Datum, AU-Kategorie).
  • Art. 17 Löschung. DSGVO-Löschung wird nicht physisch ausgeführt vor GoBD-10-Jahres-Ende (Unterweisung) bzw. 30 Jahre (Gefährdungsbeurteilung). Stattdessen: Maskierung — personenbezogene Identifikatoren (Name, User-ID, Foto) werden durch Hash-Pseudonyme ersetzt, während Struktur (Datum, Themenbezug, Hash-Chain) erhalten bleibt. Begründung: Art. 17 Abs. 3 lit. b DSGVO (rechtliche Pflicht zur Aufbewahrung).
  • Art. 20 Datenexport. Mitarbeiter kann eigene Unterweisungsnachweise und PSA-Checks als ZIP (JSON + Signatur-Bytes + Video-Verweis) herunterladen. Unfall-Akten nur soweit Person Betroffene/r ist.
  • Art. 30 Verarbeitungsverzeichnis. Separater Eintrag „Unfall- und Gesundheitsdaten“, mit ausdrücklicher Kennzeichnung Art. 9 und TOM-Referenz (KMS-Separation, RLS, Audit).
  • Art. 35 DSFA. Pflicht bei Unfall-Modul nach DSK-Positivliste (Gesundheitsdaten + Beschäftigten-Kontext). Werkszeit liefert DSFA-Template out-of-box, das der Kunde auf sein Verfahren anpasst.

— teilweise zutreffend. Die Unterweisungsplayer-Komponente ist ESS-naher Pflichtflow (jeder Mitarbeiter muss sie bedienen). Entsprechend:

  • Video mit Untertiteln als Pflicht (VTT-Spur hochladen), Tastatur-Steuerbarkeit, Fokus-Ring sichtbar.
  • Quiz in Screenreader-tauglicher Radio-Group-Semantik, Fehler-Meldungen aria-live="assertive".
  • Signatur-Pad hat alternative Text-Eingabe-Fallback („Wenn Touch-Signatur nicht möglich: Namen eingeben + PIN aus E-Mail“) für Assistive-Tech-Benutzer.
  • Kontrast: primäre CTA Orange #FF6B1A auf Anthrazit #1A1F2B = 5.2:1, Pass AA.

Das Modul berührt mittelbare Leistungskontrolle in zwei Punkten:

  1. Video-Seek-Heatmap + Quiz-Antwort-Detail. Daraus ließen sich individuelle Leistungs-Profile ableiten. Default: deaktiviert. Aktivierung nur über Admin-UI mit BR-Beschluss-Upload. Ohne BR-Beschluss wird nur das aggregierte quiz_score_pct gespeichert, quiz_antworten_json bleibt null.
  2. PSA-Verweigerungs-Log. Ist zwar arbeitsschutzrechtlich zwingend (Arbeitgeber muss reagieren), aber Häufung von Verweigerungen einer Person → Persönlichkeits-Profil. Konsequenz: Sichtbarkeit im Manager-Dashboard nur aggregiert; individuelle Auswertung nur über SiFa-Rolle + begründeten Zugriff (Zugriff wird audit-geloggt als safety.psa.details_viewed).

Audit-Eintrag bei Aktivierung der erweiterten Auswertung: safety.feature.br_freigabe_detail_auswertung.activated mit BR-Beschluss-ID, Datum, Uploader-User.

  • DGUV Vorschrift 1 §4 (Grundsätze der Prävention). Jährliche Unterweisung + arbeitsplatzspezifisch → erfüllt durch Kombination unterweisungs_themen.zielProfileCodes und intervallMonate. Fristüberwachung über unterweisungs_fristen_view und Push-Scheduler.
  • DGUV Vorschrift 1 §26 (Ersthelfer). Quote 10 % berechnet im Live-KPI, Unterdeckung triggert Webhook + SiFa-Push.
  • DGUV Information 211-005. Mitgelieferter Themen-Katalog folgt der Themen-Matrix (Absturz, Elektro, Gefahrstoff, Leitern/Tritte, Gerüst, Lärm, Hautschutz, Brandschutz, Erste Hilfe, Lagerung, Hebezeuge, Handwerkzeuge, Maschinen, Verkehrssicherheit, Witterung, Hygiene, Hautschutz, Lärm, Staub, Schweißen, PSA, Hebezeug, Gabelstapler, Abbruch).
  • DGUV Vorschrift 2. SiFa-Bestellung (F-W-06) erzeugt das Bestellungsformular im DGUV-Standard-Layout.
  • §193 SGB VII + §8 Abs. 1 SGB VII. Unfall-Meldefrist: 3 Werktage ab Kenntnisnahme, bei Tod: sofort. Berechnung erfolgt über meldefrist-Feld mit Feiertags-Library (de-NW, de-BY, …).
  • PSA-BV. PSA-Bereitstellungs-Pflicht des Arbeitgebers wird nicht direkt im Modul abgebildet (Inventar-Thematik in V2 Autolager), der PSA-Benutzungs-Nachweis dagegen schon.
  • BG BAU F1000. PDF-Layout wird jährlich aktualisiert (Wartungs-ADR für jährlichen Schema-Check). Direkt-Submit über BG-BAU-API wird geprüft (Schnittstelle steht 2026 offiziell bereit, aber erfordert Vereinbarung) — solange das nicht vorhanden ist, PDF-Upload durch den Nutzer ins BG-Serviceportal.

# Szenario Erwartetes Verhalten
EC-01 Mitarbeiter überspringt Video-Teile, versucht direkt Quiz aufzurufen. Quiz-Endpoint prüft serverseitig video_watched_pct ≥ 80. Wenn nicht: 409 Conflict mit code: video_incomplete, Client zeigt Hinweis „Bitte Video weiter ansehen (noch 12 % fehlen)“.
EC-02 Unterweisung mit abgelaufener Frist wird angefangen. Erlaubt — das System zählt rückwirkend als überfällig bis Abschluss. Dienstplanung-Gate bleibt aktiv, bis gueltigBis in Zukunft liegt. Push-Eskalation an Manager stoppt bei Status gestartet.
EC-03 Unfall-Erfassung offline, Wiederverbindung erst nach 3 Werktagen. Outbox-Eintrag wird mit unfallAt-Zeitstempel eingereicht (nicht now()). Server berechnet meldefrist aus unfallAt. Wenn Frist beim Sync bereits verletzt: Banner „Meldefrist überschritten — bitte mit BG telefonisch abklären“ + Audit-Flag frist_ueberschritten. Das schützt den Mitarbeiter vor Falschdatierung; die juristische Verantwortung bleibt beim Arbeitgeber.
EC-04 PSA-Check verweigert, Polier übersteuert per Override. Override wird eingetragen als separater psa_checks-Datensatz mit Status bedingt_mit_freigabe + freigabe_durch_user_id. Audit-Eintrag. Stempelung wird freigegeben. Manager- und SiFa-Dashboard zeigen Override gelb markiert.
EC-05 Mitarbeiter in Unterweisung, ArbZG 10h-Grenze erreicht. Unterweisung ist Arbeitszeit → kern/01-zeiterfassung warnt gemäß §3 ArbZG. Unterweisung kann trotzdem abgeschlossen werden (Gesundheitsrisiko-gering), aber Zeit-Eintrag „Unterweisung“ wird mit ArbZG-Marker in den Manager-Review gelegt.
EC-06 Multi-Tenant-Bleed-Versuch (SiFa aus Tenant A ruft Unfall aus Tenant B per ID.) API antwortet 404 Not Found (nicht 403, damit Existenz nicht offenbart wird). Audit-Log in Tenant A mit safety.access_denied, keine Spur in Tenant B. RLS-Test ist Pflicht in CI.
EC-07 Unterweisung wird doppelt signiert (Netz-Retry.) Idempotency-Key fängt ab, Server antwortet 200 OK + bestehender Nachweis. Keine Doppel-Zeile.
EC-08 Ersthelferin Frau Brehm hat eigenen Unfall. Zirkel-Fall: Sie kann sich nicht selbst als Ersthelfer eintragen. Mobile-Flow zeigt im Dropdown „Ersthelfer“ alle anderen bestellten Ersthelfer des Tenants. Fallback: Externer Rettungsdienst als Freitext.
EC-09 Mitarbeiter wird während laufender Unterweisung demotet / entlassen. Status wird bei nächstem Login-Check auf abgebrochen gesetzt, laufender Nachweis bleibt im Archiv unvollständig. Kein Block für andere User.
EC-10 BG-BAU-Serviceportal down beim Submit. Submit-Endpoint antwortet 503 Upstream Unavailable. Status bleibt pdf_generiert. PDF-Download bleibt verfügbar. Retry-Scheduler versucht alle 30 min für 24 h, danach Push an SiFa „Manueller Upload notwendig“.
EC-11 Ehemaliger Mitarbeiter fordert DSGVO-Löschung seines Unfall-Datensatzes. Maskierungs-Flow (siehe §10.4). Hergangs-Text bleibt (gerichtsrelevant), aber Personenbezug wird durch Pseudonym ersetzt. Antwort gem. Art. 17 Abs. 3 lit. b mit Verweis auf SGB VII / §147 AO.
EC-12 Sprache nicht verfügbar (Rumäne bekommt deutsche Unterweisung.) Banner „Keine Übersetzung verfügbar — SiFa kontaktieren“. Signatur wird mit Flag sprache_nicht_muttersprache markiert. SiFa-Dashboard zeigt Warnung, weil BAG-Rechtsprechung die Unterweisung ggf. als unwirksam einstuft.

Funktionalität: Arbeitssicherheit — Unterweisung, PSA-Check, Unfallmeldung
Hintergrund:
Angenommen ein Tenant "musterbetrieb-maler" mit aktivem Modul-Gate "module.handwerk.arbeitssicherheit"
Und ein Mitarbeiter "Mehmet Yılmaz" mit Rolle "Mitarbeiter" und Tätigkeits-Profil "maler_helfer_leiter"
Und ein SiFa "Thomas Schmidt" mit Rolle "SiFa" und Scope "all"
Und das Datum ist 2026-04-18
Szenario: Happy Path — Jahresunterweisung Absturzsicherung Leitern
Angenommen für "Mehmet Yılmaz" ist die Unterweisung "DGUV-211-005.ABSTURZ" fällig am 18.04.2026
Wenn Mehmet die Mobile-App öffnet und auf "Unterweisung starten" tippt
Und das 6:42 min Video zu mindestens 80 % anschaut
Und die 4 Quiz-Fragen mit ≥ 75 % richtig beantwortet
Und auf dem Touch-Signatur-Pad unterschreibt
Dann wird ein Eintrag in "unterweisungs_nachweise" erzeugt mit Status "signiert"
Und "gueltig_bis" = 2027-04-18
Und "hash_self" ist SHA-256 der kanonisierten Zeile mit Vorgänger "hash_prev"
Und ein Audit-Log-Eintrag der Art "safety.unterweisung.signed" wird erzeugt
Und die Fälligkeits-Ampel im SiFa-Dashboard wechselt von 🔴 auf 🟢
Szenario: Grenzfall 1 — Frist überschritten und Mitarbeiter eingeplant → Block
Angenommen "Mehmet Yılmaz" hat keine gültige Unterweisung "DGUV-211-005.ABSTURZ"
Und Manager "Sabine Maier" versucht, Mehmet in eine Dachdecker-Schicht am 20.04.2026 einzuplanen
Wenn Sabine im Dienstplan-Editor den Slot zuweist
Dann antwortet die API `/v1/kern/dienstplanung/shifts` mit 422
Und die Fehlermeldung enthält "unterweisung_abgelaufen: DGUV-211-005.ABSTURZ seit 18.04.2026"
Und der Slot wird nicht gespeichert
Und es gibt einen Audit-Eintrag "planung.block.unterweisungsfrist"
Und Sabine kann mit Override und Pflicht-Begründung trotzdem planen
Und der Override wird als "planung.override.unterweisungsfrist" auditiert
Szenario: Grenzfall 2 — Arbeitsunfall mit >3 Tagen AU → BG-Meldung auto-triggered
Angenommen Polier "Frank Demir" erfasst um 20.04.2026 11:50 Uhr einen Unfall für "Hannes Krüger"
Und er wählt Verletzung "Sturz, Fraktur Knöchel rechts"
Und die erwartete AU-Kategorie "> 3 Tage — meldepflichtig"
Wenn der Unfall im Backend ankommt
Dann wird das Feld "meldepflichtig" auf true gesetzt
Und "meldefrist" wird auf den 3. Werktag nach 20.04. gesetzt (23.04.2026 23:59 unter Berücksichtigung der BY-Feiertage)
Und ein Webhook "safety.unfall.erfasst" wird gefeuert
Und ein Countdown-Banner erscheint auf dem SiFa-Dashboard
Und 48 h vor Fristablauf wird `safety.unfall.meldefrist_droht` gefeuert
Und ein Audit-Log-Eintrag der Art "safety.unfall.erfasst" mit Hash-Chain-Verknüpfung wird erzeugt
Szenario: Grenzfall 3 — PSA-Check verweigert → Stempel blockiert
Angenommen "Mehmet Yılmaz" steht am 20.04.2026 um 06:58 Uhr auf der Baustelle "Lehrer Allee 7"
Und er öffnet den PSA-Check für Profil "maler_helfer_leiter"
Und er wählt "Warnweste — fehlt, im Bus vergessen"
Wenn er auf den Start-Stempel-Button tippt
Dann wird der Stempel-Button mit roter Border und Sperr-Icon angezeigt
Und die API `/v1/kern/zeiterfassung/stempel` antwortet 412 "psa_incomplete"
Und ein Audit-Eintrag "safety.psa.denied" wird erzeugt
Und der Polier erhält einen Push mit "Mehmet Y.: Warnweste fehlt"
Und erst nach Override des Poliers oder Behebung wird die Stempelung freigegeben
Szenario: Grenzfall 4 — Multi-Tenant-Isolation Unfall-Akte
Angenommen ein zweiter Tenant "shk-gebruder-schmidt" mit eigenem Unfall #2026-0017
Wenn SiFa Thomas aus Tenant A per ID "unfall_id_aus_b" die Unfall-Detail-API aufruft
Dann antwortet die API mit 404 (nicht 403, um Existenz nicht zu offenbaren)
Und es entsteht ein Audit-Eintrag "safety.access_denied" in Tenant A
Und KEIN Audit-Eintrag in Tenant B
Und der Hergangs-Text aus Tenant B taucht in keinem Log/Trace/Response-Body auf

  • U-01 calculateMeldefrist(unfallAt, bundesland) → timestamp — Property-based gegen 3-Werktage-Regel inkl. Feiertage BY/NW/BW/HE (Schnittstelle: date-holidays).
  • U-02 computeUnterweisungsFaelligkeit(user, thema) → {status, gueltig_bis} — deckt alle Zustandsübergänge ab inkl. „nie absolviert“ vs. „abgelaufen“.
  • U-03 validateVideoCompletion(segments_watched) → pct — Property-based: Ergebnis zwischen 0 und 100, monoton steigend.
  • U-04 isMeldepflichtig(erwartete_au, letal) → bool — Wahrheitstabelle über alle 8 Kombinationen.
  • U-05 canonicalizeRowForHash(row) → bytes — Hash-Stabilität gegen Feld-Reihenfolgen-Permutation.
  • W-01 Quiz-Komponente disabled state, wenn videoWatchedPct < 80. Golden-Test in de-DE + Dark.
  • W-02 Signatur-Pad blockiert Submit bei leerer Canvas. Alternative „PIN-Pad öffnen“ reagiert auf Tap.
  • W-03 Körperteil-Diagramm setzt tap-Punkt in 1 px Genauigkeit; Screenreader liest „Knöchel rechts ausgewählt“.
  • W-04 PSA-Check-Liste — jedes Item hat 3-State-RadioGroup mit aria-required.
  • W-05 Unfall-Meldefrist-Banner ändert Farbe bei <48h, <24h, <6h vor Frist; Golden-Tests je State.

13.3 Integrations-Tests (Backend, Drizzle In-Memory)

Abschnitt betitelt „13.3 Integrations-Tests (Backend, Drizzle In-Memory)“
  • I-01 Multi-Tenant-Isolation. Zwei Tenants mit jeweils Unterweisungen + Unfällen. Cross-Read-Versuch → 404, Audit in eigenem Tenant.
  • I-02 RLS-Scope own. MA sieht eigene Nachweise, nicht die des Kollegen.
  • I-03 Outbox-Idempotenz. Doppelter POST /unfaelle mit gleichem Idempotency-Key → 200 + existierender Datensatz, keine neue Row.
  • I-04 Hash-Chain-Konsistenz. Einfügen von 100 Unterweisungsnachweisen in gemischter Reihenfolge; verify_chain(tenant_id, thema_id) muss true liefern.
  • I-05 Gesundheitsdaten-RLS. Normaler Manager ohne sifa-Rolle bekommt beim Unfall-Read nur Stub-Felder; hergang ist im Response-Body maskiert.

13.4 E2E-Tests (Patrol iOS/Android, Playwright Web)

Abschnitt betitelt „13.4 E2E-Tests (Patrol iOS/Android, Playwright Web)“
  • E-01 Happy Path Unterweisung Mehmet Yılmaz auf iOS + Android + Chromium + Firefox + WebKit, inkl. Video-Heartbeat-Timing.
  • E-02 Offline-Erfassung eines Unfalls: Simulation network off → on nach 6 h; Meldefrist bleibt korrekt berechnet.
  • E-03 PSA-Gate blockiert Stempelung; Override durch Polier gibt frei; beide Events im Audit-Log.
  • E-04 Visuelle Regression: SiFa-Dashboard mit 🟢🟡🔴-Ampel auf de-DE Light+Dark.
  • E-05 Accessibility: axe-core auf /safety/unterweisung/:id ohne Critical; VoiceOver iOS liest Quiz-Fragen inkl. richtiger Antwort-Feedback.
  • C-01 Hash-Chain-Integrität: Mutation einer hergang-Zeile via SQL-Injection-Simulation lässt Chain-Verify fehlschlagen → Detection.
  • C-02 F1000-PDF-Validator: erzeugtes PDF wird gegen BG-BAU-Schema (XFA-Struktur) validiert, alle Pflichtfelder gesetzt.
  • C-03 DSFA-Template-Generator: safety-dsfa.pdf enthält die 9 Kriterien der DSK-Positivliste.
  • C-04 §193-SGB-VII-Fristberechnung: Property-based Test mit Feiertags-Matrix (BY, NW, HE, SN) gegen 200 Stichproben-Daten.
  • C-05 DGUV V1 §26 Quoten-Check: Synthetischer Tenant mit 80 MA und 7 Ersthelfern → 8,75 % → Webhook wird gefeuert.

20× lokaler Re-Run der neuen E2E-Tests grün. Particular Aufmerksamkeit: Video-Heartbeat-Timing (reale Zeitstempel-Drift wird in CI durch Fake-Clock ersetzt).


Nicht-Ziel Begründung
eigenes Learning-Management-System (LMS) mit SCORM-Authoring Werkszeit ist kein Trainings-Produkt. Wer SCORM-Pakete selbst bauen will, nutzt bestehende LMS-Tools (Moodle, rexx, Tutory) und lädt hier nur MP4 + Quiz-JSON hoch. Steht bereits als Nicht-Ziel in FUNKTIONSUMFANG.md §10.
automatische Verblurring von Gesichtern auf Unfall-Fotos Das ist ein KI-Feature mit nicht-trivialer False-Positive-Quote (übersehene Gesichter oder zu aggressiv verpixelte Verletzungsstellen können BG-Beweiswert mindern). Überlassen wir dem Kunden via externer KI-Workflow, bis ein Referenzkunde das konkret fordert.
medizinische Diagnose-Unterstützung / Triage-Vorschläge Kein Medizin-Produkt, kein MDR-konformes Risiko-Management vorgesehen. Ersthelfer macht, was er im DGUV-Kurs gelernt hat; die App dokumentiert nur.
Vollständige Integration Unfall → D-Arzt-Bericht D-Arzt-Prozess läuft zwischen Arzt und BG, nicht über den Arbeitgeber-Kanal. Werkszeit liefert der BG, was dem Arbeitgeber obliegt (F1000).
Automatische Feiertags-Regelungen für Schweiz/Österreich Phase 1 DE-only (FU §10). Bei DE-Tenant mit Schweizer Baustelle gilt Arbeitsort-Recht; aktuell manueller Override.
SiFa-Bestellung an externen Dienstleister mit eSignatur (QES) EES-Signatur reicht für die Bestellung nach ASiG §5 in der Praxis (Einvernehmen + Bestätigung). QES wäre Overkill, wie in FU §10 allgemein begründet.
Automatische Erzeugung einer Unterweisung aus LLM-Kontext Juristisch heikel — eine nicht-validierte Maschinen-Unterweisung erfüllt DGUV V1 §4 nicht zwingend. Kein Referenzkunde fordert das.
Integration in BG-BAU ASi-Portal (Meldung ohne F1000-PDF-Export) Nicht-öffentliche API, Vertrag mit BG BAU nötig. Bei Nachfrage: V2-Roadmap. Bis dahin: PDF + Upload-Link.

Risiko / Annahme Impact Wahrscheinlichkeit Gegenmaßnahme
BG-BAU F1000-Schema ändert sich (BG aktualisiert das Formular 2026 oder 2027) hoch (Tenant kann nicht mehr melden) mittel Jährlicher Schema-Check-ADR, Form als strukturiertes JSON → PDF-Layer, damit nur Template-Datei tauscht. Contract-Test gegen aktuelle BG-BAU-PDF im CI.
Video-Fortschritts-Heartbeat lässt sich clientseitig fälschen (MA-Skript feuert Fake-Progress-Calls) mittel niedrig (wenig Anreiz, hoher Aufwand für MA) Server-seitige Plausibilitätsprüfung: Heartbeats müssen monoton steigen, Delta pro Sekunde ≤1.2× Echtzeit, Gesamtzeit ≥ Video-Länge × 0.8. Anomalie → Audit-Flag unterweisung.heartbeat_anomalie.
DSFA wird bei Roll-out nicht durchgeführt hoch (Aufsichtsbehörde-Risiko) mittel DSFA-Template als Bestandteil des Onboardings, Status „DSFA durchgeführt ja/nein“ ist Pflichtfeld zum Aktivieren des Unfall-Moduls (Soft-Gate, dokumentiert).
Annahme: 80 % Video-Mindest-Watch-Quote ist DGUV-konform hoch mittel Nicht explizit vorgeschrieben; DGUV V1 §4 ist inhaltlich („angemessen, verständlich“). 80 % ist Branchenstandard (vergleichbar mit SCORM „completion“). Dokumentieren in Verfahrensdoku. Alternative: Arbeitgeber kann den Schwellwert konfigurieren (Default 80 %).
Mehrsprachige Unterweisung — Übersetzungs-Qualität liegt beim Kunden mittel hoch Kein Auto-Übersetzen. SiFa lädt geprüfte Version pro Sprache hoch. Warn-Banner „keine Übersetzung verfügbar“ ist Pflicht.
Performance der Foto-Unfall-Pipeline (12 Fotos × 4 MB = 48 MB Upload aus LTE-Loch) mittel mittel Resumable S3-Multipart + Background-Upload mit Foreground-Service auf Android; iOS BGTaskScheduler. 6 Stunden Retry-Fenster.
Manager sieht Gesundheitsdaten — Rollen-Fehler in RLS hoch (DSGVO Art. 9 Verletzung) niedrig Pflicht-CI-Test mit 12 Rollen-Kombinationen gegen gleichen Unfall-Datensatz; Regression bricht Deploy.

  • Vorbedingung: kern/03-projekte-baustellen (für Baustellen-Auswahl), kern/08-audit-log (für Hash-Chain-Audit), kern/09-foto-upload-pipeline, kern/11-dsgvo-toolkit (KMS-Separation Gesundheitsdaten).
  • Schnittstelle zu:
    • kern/01-zeiterfassung — PSA-Check-Gate beim Stempel (check_psa_before_stempel: true in Tenant-Config).
    • kern/03-dienstplanung — Unterweisungs-Fälligkeit blockiert Zuweisung (Cross-Link zu §4.11 Qualifikations-Gate).
    • handwerk/01-bautagebuch — PSA-Check ist eigener Eintragstyp dort.
    • handwerk/11-fuehrerschein-zertifikate — gemeinsame Ampel- und Push-Erinnerungs-Infrastruktur.
  • Wird konsumiert von:
    • kern/07-reporting — Monatsabschluss Arbeitsschutz (Quote, Unfälle, AU-Tage).
    • kern/05-rechnung — SiFa-Stunden als Nebenleistung in VOB-Abrechnung (bei externem SiFa-Vertrag).

Status: TBD — zu klären mit Sales + Produkt-Owner vor Sprint-Einstieg.

DOR §1.1.1 verlangt namentliche Dokumentation eines zahlenden Design-Partners.

Kandidaten-Profile:

  • SHK-Gebrüder-Schmidt (NRW, 80 MA) — Sales-Call 2026-02: „BG-Meldung ist unser Horror, wir verpassen die 3-Tage-Frist regelmäßig, der SiFa will aus der Excel-Hölle raus.“
  • Musterbetrieb Maler (Bayern, 25 MA) — Interview 2026-03: „Ich hätte gern die Jahresunterweisung auf dem Handy, sonst kommen die Leute nicht zum Termin.“
  • Dachdecker Siedlung e. G. (Hessen, 45 MA, erhöhter Absturzrisiko-Kontext) — mögliche Eignung für Dachdecker-spezifische PSA-Profile und Gefährdungsbeurteilung.

Validierungs-Fragen für das Erst-Gespräch:

  1. Wie oft haben Sie in den letzten 24 Monaten einen Unfall an die BG gemeldet? Innerhalb der 3-Werktage-Frist?
  2. Wie pflegen Sie heute den Unterweisungs-Nachweis? (Excel, Ordner, nichts?)
  3. Haben Sie einen bestellten SiFa (intern/extern)? Wer pflegt die Gefährdungsbeurteilungen?
  4. Welche PSA-Profile unterscheiden Sie heute?
  5. Würden Sie 30 Tage gegen Preis-Lock-In im Beta-Modus testen? Wir bauen Ihren BG BAU-F1000-Flow prioritär.

Build-Sequenz (innerhalb des Features):

  1. Datenmodell + RLS + Multi-Tenant-Test zuerst. Besonders Gesundheitsdaten-KMS-Separation muss stehen, bevor eine einzige Zeile Unfall-Code geschrieben wird.
  2. Unterweisungs-Player MVP (Video-Stream + Quiz + Signatur) — das ist der Kern-Nutzen und liefert sofort Wert für den Design-Partner.
  3. PSA-Check + Gate in Zeiterfassung — konkreter operativer Win, „Der Monteur ohne Warnweste wird heute gesperrt“.
  4. SiFa-Dashboard Web + Push-Scheduler — damit der SiFa den Abschluss-Blick hat.
  5. Unfall-Erfassung + F1000-PDF-Generator — schwerster Teil, nicht am Anfang.
  6. F1000-Direkt-Submit an BG BAU — erst wenn API-Vertrag steht.
  7. Mehrsprachige Unterweisung — auf Referenzkunde warten.

Risiko-Reihenfolge (was zuerst absichern, wenn Zeitnot):

  • Multi-Tenant-Isolation + Gesundheitsdaten-RLS sind nicht verhandelbar.
  • Hash-Chain + Append-only müssen von Tag 1 stehen — Nachrüsten ist nahezu unmöglich.
  • Video-Heartbeat-Anti-Skip kann V1.5b sein (als Verfahrensdoku-Maßnahme anfangs akzeptabel).
  • Mehrsprachigkeit, KI-Gefährdungsvorschlag: klar V2.

Stop-the-Bus-Triggers:

  • Gesundheitsdaten im Response-Body eines Non-SiFa-Users — sofort Incident, Pen-Test und Rotation KMS-Key.
  • F1000-PDF wird von BG-BAU-Portal abgelehnt: Schema-Check, hotfix, ADR nachziehen.
  • Hash-Chain-Bruch → Ermittlung ob Datenleck oder Schema-Migrations-Bug; kein Deploy bis geklärt.
  • DSFA ist nicht durchgeführt im Tenant, aber Unfall-Modul wird aktiviert → Soft-Block, Schreiben an Tenant-Admin.

Was uns 2027 dankbar macht:

  • Eine Audit-Hash-Chain auf allen Safety-Tabellen ab Tag 1 — bei der ersten BG-Prüfung oder Aufsichtsbehörde-Anfrage liefern wir in Sekunden nachprüfbare Unveränderlichkeits-Belege statt „Bitte warten Sie, wir exportieren erst unsere Excel-Listen“.
  • Ein klar separierter KMS-Key für Gesundheitsdaten — wenn eines Tages ein Tenant eine Datenlöschung fordert, löschen wir den Key und damit alle Gesundheitsdaten kryptografisch, ohne dass wir hoffnungsvoll in 14 Tabellen suchen müssen.
  • Das PSA-Gate zwingt uns, von Anfang an zwischen „Info-Toast“ und „harter Block“ klar zu unterscheiden. Diese Disziplin zahlt sich in jedem späteren Compliance-Feature aus.

Letzte Aktualisierung: 2026-04-19. Nächster Review-Termin: nach Festlegung des Referenzkunden und DSFA-Freigabe.