Zum Hauptinhalt springen

Zeit-Übersicht (TimeOverview)

Zweck

/time-overview ist das zentrale Arbeitszeit-Dashboard: Es führt pro Mitarbeiter Zeiterfassungen, Abwesenheiten, das Arbeitszeit-Modell und die §3-ArbZG-Mehrarbeit-Genehmigungen zu einem einheitlichen Saldo zusammen.

Die Seite hat zwei Tabs (Übersicht / Report) mit gemeinsamem Filter-Panel und einen Druck-Dialog für Abwesenheits-Auszüge (z. B. für die Lohnbuchhaltung).

Sub-Routen:

  • /time-overview — Tab Übersicht (Dashboard mit KPI-Karten und Mitarbeiter-Tabelle).
  • /time-overview/report — Tab Report (TIV-ähnliche Detail-Auswertung pro Mitarbeiter).
  • /time-overview/employee/:employeeId — Mitarbeiter-Detail mit Drill-Down auf einzelne Tage.

Voraussetzungen

- Berechtigung `view:FE_TimeOverview` für den Seiten-Aufruf. - `view:EmployeeTimeTracking`, `view:WorkTimeModel`, `view:Absence`, `view:Employee` für die Datenquellen. - `view:WorkTimeOvertimeApproval` für die Mehrarbeit-Karten; `create:WorkTimeOvertimeApproval` für den Backfill-Workflow. - Mitarbeiter mit zugeordnetem [Arbeitszeit-Modell](/work-time-models) — sonst leeres Soll. - Erfasste Zeiten und/oder genehmigte Abwesenheiten im ausgewählten Zeitraum.

Berechtigungen (CASL)

ActionSubjectWirkung
viewFE_TimeOverviewSeite aufrufbar
viewEmployeeTimeTracking, WorkTimeModel, Absence, EmployeeDatenquellen
viewWorkTimeOvertimeApproval§3-ArbZG-Approvals lesen
createWorkTimeOvertimeApprovalBackfill-Endpoint für historische Tage

Toolbar

Oben rechts auf der Dashboard-Seite:

ElementWirkung
Übersicht / ReportView-Switch (ButtonGroup). Steuert den aktiven Tab.
Drucken (TbPrinter)Öffnet den Abwesenheits-Druck-Dialog. Druckt den Zeitraum aus dem Filter-Panel.
Filter (TbFilter)Klappt das Filter-Panel auf/zu. Der Zustand wird pro Browser in localStorage (timeOverview_filterOpen) gemerkt.

Filter-Panel

Das Filter-Panel ist global für beide Tabs — Übersicht und Report lesen aus derselben State.

FeldVerhalten
Zeitraum-PresetEines aus thisWeek, lastWeek, thisMonth (Default), lastMonth, thisQuarter, thisYear, last4Weeks, custom.
Von / BisDatums-Inputs. Maximal heute 23:59:59 — Zukunfts-Tage werden gekappt.
MitarbeiterAsync-Select (paginiert). Filtert beide Tabs auf einen einzelnen Mitarbeiter.
AbteilungAsync-Select. Filtert auf Mitarbeiter mit aktiver Rolle in der gewählten Abteilung.
Niederlassung (seit Welle 132)Async-Select. Filtert auf Mitarbeiter, die laut aktivem EmployeeContract.branchId der gewählten Niederlassung zugeordnet sind. Backend-seitig als virtueller Query-Param branchId umgesetzt — der branchResolver.service löst zur Vertragsliste auf und übergibt eine id IN (...)-Bedingung an GET /api/employees.

Cap-Logik der Presets: thisWeek, thisMonth, thisQuarter, thisYear, last4Weeks enden bei heute. lastWeek und lastMonth enden am echten Ende der Vorperiode (für abgeschlossene Auswertungen).

URL-Sync: Die Filter werden direkt in die URL geschrieben (preset, from, to, employeeId, departmentId, branchId). Lesezeichen und Links auf eine bestimmte Sicht sind damit möglich.

Tab Übersicht

KPI-Karten

Sechs feste Stat-Cards plus eine optionale §3-Karte:

KPIBerechnungAmpel
Mitarbeiter mit EinträgenAnzahl Mitarbeiter mit recordedWorkHours > 0,001grün (100 %), gelb (Teilmenge), rot (0)
Erfasste ZeitworkIstSum / workSollSum in %grün ≥ 95 %, gelb 80–95 %, rot < 80 %, blau > 110 %
Saldosum(saldoHoursAfterIncluded) über alle Mitarbeiterneutral (≈ 0), grün (> 0), gelb (kleines Minus), rot (großes Minus)
Pflichtpause-CompliancerecordedBreak / breakSoll in %, wenn Soll > 0grün (≥ 100 %), gelb (50–100 %), rot (< 50 %), neutral (kein Soll)
Tage gesamtsum(workdaysInPeriod), Sub-Zeile: Fehltage-Quotekonstant lila
§3 ausstehend (nur wenn > 0)sum(pendingOvertimeHours)gelb (kleine Menge), orange (große Menge)

Mitarbeiter-Tabelle

Unter den KPIs steht eine MUI-DataGrid-Premium-Tabelle mit einer Zeile pro Mitarbeiter im Filter-Scope. Spalten u. a. (über Spalten-Selector ein-/ausblendbar): Mitarbeiter-Nr., Name, Soll-Stunden, Erfasste Arbeit, Brutto-Anwesenheit, Netto-Ist, Pflichtpause-Soll/Ist, Pausen-Defizit, Saldo, Saldo nach inkludierten Überstunden, ausstehende/genehmigte/abgelehnte Mehrarbeit, Abwesenheits-Tage, Werktage im Zeitraum.

Klick auf eine Zeile öffnet /time-overview/employee/:id mit den aktuellen Filter-Parametern (preset, from, to).

Toolbar der Tabelle: Spalten-Selector, Density-Selector, CSV-Export, Quick-Filter.

Tab Report

Der Report-Tab zeigt eine TIV-ähnliche Detail-Auswertung für einen Mitarbeiter — wahlweise den im Filter ausgewählten oder einen über den eingebetteten Employee-Picker gewählten. Sektionen:

  • Evaluation-Details — tagesgenaue Auflistung von Soll/Ist, Pausen, Saldo.
  • Mehrarbeit-Approvals — §3-Genehmigungen mit Status und Begründung.
  • Tagesübersicht — pro Tag: Zeit-Einträge, Abwesenheiten, Soll-/Ist-Bilanz.
  • Pausen-Analyse — Pflicht-Erfüllung gegen ArbZG-Schwellen.
  • Plausibilität — Auffälligkeiten (Überlappungen, fehlende Pausen, unrealistische Netto-Zeiten).

Oben rechts im Report-Tab: Print-Button (Browser-Druck), Hilfe-Modal mit Felder-Erklärung, Reload-Button (invalidiert alle Queries).

Default-Zeitraum bei Direktaufruf von /time-overview/report: Montag der Vorwoche bis heute (23:59:59). Vor Welle 113 endete der Default am Sonntag der Vorwoche — die laufende Woche fehlte.

Abwesenheits-Druck (AbrechnungPrintModal)

Der Druck-Dialog erzeugt eine HTML-Druckansicht mit allen Abwesenheiten im Filter-Zeitraum — Mitarbeiter- und Abteilungsfilter werden bewusst ignoriert, damit nichts vergessen wird.

Datenquelle

GET /api/absences
?from=<dateRange.startDate (YYYY-MM-DD)>
&to=<dateRange.endDate (YYYY-MM-DD)>
&filter=status,in,approved|requested
&size=-1

Spalten

SpalteQuelle
Mitarbeiter-Nr.employee.employeeNumber
Vorname / Nachnameemployee.firstName/lastName (Fallback: Parsing aus fullName)
AbwesenheitLokalisierte type + subtype
AnzahlBerechnet — siehe nächster Abschnitt
Startabsence.startDate (DD.MM.YYYY)
Endeabsence.endDate (DD.MM.YYYY)

Anzahl-Berechnung

SubtypBerechnung
elternzeitMitGeld, elternzeitOhneGeld, mutterschutz, pflegezeitKalendertage (Start bis Ende inklusive, inkl. Wochenende)
Alle anderen SubtypenWerktage Mo–Fr inklusive, abzüglich 0,5 bei halfDayStart und/oder halfDayEnd

Wichtig: Feiertage werden nicht abgezogen — die Liste ist eine Vorschau für die Lohnbuchhaltung, die Feiertage selbst kennt.

Farbcodierung

FarbeHexBedingung
Rot#c0392btype === "sick"
Blau#1f3a8atype === "vacation" (außer den lila Subtypen)
Lila#7c2d8aSubtyp aus elternzeit*, mutterschutz, pflegezeit
Grau#4a5568Default (Training, Berufsschule, Sonstiges)

Druck-Ablauf

  1. Modal öffnet sich, zeigt eine sortierte Tabelle (Nachname aufsteigend) zur Kontrolle.
  2. Drucken-Button erzeugt eine eigene HTML-Seite, öffnet ein neues Browser-Fenster (1100×800 px), schreibt das HTML hinein und ruft nach kurzem setTimeout window.print() auf.
  3. Der Druck verzichtet bewusst auf @media print im Original-DOM — so vermeidet er Stacking-Probleme mit Chakra-UI-Portalen.

TIV-Druck-Auszug — Mitarbeiter-Übersicht mit Abwesenheiten-Liste.

Saldo-Modell (Backend)

Seit Welle 113 trennt der Backend-Service workTimeBalance strikt zwischen reiner Arbeitszeit und Brutto-Anwesenheit:

FeldBedeutung
recordedWorkMinutesErfasste Arbeit (roh, vor Korrekturen)
recordedBreakMinutesErfasste Pausen
bruttoIstMinutesrecordedWorkMinutes + recordedBreakMinutes (Anwesenheit)
actualMinutesReine Arbeitszeit nach §3-ArbZG-Cap (work-only) — Basis für Saldo
absenceCreditMinutesGutschrift für genehmigte Abwesenheits-Tage
breakDeficitMinutesPausen-Soll-Unterschreitung
balanceMinutesactualMinutes + absenceCreditMinutes − targetMinutes
balanceMinutesAfterIncludedSaldo nach Abzug der im Vertrag inkludierten Mehrarbeit

Formel:

bruttoIst = recordedWork + recordedBreak
nettoIst = bruttoIst − duplicateDeduction − overlapDeduction
− unrealisticNetDeduction − breakDeficitDeduction
istCorrected = nettoIst + absenceCredit
sollHours = arbeitsrelevanteTage × (workHoursPerDay + breakHoursPerDay)
balance = istCorrected − sollHours

Diese Aufschlüsselung sorgt für eine konsistente SSoT zwischen Dashboard, Report-Tab und Mitarbeiter-Detail — alle drei Sichten rufen denselben Endpoint auf.

Backfill für Mehrarbeit-Genehmigungen

Wenn ein historischer Tag noch keine §3-ArbZG-Approval hat (z. B. weil die Erfassung nachträglich korrigiert wurde), kann ein Approval-Record manuell erzeugt werden:

POST /api/work-time-overtime-approvals/backfill
{
"employeeId": "<uuid>",
"date": "2026-04-15",
"decision": "approved", // optional
"reason": "Notfall-Einsatz Brandanlage Pflegeheim" // bei "rejected" Pflicht
}
  • Der Service prüft, ob am genannten Tag ein §3-Überlauf (> 10 h reine Arbeit) vorliegt. Wenn nicht, schlägt der Aufruf mit einer entsprechenden Fehlermeldung fehl.
  • Wird decision mitgegeben, setzt der Service Status und Metadaten direkt — andernfalls wird der Approval-Record im Status pending angelegt und läuft durch den normalen Workflow.

Details und der nutzerseitige Workflow stehen im Tutorial Mehrarbeit genehmigen und im Modul Mehrarbeit-Genehmigungen.

Schritt-für-Schritt — Routine-Nutzung

Wöchentliche HR-Auswertung

  1. /time-overview öffnen — Default ist thisMonth, ggf. auf lastWeek umschalten.
  2. KPI-Karten anschauen: gibt es Mitarbeiter mit ungewöhnlichem Saldo oder Pausen-Defizit?
  3. §3 ausstehend prüfen — wenn die Karte erscheint, in den Report-Tab oder direkt zu Mehrarbeit-Genehmigungen wechseln.
  4. Auffällige Mitarbeiter anklicken → Detail-Sicht öffnet sich mit denselben Filter-Parametern.

Monatsabschluss

  1. Preset auf lastMonth setzen.
  2. Drucken klicken → Abwesenheits-Auszug für die Lohnbuchhaltung. Im Druck-Dialog die Tabelle prüfen, dann Druckfenster öffnen.
  3. CSV-Export der Mitarbeiter-Tabelle (Toolbar der DataGrid) als Anlage zum Monatsabschluss.

Wiederverwendbare Konzepte

Verknüpfungen zu anderen Modulen

Häufige Fehler und Lösungen

FehlerLösung
Mitarbeiter fehlt in ListeKein Arbeitszeit-Modell zugeordnet, status != active oder employeeType != internal (Dashboard filtert auf interne Mitarbeiter).
KPI „Erfasste Zeit" steht auf 0 %Zeitraum überschneidet keine Buchungen, oder alle Buchungen sind im Status, der vom Saldo nicht berücksichtigt wird.
Karte „§3 ausstehend" erscheint nichtEs gibt keine offenen Mehrarbeit-Genehmigungen — Karte wird nur eingeblendet, wenn sum(pendingOvertimeHours) > 0,001 h.
Druck-Fenster bleibt leerPop-up-Blocker des Browsers erlaubt das neue Fenster nicht; in den Browser-Einstellungen Pop-ups für die Seite freigeben.
Backfill-Aufruf schlägt mit „kein §3-Überlauf" fehlAm genannten Tag liegt die reine Arbeitszeit ≤ 10 h. Backfill ist nur für tatsächliche Überschreitungen vorgesehen.
Zukunftstage werden mit 0 ausgewertetErwartet — Presets cappen auf heute 23:59:59. Im custom-Preset kann manuell weiter in die Zukunft gewählt werden, hat aber keine Aussagekraft.

API/Schnittstellen

MethodeEndpointZweckCASL
GET/api/employees/:id/time-balance?from=&to=Saldo + Detail-Felder pro Mitarbeiter im Zeitraum (SSoT)view EmployeeTimeTracking
GET/api/employees?status=active&employeeType=internalMitarbeiter-Pool für das Dashboardview Employee
GET/api/absencesQuelle für Abwesenheits-Druck und absenceCreditview Absence
GET/api/work-time-modelsSoll-Stunden-Modelle (Detail-Sicht)view WorkTimeModel
GET/api/work-time-overtime-approvals§3-Approvals für KPI „§3 ausstehend"view WorkTimeOvertimeApproval
POST/api/work-time-overtime-approvals/backfillManueller Backfill historischer Tagecreate WorkTimeOvertimeApproval

Versionshinweise

  • 2026-04-30: Initiale Veröffentlichung.
  • 2026-05-12 (Welle 113): Zentrales Arbeitszeit-Dashboard mit Two-Tab-Layout (Übersicht/Report), Toolbar + collapsible Filter-Panel mit Presets, sechs feste KPI-Karten und optionale §3-Karte, MUI-DataGrid-Premium mit CSV-Export, neuer Druck-Dialog AbrechnungPrintModal mit Subtyp-spezifischer Anzahl-Logik, Default-Zeitraum für Report-Tab auf „Montag Vorwoche bis heute" geändert, EmployeePicker-Sort-Format-Fix (employeeNumber,asc), useTimeOverviewData als gemeinsame Daten-SSoT (GET /employees/:id/time-balance pro MA). Backend-seitig: actualMinutes ist jetzt reine Arbeitszeit nach §3-Cap, bruttoIstMinutes als separate Brutto-Anwesenheit, neuer Endpoint POST /work-time-overtime-approvals/backfill für historische Tage.
  • 2026-05-20 (Welle 132): Niederlassungs-Filter im Filter-Panel ergänzt — Async-Select über BranchService.listBranches, im URL-Sync mit branchId. Backend: virtueller branchId-Query-Param auf GET /api/employees, aufgelöst über branchResolver.service (zieht alle EmployeeContract.branchId-Treffer, dedupliziert zu id IN (...)-Filter). Damit ist die Branch-Auswertung im Dashboard verfügbar, ohne dass Employee selbst ein Branch-Feld trägt. Quelle: FE 923c6a7d, BE dd366422 + neuer Service branchResolver.service.ts.