Konzept: Zeiterfassung (§14) + überarbeitetes Rechte-Konzept (§2)

- §14 Zeiterfassung (Kommen/Gehen): TimeEntry append-only/revisionssicher,
  Stempeln per App/Kiosk/Web (NFC/PIN), Compliance (ArbZG/DSGVO/BetrVG)
- §2 neu: mehrere Logins pro Ebene, Rechtegruppen, delegierte Rechtevergabe
  (Rolle <= eigene Ebene + nur eigener Mandanten-Teilbaum), RoleAssignmentVoter

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Thomas Peterson 2026-06-01 13:19:23 +02:00
parent 79e996ab03
commit 46a75f859b

View File

@ -32,9 +32,44 @@
**Hierarchie der Mandanten:** `Plattform → Reseller → Company → Location → Employee/Profil`
### Mehrere Logins pro Ebene
Jede Ebene kann **mehrere Benutzer** haben (mehrere Plattform-, Reseller-, Firmen-Admins).
Technisch: `User` ist die Login-Identität (1 Person = 1 Login), `n` User pro
Reseller bzw. Company. Ein `User` ist optional mit einem `Employee` (Profil) verknüpft —
so wird aus einem Mitarbeiter mit Profil per **Rechtegruppen-Zuweisung** zusätzlich
ein eingeloggter Admin.
### Rechtegruppen (zuweisbare Rollen)
Die Rollen werden im UI als wählbare **Rechtegruppe** angeboten — beim Anlegen/Bearbeiten
eines Benutzers bzw. direkt in der Mitarbeiterliste („kein Login" | „Mitarbeiter" |
„Firmen-Admin" | …). Start = die vier festen Rollen; später optional **granulare
Berechtigungs-Gruppen** (eigene Rechte-Sets pro Mandant).
### Delegierte Rechtevergabe (Kernregel)
Jede Ebene vergibt Rechte **nur an oder unterhalb der eigenen Ebene** und **nur im
eigenen Mandanten-Teilbaum** — niemals nach oben, nie über Mandantengrenzen hinweg:
| Akteur | darf Rechtegruppe vergeben | Geltungsbereich |
|--------|----------------------------|-----------------|
| Plattform-Admin | Plattform-Admin, Reseller-Admin (+ darunter) | alle Mandanten |
| Reseller-Admin | Reseller-Admin (weitere im eigenen Reseller), Firmen-Admin, Mitarbeiter | nur eigener Reseller + dessen Firmen |
| Firmen-Admin | Firmen-Admin (weitere der eigenen Firma), Mitarbeiter | nur eigene Firma |
| Mitarbeiter | — | — |
D. h.: *wir* vergeben Reseller-Rechte, Reseller vergeben Firmen-Admin-Rechte, Firmen-Admins
vergeben Mitarbeiter-/weitere Firmen-Admin-Rechte. Same-Level-Vergabe (weitere Admins der
eigenen Ebene) ist erlaubt → ermöglicht mehrere Logins pro Ebene.
Durchsetzung über:
- **Doctrine-Filter** (automatisches Scoping nach `reseller_id` / `company_id` je nach eingeloggtem Kontext)
- **Security Voters** (z. B. `ProfileVoter`, `CompanyVoter`) für feingranulare Aktionen
- **Doctrine-Filter / API-Platform-Query-Extension** (automatisches Scoping nach
`reseller_id` / `company_id` je nach eingeloggtem Kontext)
- **Security Voters** (`CompanyVoter`, `EmployeeVoter`) für Aktion×Objekt
- **`RoleAssignmentVoter`/-Service**: prüft bei jeder Rollen-/Rechtegruppen-Vergabe,
dass die Zielrolle ≤ der höchsten Rolle des Akteurs ist **und** der Ziel-Benutzer im
Mandanten-Teilbaum des Akteurs liegt (Schutz vor Privilege-Escalation)
- API-Platform `security`-Attribute pro Resource & Operation
---
@ -75,7 +110,7 @@ und legen sie versioniert ab. Öffentliche Endpunkte liefern immer den aktuellen
### Kernentitäten
- **User** — Auth-Identität. Felder: `email`, `password`, `roles[]`, `status`, `lastLogin`. Verknüpft optional mit `reseller_id`, `company_id`, `employee_id` (je nach Rolle).
- **User** — Auth-Identität (1 Login). Felder: `email`, `password`, `roles[]` (= Rechtegruppe), `status`, `lastLogin`. Verknüpft optional mit `reseller_id`, `company_id`, `employee_id`. **Mehrere User pro Reseller/Company** möglich; Rollenvergabe ist delegiert & scope-geprüft (siehe §2).
- **Reseller** — oberster Mandant. Felder: `name`, `slug`, `primaryDomain`, Branding-Defaults, `status`, `platformPlan_id`. Hat Limits aus dem Plattform-Paket.
- **PlatformPlan** — Reseller-Pakete (Starter/Professional/Business). Felder: `name`, `pricePerMonth`, `maxProfiles`, `maxCompanies`, `features[]`.
- **ResellerSubscription** — Abo des Resellers bei der Plattform. Felder: `plan_id`, `status`, `startedAt`, `renewsAt`, `paymentRef` (Stripe).
@ -391,3 +426,74 @@ Font-Embedding ist zugleich Voraussetzung für striktes **PDF/X**.
`CardTemplate.fonts` (`[{family, path}]`). Dateien liegen außerhalb des Webroots unter
`var/storage/cards/{companyId}/`. Upload-Endpunkte:
`POST /api/companies/{id}/card-template/background` (PDF) und `.../font` (TTF/OTF).
---
## 14. Zeiterfassung (Modul „Kommen/Gehen")
Erweiterung über die digitale Visitenkarte hinaus: eine **Arbeitszeiterfassung**
für die Firmenkunden der Reseller (vorzugsweise Druckshops). Der Mitarbeiter ist
die gemeinsame Klammer — dieselbe Identität (`Employee`), dieselben Standorte,
dieselbe NFC/QR-Infrastruktur. Positioniert die Plattform vom „Identity/Print-Tool"
hin zur **Mitarbeiter-Plattform** (ein Datensatz, mehrere Module).
### Markttreiber
EuGH 2019 + BAG 2022 → **Pflicht zur Arbeitszeiterfassung** in DE. SMB-Kunden
suchen einfache Lösungen → starkes Verkaufsargument; NFC-Terminals/Badges sind
zusätzlicher **Hardware-Umsatz** für den Druckshop.
### Erfassungswege
- **Web/App** (authentifizierter Mitarbeiter über sein `User`-Konto): Button
„Kommen / Gehen / Pause" im Dashboard bzw. Handy-Browser. Quelle `web`/`app`.
- **Kiosk** (geteiltes Standort-Gerät, per Geräte-Token): Mitarbeiter identifiziert
sich per **NFC-Tap** (`shortCode`) oder **PIN**. Quelle `kiosk`.
- NFC/QR nutzt die bestehende `shortCode`/`/t/`-Infrastruktur. *Hinweis:* da der
Profil-`shortCode` halb-öffentlich ist, am Kiosk zusätzlich **PIN** empfehlen.
### Datenmodell
**`TimeEntry`** — append-only Stempel-Ereignis (revisionssicher, **kein** Update/Delete):
`employee`, `type` (`clock_in`|`clock_out`|`break_start`|`break_end`),
`occurredAt` (Stempelzeit), `recordedAt` (Speicherzeit, immutable),
`source` (`web`|`app`|`kiosk`|`nfc`|`qr`|`manual`), `location` (nullable),
`createdBy` (User), `note`, `status` (`active`|`corrected`|`voided`),
`correctsEntry` (self-ref, nullable), `reason` (bei Korrektur/Storno).
**`KioskDevice`** — registriertes Standort-Terminal: `name`, `token`, `location`,
`status`, `lastSeenAt`.
**`Employee`-Ergänzung:** `clockPin` (optional, für Kiosk/App-Identifikation).
**Korrekturprinzip:** Originale bleiben erhalten; eine Korrektur erzeugt einen
**neuen** `TimeEntry` (`type=correction`-Bezug via `correctsEntry`) mit Grund +
Bearbeiter, der alte wird als `corrected` markiert. Der „gültige" Stundenzettel
wird aus den `active`-Einträgen berechnet. Jede Markierung ist protokolliert →
nachvollziehbar/manipulationssicher.
### Endpunkte (grob)
- `POST /api/time-clock/punch` — eingeloggter Mitarbeiter stempelt (Typ ermittelt
oder explizit), Quelle web/app.
- `POST /api/kiosk/punch` — Geräte-Token + Mitarbeiter-Kennung (NFC-`shortCode`/PIN).
- `GET /api/time-entries` — mandantengescoped (Mitarbeiter: eigene; Firmen-Admin: alle).
- `GET /api/timesheets?employee=&period=` — Aggregation (Tag/Woche, Überstunden, je Standort).
- `POST /api/time-entries/{id}/correct` — Firmen-Admin, hängt Korrektur an (Audit).
### Rollen
- **Mitarbeiter**: stempelt, sieht eigene Zeiten (Transparenzpflicht).
- **Firmen-Admin**: Stundenzettel, Korrekturen (mit Grund/Audit), Export, Kiosk-Geräte.
- **Reseller/Plattform**: white-label Einstellungen, Limits.
### Compliance (bewusst zu behandeln)
- **Revisionssicherheit**: append-only + protokollierte Korrekturen (s. o.).
- **ArbZG**: Pausen/Höchstarbeits-/Ruhezeiten erfassbar bzw. warnend.
- **DSGVO + BetrVG §87** (Mitbestimmung): kein heimliches Tracking, Einsicht für
Mitarbeiter, Standort EU, Lösch-/Exportkonzept.
### Auswertung & Export
Stundenzettel (Tag/Woche/Monat), Überstunden, Pausensummen je Mitarbeiter/Standort;
Export CSV/PDF — später Lohn-Schnittstelle (z. B. DATEV).
### MVP-Abgrenzung
**Drin:** Kommen/Gehen/Pause über Web/App/Kiosk (NFC/PIN), Stundenzettel,
revisionssichere Korrektur, CSV/PDF-Export. **Draußen (Folgeschritte):** Urlaub/
Krankmeldung, Schicht-/Dienstplanung, Projektzeiten, DATEV-Export, Geofencing.