84 lines
9.7 KiB
Markdown
84 lines
9.7 KiB
Markdown
# Zeiterfassung 2.0
|
|
|
|
Unter **2.0 → Zeiterfassung** werden Anwesenheiten direkt als Zeitspannen gespeichert. Eine Anwesenheit enthält Beginn, Ende, Pausen und optionale Projektzuordnungen. Arbeitszeit ist die Anwesenheitsdauer abzüglich der Pausen. Die bisherige Erfassung bleibt für noch nicht umgestellte Mitarbeiter verfügbar.
|
|
|
|
## Erfassung und Freigaben
|
|
|
|
**Kommen** öffnet eine Anwesenheit. **Pause beginnen** und **Pause beenden** erfassen Pausen innerhalb dieser Anwesenheit. **Gehen** schließt die Anwesenheit und gegebenenfalls eine laufende Pause. Manuelle Einträge können mehrere Tage umfassen. Überschneidungen, Pausen außerhalb der Anwesenheit und Projektzeiten innerhalb einer Pause werden abgelehnt. Projektzuordnungen sind exklusive Teilzeiträume; mehrere Projekte können nicht dieselbe Arbeitszeit beanspruchen.
|
|
|
|
Abgeschlossene Einträge können eingereicht werden. Berechtigte Personen können sie genehmigen oder mit Begründung ablehnen. Korrekturen verlangen einen Grund und setzen den Status auf **Erfasst** zurück. Damit wird die korrigierte Fassung erneut eingereicht und genehmigt. Der Verlauf enthält die vorherige und die neue Fassung, den Akteur, die Aktion und den Grund. Pausen und Projekte werden gemeinsam mit der Anwesenheit freigegeben.
|
|
|
|
Urlaub, Krankheit, Freizeitausgleich, Feiertag und Sonstiges werden als eigene Spannen geführt. Ganztägige Einträge verwenden in der Oberfläche einen ersten und letzten Tag einschließlich. Die API speichert das Enddatum exklusiv. Anwesenheiten und Abwesenheiten dürfen sich nicht überschneiden; bei teilweiser Abwesenheit sind Uhrzeitspannen zu verwenden.
|
|
|
|
## Auswertung
|
|
|
|
Die Auswertung verwendet Europe/Berlin für Tagesgrenzen und begrenzt jede Spanne auf den gewählten Zeitraum. Nachtschichten zählen anteilig zu den jeweiligen Tagen oder Monaten. Zeitumstellungen werden anhand der tatsächlich vergangenen Zeit berücksichtigt. In manuellen Eingaben wird die angegebene IANA-Zeitzone verwendet; nicht existierende Ortszeiten werden im Formular abgelehnt. Unveränderte Zeitpunkte behalten ihren ursprünglichen Offset. Bei neu eingegebenen, doppelt vorkommenden Ortszeiten verlangt das Formular die Eingabe in UTC. Die API akzeptiert Zeitpunkte mit explizitem UTC-Offset.
|
|
|
|
Sollzeit stammt aus den regelmäßigen Wochenarbeitszeiten des Mitarbeiterprofils. Arbeitszeitsummen enthalten nur abgeschlossene Einträge; eingereicht umfasst auch genehmigte Zeiten. Urlaub und Krankheit werden nur genehmigt gutgeschrieben. Ganztägige Abwesenheiten zählen an Arbeitstagen gemäß Sollzeit, zeitweise Abwesenheiten höchstens bis zur Tagessollzeit. Feiertagsausgleich hat Vorrang vor Abwesenheitsgutschriften und zählt pro Datum einmal. Die Summe der Gutschriften überschreitet die Tagessollzeit nicht.
|
|
|
|
Freizeitausgleich und Sonstiges werden ausgewiesen, erzeugen aber keine zusätzliche Zeitgutschrift. Ein freier Arbeitstag zum Überstundenabbau reduziert deshalb den Saldo um seine Sollzeit. Aktuelle Anwesenheit und Pausen sind unabhängig vom ausgewählten Auswertungsmonat sichtbar.
|
|
|
|
## Berechtigungen
|
|
|
|
Eigene Zeiten können angemeldete Mitarbeiter mit Profil im aktiven Mandanten erfassen. Folgende Berechtigungen werden im Backend durchgesetzt:
|
|
|
|
| Berechtigung | Funktion |
|
|
| --- | --- |
|
|
| `staff.time.read_all` | Zeiten anderer Mitarbeiter lesen |
|
|
| `staff.time.write_all` | Zeiten anderer Mitarbeiter lesen, erfassen, korrigieren und einreichen |
|
|
| `staff.time.approve` | Zeiten lesen und eingereichte Zeiten genehmigen oder ablehnen |
|
|
| `staff.time.activate` | Zeiten lesen, Übernahme prüfen und Mitarbeiter umstellen |
|
|
|
|
Administratoren können diese Aktionen ebenfalls ausführen. Die Rechte werden als Berechtigungen der bestehenden Authentifizierungsrollen vergeben. Ein Lese- oder Genehmigungsrecht erlaubt keine Korrektur fremder Zeiten. Die Menüanzeige folgt dem vorhandenen Mandantenmerkmal `staffTime`.
|
|
|
|
## Einführung und Datenübernahme
|
|
|
|
1. Backend und Frontend gemeinsam bereitstellen. Vor Start des neuen Backends die regulären Datenbankmigrationen ausführen (`npm run migrate` im Backend). Die additive Migration `0080_staff_time_spans` legt die neuen Tabellen und Schutzfunktionen an. Sie aktiviert niemanden automatisch.
|
|
2. Als Administrator einen Mitarbeiter unter **2.0 → Zeiterfassung** auswählen und **Übernahme prüfen** anklicken.
|
|
3. Anzahl, Arbeitsminuten und Einträge in der Vorschau prüfen. Offene Einträge, fehlende Enden, unbekannte Ereignisse, uneinheitliche Freigaben oder überlappende Daten aus den beiden alten Modellen verhindern die Aktivierung. Diese Fälle müssen vorab in den Altdaten geklärt werden. Es werden keine Endzeiten erfunden und keine doppelten Einträge stillschweigend verworfen.
|
|
4. **Geprüfte Daten übernehmen und aktivieren** übernimmt die vollständige Historie dieses Mitarbeiters in einer Transaktion. Wenn sich die Altdaten seit der Vorschau geändert haben, muss die Vorschau neu geladen werden. Wiederholte Aktivierung erzeugt keine Duplikate.
|
|
5. Nach Aktivierung kommen auch Lesezugriffe auf historische Zeiträume aus dem neuen Modell. Die alten Datensätze bleiben erhalten; Quellverweise verbinden sie mit den übernommenen Spannen. Alte Schreibzugriffe werden durch Datenbanktrigger gesperrt. Eine Rückkehr zum alten Schreibmodell ist kein unterstützter Schalter.
|
|
|
|
Die erste Aktivierung sollte mit einem Mitarbeiter erfolgen, dessen Zeiten und Salden bekannt sind. Zusätzlich zur Arbeitsminutensumme sollten die Freigaben, Abwesenheitstage und Projektzuordnungen der Vorschau fachlich geprüft werden. Fehler der bisherigen Ereignisrekonstruktion können zu abweichenden, nun korrekt berechneten Ergebnissen führen. Der Import erhält die ursprünglichen Uhrzeitgrenzen; er interpretiert alte Uhrzeitspannen nicht nachträglich als ganztägige Abwesenheit.
|
|
|
|
Vor einer Bereitstellung ist eine Datenbanksicherung nötig. Nach erfolgter Umstellung ist bei Problemen eine Vorwärtskorrektur vorzuziehen. Ein vollständiges Zurücksetzen auf die Sicherung würde zwischenzeitlich erfasste Zeiten verlieren und ist eine gesonderte Betriebsentscheidung.
|
|
|
|
## Bestehende Anbindungen
|
|
|
|
Die bisherigen Schnittstellen für Start, Ende, Einreichen, Genehmigen, Ablehnen und Auswertung erkennen aktivierte Mitarbeiter. Sie arbeiten dann auf den neuen Spannen. Die Webansicht unter Mitarbeiter → Zeiten wechselt für aktivierte Mitarbeiter zur neuen Oberfläche. Dashboard, Anwesenheitsanzeige und Plantafel lesen über diese kompatiblen Schnittstellen. Die Anwesenheitsanzeige berücksichtigt laufende Pausen.
|
|
|
|
Die Mobile-App fasst die kompatiblen Arbeitssegmente zu einer Anwesenheit zusammen und bietet Pausenaktionen für umgestellte Mitarbeiter. RFID-Terminals wechseln zwischen Kommen und Gehen; Gehen beendet eine laufende Pause. Für wiederholte Terminalübertragungen muss derselbe Zeitstempel oder dieselbe `request_id` mitgesendet werden. Scans ohne Zeitstempel und Anfrage-ID gelten jeweils als neue Betätigung. Rückwirkende Offline-Scans werden bei unpassendem Zustand oder Überschneidung abgelehnt und erfordern eine manuelle Korrektur.
|
|
|
|
Interne API-Aufrufe bleiben an den authentifizierten Mandanten gebunden. Öffentliche Formulare legen für umgestellte Mitarbeiter eine vollständige Anwesenheit mit Projektzuordnung an. Dokument und Zeit werden gemeinsam in einer Transaktion geschrieben, damit ein Zeitkonflikt keinen halbfertigen Lieferschein hinterlässt. Alte Bearbeitungs- und Projektänderungsaufrufe verweisen auf die vollständige Bearbeitung unter 2.0, damit Pausen oder Freigaben nicht verloren gehen.
|
|
|
|
## Technischer Aufbau
|
|
|
|
`staff_time_spans` enthält Anwesenheiten und Abwesenheiten mit diskriminierendem Typ, Status und Versionsnummer. Gemeinsam gespeicherte Intervalle erlauben eine einheitliche Prüfung auf Überschneidungen. Zugehörige Tabellen enthalten Pausen, Projektzeiten, Änderungshistorie, Quellverweise sowie aktivierte Mitarbeiter und wiederholungssichere Anfragen. Der Mandantenexport nimmt auch Pausen, Projektzuordnungen und Änderungshistorie mit. Die Dienstschicht liegt unter `backend/src/modules/time-v2`.
|
|
|
|
Die API liegt unter `/api/v2/time`. `GET /context` liefert Mitarbeiterauswahl und Rechte, `GET /spans` die Spannen und den aktuellen Zustand, `GET /evaluation` die Auswertung und `GET /spans/:id/history` den Verlauf. `POST /commands` akzeptiert `user_id`, `request_id` und `command`. Bearbeitungen und Statuswechsel enthalten ID und erwartete Versionsnummer. Gleichzeitige Änderungen werden mit HTTP 409 beantwortet. Ein Wiederholungsversuch muss dieselbe Anfrage-ID und identische Daten verwenden. `GET /migration` erzeugt die Vorschau; `POST /activate` verlangt deren Fingerprint.
|
|
|
|
## Prüfungen
|
|
|
|
Fachliche Tests laufen mit:
|
|
|
|
```sh
|
|
node --import ./backend/node_modules/tsx/dist/loader.mjs --test backend/tests/timeV2.test.ts
|
|
```
|
|
|
|
Die PostgreSQL- und HTTP-Integrationstests benötigen eine ausdrücklich bestimmte, entbehrliche Testdatenbank namens `time_v2_test`:
|
|
|
|
```sh
|
|
TIME_V2_TEST_DATABASE_URL=postgres://USER:PASSWORD@HOST:PORT/time_v2_test \
|
|
node --import ./backend/node_modules/tsx/dist/loader.mjs --test backend/tests/timeV2.test.ts
|
|
```
|
|
|
|
Jeder Integrationstest erzeugt ein eigenes Schema und entfernt es anschließend. Ohne die Variable werden diese Integrationstests übersprungen. Getestet werden unter anderem Pausenabzug, Monatsgrenzen, Zeitumstellungen, Feiertage, Migration, Wiederholungen, Parallelzugriffe, Versionskonflikte, Statuswechsel und Mandantentrennung. Zusätzlich sind Backend-Typecheck und Frontend-Build auszuführen.
|
|
|
|
Die Browserprüfung verwendet ausschließlich simulierte API-Antworten und prüft den gebauten Frontend-Stand. Sie benötigt eine lokale Playwright-Installation und einen laufenden Frontend-Previewserver:
|
|
|
|
```sh
|
|
PLAYWRIGHT_MODULE=/ABSOLUTER/PFAD/playwright/index.mjs \
|
|
TIME_V2_FRONTEND_URL=http://127.0.0.1:3148 \
|
|
node frontend/tests/time-v2.browser.mjs
|
|
```
|