diff --git a/docs/KONZEPT.md b/docs/KONZEPT.md index f73146b..fb78aef 100644 --- a/docs/KONZEPT.md +++ b/docs/KONZEPT.md @@ -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.