Zum Inhalt springen

Abwesenheiten — Urlaub, eAU, Sonderurlaub, Freistellung, Genehmigungs-Workflow

Live in Produktion

Feinkonzept — Abwesenheiten (Urlaub · eAU · Sonderurlaub · Freistellung)

Abschnitt betitelt „Feinkonzept — Abwesenheiten (Urlaub · eAU · Sonderurlaub · Freistellung)“

Kern-Modul. Dieses Feinkonzept regelt alle Nicht-Anwesenheiten: bezahlter Erholungsurlaub (BUrlG), Arbeitsunfähigkeitsbescheinigung (eAU via GKV-Schnittstelle § 109 SGB IV), Sonderurlaub (§ 616 BGB, TV Bau § 6), Bildungsurlaub (Ländergesetze), unbezahlte Freistellung, Mutterschutz/Elternzeit (nur Stamm-Erfassung, keine Lohnabrechnung). Struktur nach Pflichtvorlage ../TEMPLATE.md, alle 18 Sektionen.


feature_id: kern/03-abwesenheiten
title: Abwesenheiten — Urlaub, eAU, Sonderurlaub, Freistellung, Genehmigungs-Workflow
funktionsumfang_ref: §3.3
roadmap_horizont: MVP # Urlaub + eAU-Import MVP; Bildungsurlaub V1
plattformen:
mobile: vollständig # Antrag + Status + Familienkalender-View
web: vollständig # Genehmigung, Bulk, Urlaubsplanung
desktop: aus # nichts Desktop-spezifisches
owner_rolle: HR # Regeln; Manager genehmigt, MA beantragt
modul_gate_flag: module.kern.abwesenheiten
compliance_flags:
gobd: true # Urlaubskonten append-only, Genehmigungshistorie
arbzg: true # Urlaub reduziert Soll, eAU = 0 Ist
vob: false
dsgvo: true # Gesundheitsdaten Art. 9 — besondere Kategorie
bfsg: true
betrvg: true # §87(1)5 Urlaubsgrundsätze, §87(1)1 Ordnung
stvg: false
weitere: [BUrlG, EntgFG §3, §5, SGB V §109 (eAU), TV Bau §6, MuSchG (Stamm), BEEG (Stamm), JArbSchG §19]
referenzkunde:
status: TBD
name: "zu klären mit Sales — Kandidaten-Profil: 20–80 MA, Bauwirtschaft mit Saison"
quelle: Sales-Call "Urlaubsplanung auf Papier"
estimate_eng_tage: 36
abhängigkeiten:
- kern/01-zeiterfassung # Abwesenheit blockiert Stempel-Einträge am Tag
- kern/02-arbeitszeit-compliance # Urlaub zählt auf Soll an, reduziert ÜS-Konto
- kern/08-dienstplanung # Genehmigung nur bei Schicht-Freigabe

Marktrealität (DE-Handwerk). Urlaubsplanung im Handwerk ist saison-getrieben (Winterpause Bau, Sommer-Peak SHK-Heizungswartung). In 80 % der KMU geschieht das auf Papier-Kalender im Büro, per WhatsApp oder in einer Excel-Tabelle mit farbigen Zellen. Folge: Überschneidungen werden erst gemerkt, wenn die Baustelle leer ist. Krankmeldung (seit 2023 eAU-Pflicht: Arbeitgeber ruft elektronisch bei der GKV ab, §109 SGB IV) wird regelmäßig vergessen, weil niemand im Büro das TI-Portal kennt. Sonderurlaub (Hochzeit, Umzug, Todesfall naher Angehöriger) wird „großzügig gewährt“, aber nie im System erfasst — Nachweis bei Lohnsteuer-Prüfung fehlt.

Schmerzpunkt der Alt-App. LESSONS-LEARNED §7 erwähnt: Urlaubsanträge waren als requestType = 'URL' im timeEntries-Baum gemischt, kein separates Modell. Ergebnis: Ein Urlaubstag, den ein Mitarbeiter zusätzlich noch abstempelte, erzeugte ein Ghost-Ist von 8 h. eAU existierte gar nicht — Arbeitsunfähigkeit wurde per hand als Null-Stempel eingetragen. Genehmigungslogik war eine Bool-Spalte approved ohne Workflow-State, ohne Ablehnungsgrund, ohne BR-Zustimmungsprotokoll.

Erwarteter Outcome.

  • Antrag zu Genehmigung < 24 h (heute: 3–10 Tage, weil Papier im Büro liegt).
  • eAU-Abruf automatisch, mit Fallback bei GKV-Timeout (manuelle Erfassung + Auditpunkt).
  • Urlaubskonten transparent mit Verfallslogik nach BUrlG § 7(3) + BAG-Urteil 31.01.2023 (9 AZR 244/20 „Aufforderungs- und Hinweisobliegenheit“).
  • DSGVO Art. 9: Krankheitsdaten strikt getrennt, Zugriff protokolliert, nie in Statistiken nicht-anonymisiert.
  • Familien-Kalender-View für Monteure: „kann ich nächste Woche frei nehmen?“ ohne Manager-Call.

Rolle Aktion Scope Plattform
Mitarbeiter Antrag stellen, Status verfolgen, Restanspruch sehen, eAU nachreichen (Foto als Fallback) own 📱🌐
Manager Genehmigen/ablehnen, Team-Urlaubskalender, Engpass-Warnung team 📱🌐
HR Sonderurlaub-Kategorien pflegen, Sperrzeiten setzen, eAU-Abruf prüfen, Korrekturen all 🌐
Admin Tarifliche Anspruchsregeln, BR-Zustimmung dokumentieren all 🌐
Buchhaltung Lese-Zugriff für Lohnabrechnung (DATEV-Export Feinkonzept §3.7) all 🌐

Persona-Skizzen.

  • Hannes Krüger (Monteur, 38, Maler-Geselle, Musterbetrieb-Maler) — plant Familienurlaub Pfingsten 2026, will Montag mittag per Handy einen Antrag stellen, hat keine Lust, das Büro anzurufen. Stammdaten: 30 Urlaubstage/Jahr (TV Bau, 5-Tage-Woche), Rest 19, davon 4 aus Vorjahr mit Verfall 31.03.
  • Sabine Maier (Managerin, 45) — muss in KW 27 entscheiden, ob sie Hannes freigibt oder nicht, weil am 08.06. ein großes Fassadenobjekt startet und sie zwei Geselle braucht. Arbeitet am Tablet, will Ampel-Kalender.
  • Frau Müller (Buchhaltung, 56) — generiert Krankenquote für DATEV-LODAS-Export, braucht zuverlässige Abwesenheitsarten-Mapping zu DATEV-Lohnarten (100 = Gehalt, 200 = Krank, 300 = Urlaub, 400 = Sonderurlaub), hat heute 3 verschiedene Excel-Tabellen.
  • Thomas Schmidt (Bauleiter, 52) — meldet selten Krank, aber 19.04. war er beim Zahnarzt. System muss seine eAU automatisch in 24 h haben.

US-01 [MVP] Als Mitarbeiter möchte ich einen Urlaubsantrag vom Handy stellen
und sofort sehen, wie viele Resttage nach Genehmigung übrig bleiben,
um vor dem Absenden keine Überraschung zu erleben.
US-02 [MVP] Als Manager möchte ich in einer Team-Kalender-Ansicht sehen, wer
im Antragszeitraum bereits frei ist, bevor ich genehmige —
damit ich keine Baustelle lahmlege.
US-03 [MVP] Als Mitarbeiter möchte ich meine Krankmeldung per eAU-Pull
automatisch gemeldet bekommen, damit ich beim Arztbesuch nichts
weiteres tun muss als dem Arzt zu sagen „elektronisch".
US-04 [MVP] Als HR möchte ich gesetzeskonform den Verfall von Urlaub
vermerken und Mitarbeiter auf den drohenden Verfall hinweisen
(BAG 9 AZR 244/20), damit der Arbeitgeber nicht haftet.
US-05 [V1] Als Manager möchte ich Sperrzeiten für Teams definieren können
(z. B. „Hochphase Fassaden KW 23–27 — max. 1 Person in Urlaub"),
damit das System Anträge vorfiltert.
US-06 [V1] Als Mitarbeiter möchte ich Sonderurlaub (Hochzeit, Umzug, Tod)
mit korrekter TV-Bau-Kategorie beantragen können, ohne das
Regelwerk auswendig zu kennen.
US-07 [V1.5] Als Admin möchte ich Bildungsurlaub nach Landes-Bildungsurlaubs-
gesetz konfigurierbar gewähren (5 Tage NRW, 5 Tage Bayern eingeführt 01/2025).

  • F-M-01 Urlaubsantrag: Zeitraum (von/bis inkl. halber Tage), Kategorie (Urlaub / Sonderurlaub / unbezahlt), Vertretung wählen (optional), Notiz (optional). Vor Absenden: Vorschau „nach Genehmigung verbleiben X Tage“.
  • F-M-02 eAU-Meldung: „Ich bin krank“ → sendet Trigger an Backend → Backend pollt GKV-Schnittstelle alle 30 min für 72 h. Fallback: Foto der AU hochladen (Zwischenschritt, verschlüsselt abgelegt, Löschung nach eAU-Eingang).
  • F-M-03 Kalender-Strip der nächsten 60 Tage mit Status-Pins (genehmigt, offen, abgelehnt, eAU). Tap → Detail.
  • F-M-04 Restanspruch-Kachel „Ich habe noch: 19 Tage, davon 4 bis 31.03.2027“ inkl. Hinweis bei drohendem Verfall < 45 Tage.
  • F-M-05 Push bei Status-Wechsel (genehmigt / abgelehnt mit Grund / eAU erfolgreich abgerufen).
  • F-W-01 Team-Urlaubskalender: Heatmap 12-Monate-Grid, Filter Team/Qualifikation, Engpass-Warnung (rot, wenn < 60 % Team anwesend).
  • F-W-02 Genehmigungs-Inbox für Manager: Liste offener Anträge, Bulk-Genehmigung (bis 10), Ablehnung mit Pflichtfeld „Begründung“.
  • F-W-03 HR-Ansicht Urlaubskonten-Matrix: alle MA, Soll/Ist/Rest/Verfallsdatum, Bulk-Import CSV (Migration Alt-System).
  • F-W-04 eAU-Cockpit: offene Pulls, TI-Timeouts, manueller Nachforder-Button.
  • F-W-05 Sperrzeiten-Editor: Baustein-Formular (Team, Zeitraum, max. gleichzeitig, Override-Regel).
  • F-W-06 Report „Fehlzeitenquote“ monatlich (Krankenquote DSGVO-konform aggregiert, keine Einzel-Identifikation bei < 5 MA pro Team).
  • F-X-01 Antrag-Statusmodell: draft → submitted → approved|rejected|withdrawn (append-only Historie).
  • F-X-02 Familien-Kalender-Export: iCal-Feed je Mitarbeiter (signed URL, pro Device revokable).
  • F-X-03 Genehmigte Abwesenheit blockiert Stempeln (kern/01) am betreffenden Tag, außer HR überschreibt mit Audit-Pflicht.
  • F-A-01 Tariflicher Urlaubsanspruch pro Lohngruppe (TV Bau: 30 Tage gewerblich ab 2. Jahr) mit Jahres-Rollover-Regel.
  • F-A-02 Kategorien-Mapping zu DATEV-Lohnarten (§3.7) — Vorlage für „Musterbetrieb-Maler“ und „SHK-Gebrüder-Schmidt“ mitgeliefert.
  • F-A-03 GKV-Abruf-Konfiguration (Betriebsnummer, TI-Zertifikat-Upload, Timeouts).
Anforderung MVP V1 V1.5 V2
F-M-01 Antrag
F-M-02 eAU-Trigger
F-M-03 Kalender-Strip
F-M-04 Restanspruch
F-W-01 Team-Kalender
F-W-02 Genehmigungs-Inbox
F-W-03 HR-Matrix
F-W-04 eAU-Cockpit
F-W-05 Sperrzeiten
F-A-03 GKV-Konfig
Bildungsurlaub Länder
Mutterschutz/Elternzeit

HTML-Hero-Mockup: 03-abwesenheiten.html — Mobile (Antrag Pfingst-Urlaub Hannes) und Web (Genehmigungs-Inbox + Team-Kalender Sabine) Side-by-Side.

┌────────────────────────────────┐
│ ← Urlaubsantrag ⓘ │
│ 🛡 DSGVO · BUrlG · TV Bau │
│────────────────────────────────│
│ Restanspruch │
│ ┌──────────────────────────┐ │
│ │ 19 / 30 Tage │ │
│ │ davon 4 verfallen 31.03 │ │
│ └──────────────────────────┘ │
│ │
│ Zeitraum │
│ ┌─────────────┐ ┌───────────┐ │
│ │ Mo 25.05.26 │ │Do 04.06.26│ │
│ └─────────────┘ └───────────┘ │
│ = 8 Werktage │
│ │
│ Kategorie │
│ (•) Erholungsurlaub │
│ ( ) Sonderurlaub │
│ ( ) Unbezahlt │
│ │
│ Vertretung (optional) │
│ [ Frank Demir ▼ ] │
│ │
│ Notiz │
│ [ Familienurlaub Ostsee ] │
│ │
│ ▼ Nach Genehmigung: 11 Tage │
│ ⚠ KW 22 sind bereits 2 MA auf │
│ Urlaub · Manager entscheidet│
│ │
│ [ Abbrechen ] [ ✓ ANTRAG ] │
└────────────────────────────────┘

6.2 Mobile — Restanspruch-Kachel mit Verfallshinweis

Abschnitt betitelt „6.2 Mobile — Restanspruch-Kachel mit Verfallshinweis“
┌────────────────────────────────┐
│ Urlaub │
│────────────────────────────────│
│ │
│ 19 │
│ ───────── │
│ Tage frei │
│ │
│ von 30 Jahres-Anspruch │
│ bereits 8 genommen, 3 geplant │
│ │
│ ⚠ Achtung Verfall │
│ ┌──────────────────────────┐ │
│ │ 4 Tage aus 2025 verfallen│ │
│ │ am Di 31.03.2026 (§7(3) │ │
│ │ BUrlG, BAG 9 AZR 244/20) │ │
│ │ │ │
│ │ [ Antrag jetzt stellen ] │ │
│ └──────────────────────────┘ │
│ │
│ eAU-Status: kein Eintrag │
│ (letzte AU 03.02.2026, 2 Tage)│
└────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ musterbetrieb-maler › Abwesenheiten › Genehmigungs-Inbox Sabine Maier │
│ 🛡 DSGVO Art. 9 · BUrlG · BetrVG §87(1)5 · BR-Zustimmung 02/2026 │
│──────────────────────────────────────────────────────────────────────────────│
│ Offen [ 4 ] Genehmigt [ 12 ] Abgelehnt [ 1 ] eAU-Pulls [ 2 offen ] │
│ │
│ [✓] MA Zeitraum Tage Art Engpass Aktion │
│ [ ] Hannes Krüger Mo 25.05–04.06 8 Urlaub ⚠ KW 22 [Detail][✓][✗]│
│ [ ] Mehmet Yılmaz Mi 06.05–08.05 3 Urlaub ok [Detail][✓][✗]│
│ [ ] Frank Demir Fr 22.05 0,5 Sonder. ok [Detail][✓][✗]│
│ [ ] Jonas Brehm Mo 01.06–05.06 5 Bildung — [Detail][✓][✗]│
│ │
│ [ Bulk genehmigen (0) ] [ CSV ] │
│──────────────────────────────────────────────────────────────────────────────│
│ Team-Kalender KW 21–27 (2026) │
│ Name M D M D F | M D M D F | M D M D F | M D M D F | M D M D F │
│ Hannes · · · · · | U U U U U | U U U · · | · · · · · | · · · · · │
│ Mehmet · · · · · | · · · · · | · U U U · | · · · · · | · · · · · │
│ Frank · · · · · | · · · · · | · · · · S | · · · · · | · · · · · │
│ Thomas · · · · · | · · · · · | · · · · · | · · · · · | · · · · · │
│ Andreas · · · · · | · · · · · | · · · · · | · · · · · | · · · · · │
│ … │
│ Legende: U=Urlaub S=Sonder K=Krank (eAU) B=Bildung ▲=Anwesend=<60% (rot) │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ HR › Urlaubskonten 2026 Frau Müller │
│──────────────────────────────────────────────────────────────────────────────│
│ MA Soll Rest-VJ Neu26 Genommen Offen Rest Verfall │
│ Hannes Krüger 30 4 30 8 8 19 31.03.26 → 4T │
│ Mehmet Yılmaz 30 2 30 4 3 25 — │
│ Frank Demir 30 0 30 2 0 28 — │
│ Thomas Schmidt 30 6 30 0 0 36 ⚠ 31.03.26 → 6T │
│ Jonas Brehm 28 0 28 0 5 23 — (JArbSchG §19)│
│ Σ 18 MA … │
│ │
│ [ CSV-Export ] [ BUrlG-Hinweis verschicken (3 MA mit Verfall) ] │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ HR › eAU-Cockpit GKV-Schnittstelle · letzte 15 min│
│──────────────────────────────────────────────────────────────────────────────│
│ Pending Pulls │
│ MA Trigger Kasse Status Seit Aktion │
│ Thomas Schmidt Mo 19.04 08:15 TK ⏳ retry 3 5:12 h [Nachpol.] │
│ Andreas Wirth Fr 16.04 11:04 AOK-Bay ✓ empfangen — [Anzeigen] │
│ Frank Demir Do 15.04 06:48 Barmer ✗ not_found 98 h [Manuell ↑] │
│ │
│ [ Manuelle Erfassung starten ] [ Log GKV-TI ansehen ] │
└──────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────┐
│ ← Ich bin krank │
│────────────────────────────────│
│ │
│ Krankmeldung starten │
│ │
│ Arzt war │
│ (•) Heute │
│ ( ) Gestern │
│ ( ) Anderes Datum ▼ │
│ │
│ Gehe ich zum Arzt? │
│ [ Ja, elektronisch (eAU) ] │
│ [ Ich reiche AU als Foto ] │
│ │
│ ▼ Was passiert danach? │
│ • Werkszeit fragt deine Kasse │
│ in den nächsten 72 h ab │
│ • Du siehst den Status hier │
│ • Kein Stempeln für heute │
│ │
│ [ Abbrechen ] [ KRANK MELDEN ]│
└────────────────────────────────┘

Drift (Flutter-lokal) + Drizzle (Server). Abwesenheiten sind ein eigenständiges Aggregat, nie in time_entries gemischt (Lesson Alt-App).

class AbsencesTable extends Table {
TextColumn get id => text()(); // UUID v7
TextColumn get tenantId => text()();
TextColumn get employeeId => text()();
TextColumn get category => text()(); // vacation | sick | special | unpaid | education | maternity | parental
DateTimeColumn get startDate => dateTime()(); // inklusive, lokale TZ
DateTimeColumn get endDate => dateTime()(); // inklusive
RealColumn get days => real()(); // berechnet, inkl. halber Tage
TextColumn get status => text()(); // draft | submitted | approved | rejected | withdrawn
TextColumn get substituteId => text().nullable()(); // Vertretung
TextColumn get note => text().nullable()();
TextColumn get createdBy => text()();
DateTimeColumn get createdAt => dateTime()();
TextColumn get approvedBy => text().nullable()();
DateTimeColumn get approvedAt => dateTime().nullable()();
TextColumn get rejectionReason => text().nullable()();
TextColumn get eauSource => text().nullable()(); // gkv_ti | photo | manual
DateTimeColumn get eauReceivedAt => dateTime().nullable()();
TextColumn get hashPrev => text().nullable()();
TextColumn get hashSelf => text()(); // SHA-256
}
class AbsenceBalancesTable extends Table { // Jahresanspruch-Konto
TextColumn get id => text()();
TextColumn get tenantId => text()();
TextColumn get employeeId => text()();
IntColumn get year => integer()();
RealColumn get entitlement => real()(); // 30 Tage TV Bau
RealColumn get carryOverPrevYear => real()(); // § 7(3) BUrlG
DateTimeColumn get carryOverExpiresAt => dateTime().nullable()();
RealColumn get taken => real()();
RealColumn get scheduled => real()();
}
class AbsenceLockPeriodsTable extends Table { // Sperrzeiten
TextColumn get id => text()();
TextColumn get tenantId => text()();
TextColumn get teamId => text().nullable()();
DateTimeColumn get startDate => dateTime()();
DateTimeColumn get endDate => dateTime()();
IntColumn get maxConcurrent => integer()(); // max. gleichzeitig auf Urlaub
TextColumn get reason => text()();
}

Server-seitig zusätzlich:

  • eau_pulls (Triggered-Pulls an GKV TI, retry-Counter, Response-Log verschlüsselt)
  • absence_audit_log (jeder Statuswechsel mit Hash-Chain hash_prev/hash_self, GoBD)
  • RLS: tenant_id = current_setting('app.tenant')::uuid. Zusätzlich für category='sick' eine separate Policy: nur HR und Mitarbeiter selbst, nicht Manager (DSGVO Art. 9: besondere Kategorie).

Retention. Urlaubsanträge 10 Jahre (GoBD, Lohnsteuer-Belege). eAU-Rohdaten: 6 Monate in primären DB, danach verschlüsselt archiviert, Schlüssel-Escrow bei HR. Foto-AUs löschen binnen 30 Tage nach eAU-TI-Eingang (DSGVO-Datenminimierung).


Anspruchsberechnung. Neues Jahr = Entitlement (Tarifvertrag) + Carry-over aus Vorjahr, capped an tarifliches Maximum. Verfallsdatum der Carry-over-Tage = 31.03. des Folgejahres, außer der Arbeitgeber hat nicht nachweislich aufgefordert und hingewiesen (BAG 9 AZR 244/20) — dann Verfall ausgesetzt. Das System erzwingt Aufforderung: Automatischer E-Mail/In-App-Hinweis an MA, Empfangsnachweis gespeichert, HR-Checkbox „Alle aufgefordert“ vor Jahreswechsel.

Antrag-Workflow.

  1. MA erstellt Antrag (Status draft lokal, bis Submit).
  2. Submit → submitted. Validierung server-seitig: Restanspruch ausreichend? Zeitraum nicht in Sperrzeit (außer Override)? Nicht überlappend mit genehmigter Abwesenheit?
  3. Manager-Notification via Push/E-Mail. Manager entscheidet → approved|rejected.
  4. Approval setzt in kern/01-zeiterfassung einen Day-Block (keine Stempelung möglich, außer HR-Override mit Begründung).
  5. Withdraw (durch MA) nur in Status submitted oder approved bis Start-Datum, wird als neuer Log-Eintrag geschrieben, nicht gelöscht.

eAU-Pull. Trigger = „Ich bin krank“ Event. Backend-Queue startet einen 30-min-Pull gegen GKV-TI pro MA, bis max. 72 h. Bei Treffer: eau_received_at gesetzt, Abwesenheit automatisch auf approved. Bei Kein-Treffer nach 72 h: HR-Eskalation („Mitarbeiter nach AU fragen / Abmahnung prüfen“). Fallback Foto-AU: MA lädt AU-Scan hoch, HR muss in 5 Werktagen manuell prüfen, Datei wird nach eAU-Eingang gelöscht.

Sperrzeiten. Manager/HR definieren {team, start, end, maxConcurrent}. Neuer Antrag in Sperrzeit: zuerst prüfen ob count(approved) < maxConcurrent, sonst Antrag mit Status submitted aber Kennzeichnung „Engpass“ — Manager sieht rotes Icon und muss explizit overriden.

DSGVO Art. 9. Krankheitsdaten: Zugriff nur HR + MA selbst. Manager sieht Kategorie „Abwesenheit“ aber nicht „Krank“ vs. „Urlaub“ in Team-Listen — außer Manager gehört zu Rolle HR ebenfalls. Report-Aggregation: Bei Team < 5 MA wird Krankenquote geblurred (< 5 MA, keine %-Zahl).

DATEV-Mapping. Kategorien →

vacation → Lohnart 300 (bezahlter Urlaub)
sick → Lohnart 200 (bezahlt §3 EntgFG 6 Wochen)
sick > 42 Tage → Lohnart 210 (Krankengeld-Zuschuss / unbezahlt)
special → Lohnart 400 (bezahlter Sonderurlaub TV Bau §6)
unpaid → Lohnart 500 (unbezahlte Freistellung)
education → Lohnart 410 (Bildungsurlaub, bezahlt nach LBG)

Feature: Urlaubsantrag stellen, Restanspruch prüfen, Manager genehmigt
Scenario: Hannes stellt am 19.04.2026 Antrag für Pfingsten
Given Hannes hat Restanspruch 19 Tage (davon 4 Verfall 31.03.2027)
And Sperrzeit "Fassaden KW 23–27 max 1 MA" ist aktiv
When Hannes Antrag "25.05.–04.06.2026, Erholungsurlaub" sendet
Then im Team-Kalender erscheint der Antrag als "Engpass" gelb
And Manager Sabine erhält eine Push-Benachrichtigung
When Sabine den Antrag öffnet
Then sie sieht: 2 MA sind in KW 22 bereits genehmigt → Konflikt markiert
When Sabine mit Begründung "Baustelle pausiert in KW 22" genehmigt
Then der Antrag wird auf "approved" gesetzt (append-only)
And Hannes erhält Push "Urlaub genehmigt, Rest 11 Tage"
And in kern/01 (Zeiterfassung) sind die Tage 25.05.–04.06. blockiert
And im audit_log steht `approval` mit hash_self SHA-256
Scenario: GKV antwortet nicht binnen 72 h, Hannes reicht Foto nach
Given Hannes meldet "Ich bin krank" am Mo 19.04.2026 07:30
And Backend startet GKV-Pull alle 30 min
When 72 h vergangen sind ohne Treffer
Then HR-Cockpit zeigt "Frank Demir: not_found" in Rot
And Mitarbeiter bekommt Hinweis "Bitte AU als Foto einreichen"
When Hannes lädt AU-Foto hoch
Then Datei wird verschlüsselt (AES-256, Schlüssel Schlüsselverwaltung HR)
And Status "photo" wird gesetzt
And HR prüft manuell, genehmigt, `eau_received_at` gesetzt
And Foto wird 30 Tage nach Einlesung automatisch gelöscht
Scenario: Arbeitgeber hat nicht aufgefordert, Verfall ausgesetzt (BAG 9 AZR 244/20)
Given Thomas Schmidt hat 6 Tage Resturlaub aus 2025
And Das System hat bis 15.02.2026 keinen Aufforderungs-Log gesendet
When die Jahres-Rollover-Task am 31.03.2026 läuft
Then die 6 Tage werden NICHT verfallen gesetzt
And ein HR-Eintrag "Verfall ausgesetzt — §7(3) BUrlG BAG 9 AZR 244/20" wird erzeugt
And Thomas' Konto zeigt "6 Tage, kein Verfallsdatum (Aufforderung fehlt)"
Scenario: Manager sieht Krankheit nur als "Abwesenheit", nicht als "Krank"
Given Frank Demir ist am 15.04.2026 krank gemeldet (eAU)
And Manager Sabine (keine HR-Rolle) öffnet Team-Kalender
When sie 15.04.2026 anschaut
Then sieht sie "Abwesend" grau
And NICHT das Wort "Krank" oder "AU"
When Frau Müller (HR) denselben Tag öffnet
Then sieht sie "Krank eAU, TK"
And ein Zugriffs-Log wird geschrieben (DSGVO Art. 30)
Scenario: Antrag aus anderem Tenant
Given Tenant musterbetrieb-maler und shk-gebruder-schmidt
And Hannes hat einen Antrag in musterbetrieb-maler
When ein Request mit Hannes' Antrag-ID gegen shk-gebruder-schmidt gestellt wird
Then antwortet Server mit 404 (nicht 403, um Existenz nicht zu bestätigen)
And RLS-Policy blockt auf DB-Ebene, nicht nur Application-Schicht
Scenario: Ein vergangener Monat ist gesperrt
Given Der März 2026 wurde am 15.04. GoBD-festgeschrieben (§3.7 Rechnungsstellung)
When HR versucht, einen Urlaubstag 20.03. rückwirkend zu setzen
Then Server antwortet mit 423 "Monat gesperrt"
And HR bekommt Option "Korrektur-Lauf anfordern" (Supersedes-Eintrag, nicht Update)
And der neue Eintrag referenziert die Original-ID via `supersedes_id`

  • P95 Antrag-Submit < 350 ms (reine DB-Write, GKV-Pull asynchron).
  • P95 Team-Kalender < 600 ms für 12-Monate / 50 MA.
  • eAU-Pull Worst-Case 72 h bis Fallback — SLA gegen GKV-TI ist extern, wir SLA gegen unser Retry.
  • Push-Delivery < 30 s bei Statuswechsel (App Badge Update).
  • Offline-Fähigkeit: Antrag auf iPhone im Keller erstellbar (Draft lokal, Submit beim Reconnect, idempotent via UUID v7).
  • BFSG/WCAG 2.2 AA: Kalender-Heatmap mit Textmuster-Alternative (nicht nur Farbe), Screen-Reader liest „Mo 25.05. genehmigter Urlaub Hannes Krüger“.
  • Sprach-Barrierefreiheit: Deutsch Pflicht, Türkisch und Polnisch als Anzeige-Sprache ab V1 (Monteure DE-Handwerk).

Regelwerk Pflicht Umsetzung
BUrlG §7(3) Verfall Urlaub Auto-Aufforderung 45 d vor Jahreswechsel, Empfangs-Log
BAG 9 AZR 244/20 Hinweisobliegenheit Verfallsdatum nur aktiv, wenn Aufforderungs-Log vollständig
EntgFG §3 Lohnfortzahlung 6 W bei Krank Status-Wechsel auf Tag 43 → Lohnart 210 im DATEV-Export
SGB V §109 eAU elektronisch GKV-TI-Pull, Betriebsnummer pro Tenant, TI-Zertifikat-Rotation
DSGVO Art. 9 Gesundheitsdaten Separate RLS-Policy category='sick' → nur HR + Selbst
DSGVO Art. 30 Verarbeitungsverzeichnis Auto-Log jedes Zugriffs auf eAU-Daten
TV Bau §6 Sonderurlaub-Tatbestände Katalog vorkonfiguriert, editierbar per Admin
BetrVG §87(1)5 Urlaubsgrundsätze BR-Zustimmungsdatum pro Tenant, Policy-Versionierung
BetrVG §87(1)1 Ordnung des Betriebs Genehmigungsregeln sind Policy, BR-approved
MuSchG / BEEG Stamm-Erfassung Eigene Kategorie, kein Lohnlauf aus Werkszeit
GoBD §147 AO 10 Jahre Aufbewahrung append-only Tabelle, S3 Object Lock Compliance
JArbSchG §19 25–30 Tage Azubi-Urlaub Alters-abhängige Entitlement-Berechnung

Auszug. Volle Spec siehe openapi/kern/abwesenheiten.yaml.

paths:
/v1/kern/absences:
get:
parameters:
- name: employee_id
- name: year
- name: status
responses:
"200": { content: { application/json: { schema: { $ref: '#/components/schemas/AbsenceList' } } } }
post:
requestBody:
content:
application/json:
schema: { $ref: '#/components/schemas/AbsenceCreate' }
responses:
"201": { headers: { "Idempotency-Key": { required: true } } }
"409": { description: "Konflikt Sperrzeit / Überschneidung" }
"422": { description: "Restanspruch unzureichend" }
/v1/kern/absences/{id}/approve:
post:
responses:
"200": {}
"423": { description: "Monat gesperrt (GoBD)" }
/v1/kern/absences/{id}/reject:
post:
requestBody:
content:
application/json:
schema: { type: object, required: [reason], properties: { reason: { type: string, minLength: 5 } } }
/v1/kern/eau/trigger:
post:
requestBody:
content:
application/json:
schema: { $ref: '#/components/schemas/EauTrigger' }
/v1/kern/absences/balances:
get: { ... }
components:
schemas:
AbsenceCreate:
required: [employee_id, category, start_date, end_date]
properties:
id: { type: string, format: uuid } # UUID v7, client-generiert
category: { enum: [vacation, sick, special, unpaid, education] }

Ausschließlich wz-*-Klassen aus ../assets/werkszeit-design-system.css + components.html:

  • wz-frame-mobile / wz-frame-web
  • wz-compliance-banner mit Shields BUrlG/DSGVO/GoBD
  • wz-card mit wz-card__header für Restanspruch-Kachel
  • wz-pill--ok|warn|danger für Status
  • wz-table + wz-row--warn für Genehmigungs-Inbox
  • wz-kpi-grid mit 4 KPIs (Offen, Krankenquote, Ø Resttage, Verfall-Risiko)
  • Neue Spezialkomponente: wz-kalender-heatmap (aus cookbook, hier wiederverwendet), wz-restanspruch-dial, wz-eau-pull-row

Kein Selbstbau von Komponenten ohne DS-Eintrag. Erweiterungen gehen zuerst in components.html #cookbook.


  • Unit (Drift + Drizzle): Anspruchsberechnung, Carry-over-Verfall, Sperrzeit-Overlap, DSGVO-RLS-Policy.
  • Property-based: Zufällige Abwesenheits-Zeiträume × Schaltjahr × DST — Tageszahl-Berechnung muss stimmen (kein 7-statt-8-Tage-Bug wegen 29.03.2026 DST).
  • Integration: GKV-TI-Mock (recorded fixtures), Timeout-Path, not_found-Path, success-Path.
  • Contract: OpenAPI-Dryrun für jede Response (200/409/422/423/404).
  • E2E Patrol (iOS+Android): Hannes-Happy-Path Antrag → Genehmigung → Stempel-Block am 25.05.
  • E2E Playwright (Web): Sabine-Bulk-Genehmigung (10 Anträge), Engpass-Warnung.
  • Multi-Tenant-Invariant-Test: Ein Test ruft bewusst Tenant A-Antrag über Tenant-B-Context → muss 404.
  • DSGVO-Test: Manager-Rolle darf in keinem Endpoint category='sick' sehen (Snapshot-Test über alle Responses).
  • Load-Test (k6): 200 gleichzeitige Anträge, Team-Kalender 12 Monate bei 100 MA.

  1. CSV-Import Alt-System. Template mit 12 Spalten (MA-ID, Kategorie, Von, Bis, Tage, Status, …). Bulk-Loader im HR-Cockpit, Dry-Run-Modus mit Konflikt-Report.
  2. Feature-Flag module.kern.abwesenheiten default false. Aktivierung pro Tenant im Admin.
  3. Pilot: musterbetrieb-maler (25 MA) 2 Wochen mit Schattenbetrieb (Papier + App parallel). Fehler-Budget 1 falscher Antrag / 100.
  4. Rollout: GKV-TI-Produktion erst nach Zertifikats-Validierung durch gematik (SLA: 3 Wochen).
  5. Rollback: Feature-Flag aus → Read-only, Lese-Zugriff bleibt. Keine Daten-Löschung.

KPIs zur Abnahme:

  • 0 falsche Krankenquoten in Pilot
  • eAU-Success-Rate > 90 %
  • Ø Zeit Antrag → Genehmigung < 24 h
  • 0 DSGVO-Zugriffs-Verstöße im Pen-Test

Risiko Wahrsch. Impact Mitigation
GKV-TI-Zertifikat-Rotation kaputt mittel hoch Rotation-Reminder 30 d vorher, Dual-Active-Zertifikate
Manager genehmigt flüchtig, Engpass übersehen hoch mittel Engpass-Warnung muss explizit override-bestätigt werden
BAG-Urteil zu Verfall ändert sich niedrig hoch Verfalls-Logik in separater Policy-Tabelle, nicht Code
Foto-AU wird nicht gelöscht mittel hoch (DSGVO) Scheduled Job mit Prüfsumme, Monitoring-Alert bei Misslöschung
BR-Zustimmung fehlt, System läuft niedrig sehr hoch BR-approved-Flag pro Tenant Pflicht vor Aktivierung

Annahmen

  • Alle Ziel-Kunden haben Betriebsnummer und können GKV-TI anbinden (true für alle > 5 MA).
  • DATEV-Mapping Standard ist für 80 % ausreichend; individuelle Lohnarten pro Tenant im Admin editierbar.
  • Azubi-Urlaub JArbSchG (max. 30 Tage bis 16 J., 25 bis 17, Standard ab 18) wird aus Geburtsdatum berechnet — Stammdaten-Pflege ist Verantwortung HR (kern/06).

Profil: 20–80 MA Bauwirtschaft, TV-Bau-gebunden, Saison-Peak Mai–Oktober, heute Excel-Urlaubskalender und Papier-Krankmeldung. Kernfrage im Sales-Call: „Wie viele Resttage hat Monteur X im Juni?“ — Antwort muss heute in 5 s im System stehen, nicht in 30 min Excel-Blätter-Blättern.

Pitch-Mikro-Narrativ: „Mit Werkszeit stellt Ihr Monteur seinen Urlaubsantrag am Frühstückstisch per Handy. Ihr Manager sieht auf dem Tablet sofort, ob die Baustelle dadurch kippt. Ihre HR spart bei jedem Krankheitsfall 15 Minuten TI-Portal. Und bei der Lohnsteuerprüfung ist die Beweiskette lückenlos — 10 Jahre, append-only, BUrlG-konform.“

Referenz-Name: TBD nach Sales-Call „BAG-Deadline-Angst“ Q1/2026.


  1. Keine Lohnabrechnung — wir berechnen keine Bezüge, exportieren nur nach DATEV (siehe kern/05 Rechnung / kern/07 Reporting).
  2. Keine Entgeltumwandlung — Elternzeit/Mutterschutz nur Stamm, keine ElterngeldPlus-Berechnung.
  3. Keine Schwerbehinderten-Zusatzurlaub-Automatik (SGB IX § 208) — wird als Sonderurlaub manuell gepflegt in Phase 1.
  4. Kein Abwesenheits-Vertretungs-Mechanismus mit Task-Umverteilung (das macht kern/08 Dienstplanung).
  5. Keine Urlaubs-Optimierer-KI („beste Urlaubstage 2026 für Brückentage“) — Spielerei, nicht Compliance.
  6. Kein Papier-Formular-PDF-Druck — das wäre Rückschritt, BAG verlangt elektronisch.
  7. Keine automatische Krankengeld-Abrechnung ab Tag 43 — Hinweis an HR, manuelle Weiterleitung an Krankenkasse, aber kein Krankengeld-Antrag.
  8. Keine eigene TI-Anbindung zu Arzt-Praxen — wir nutzen ausschließlich GKV-Schnittstelle § 109 SGB IV, der Arzt meldet weiter an die Kasse.

Ende Feinkonzept. Revision 0.1 — 19.04.2026 — Autor: Senior Consultant · Review offen: HR-Compliance + Betriebsrat-Muster + DATEV-Mapping-Abgleich.

Für Entwickler — API-Endpoints8
MethodePfadAuthZweck
GET/v1/kern/absencesbearerAuthAbwesenheiten auflisten
POST/v1/kern/absencesbearerAuthAbwesenheitsantrag anlegen
GET/v1/kern/absences/{id}bearerAuthEinzelne Abwesenheit
POST/v1/kern/absences/{id}/approvebearerAuthAntrag genehmigen
POST/v1/kern/absences/{id}/rejectbearerAuthAntrag ablehnen
POST/v1/kern/absences/{id}/withdrawbearerAuthAntrag zurückziehen (MA selbst)
GET/v1/kern/absences/balancesbearerAuthUrlaubskonten auflisten
POST/v1/kern/eau/triggerbearerAutheAU-Abruf starten