feat: perinco Pulse - Zeiterfassung fuer perinco

Erste Fassung, live unter https://pulse.perinco.de.

Frontend React 19 + TypeScript + Tailwind 4 (Vite), Backend Express mit
mysql2 auf MariaDB, Exporte als PDF (pdfkit), Excel (exceljs) und CSV.
Aufbau, Designsystem und die sieben Themes kommen aus dem perinco CRM.

Fachlich:
- Buchung mit Wochen-, Monats- und Jahresansicht, Zeiten als 1:23 oder 1,5
- Projekte mit n Budgettoepfen: Laufzeit, Volumen, Sperre bei Ablauf oder
  aufgebrauchtem Budget, Buchung nur auf den passenden Topf
- Kunden, Team mit Hierarchie, Reports im Harvest-Stil, Monatsexporte
- Rollen user/lead/management/admin, doppelt geprueft (Menue und Backend)
- SSO gegen Microsoft Entra ID, Uebernahme der AD-Benutzer per Drag & Drop

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-17 22:52:20 +02:00
co-authored by Claude Opus 5
commit ef676a95f4
104 changed files with 21030 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
# perinco Pulse
Zeiterfassung für perinco – live unter **https://pulse.perinco.de**.
Stil, Themes und Aufbau folgen dem perinco CRM.
| Teil | Technik | Wo |
| --- | --- | --- |
| Frontend | React 19, TypeScript, Tailwind 4, Vite | `/var/www/pulse/dist` |
| Backend | Express 4, mysql2, exceljs, pdfkit | `/var/www/pulse/server`, `127.0.0.1:5010`, Dienst `perinco-pulse-backend` (User `pulse`) |
| Datenbank | MariaDB 11.8 | `perinco_pulse` (User `pulse`) |
| Webserver | Apache Vhost `pulse.perinco.de`, Let's Encrypt | `/etc/apache2/sites-available/pulse.perinco.de*.conf` |
Erster Login: Benutzer `admin`, Passwort aus `ADMIN_PASSWORD` in `server/.env` (wird beim ersten Start einmalig angelegt) – danach unter *Mein Konto* (unten links auf den Namen klicken) ändern.
## Rollen und Menü
| Rolle | Startseite | Menü |
| --- | --- | --- |
| Mitarbeiter (`user`) | Buchung | Buchung, Projekte (nur zugewiesene, ohne Beträge) |
| Vorgesetzter (`lead`) | Buchung | Dashboard (nur eigenes Team, rekursiv), Buchung, Projekte, Team, Kunden, Reports |
| Management | Dashboard | wie Vorgesetzter, sieht alles; Einstellungen nur „SSO-Verwaltung“ |
| Administrator | Einstellungen | Dashboard, Projekte, Team, Kunden, Reports, Einstellungen – keine Buchung, taucht in Auswertungen nicht auf |
Die Startseite gilt nach jeder Anmeldung (Passwort und SSO), auch wenn noch eine alte Seite in der Adresse steht; Neuladen bleibt auf der aktuellen Seite. Das Logo oben links führt ebenfalls zur Startseite.
Rechte werden im Menü **und** im Backend (403) geprüft. Jeder bucht nur auf Projekte, denen er zugewiesen ist.
## Fachregeln
- **Zeiteingabe**: `1:23` = 1 h 23 min, `1,5` / `1.5` = 1 h 30 min, außerdem `2`, `:45`, `1h 30m`.
- **Projektcode** automatisch nach Muster (Standard `{KUERZEL}-{JAHR}-{NR}` → `KUKA-2026-001`), **Anzeigename** nach Muster (Standard `{KUNDE} - {PROJEKT}` → `KUKA - PMO`). Beides in *Einstellungen → Allgemein*.
- **Projekttypen**: Billable (Preis pro Stunde oder Tag), Non-billable (intern), Festpreis (virtueller Stundensatz = Festpreis ÷ gebuchte Stunden, nur intern).
- **Budgettöpfe** (mind. einer je Projekt, beliebig viele): Bezeichnung, Laufzeit von/bis, Budget € + Satz (→ Volumen in Stunden/Tagen) oder direktes Kontingent.
- Gebucht wird nur auf einen Topf, dessen **Laufzeit das Leistungsdatum enthält**; überlappen Töpfe, zuerst der früher endende.
- Volumen erreicht **oder** Ablaufdatum überschritten → Warnung **und nicht mehr buchbar**.
- Bei Billable-Projekten verbrauchen nur abrechenbare Stunden das Volumen (Reisezeit o. ä. nicht); ist ein Topf leer, ist er für alles gesperrt.
- Warnung ab 80 % Verbrauch bzw. 14 Tage vor Ablauf (einstellbar) – im Buchungsdialog, in der Buchungsansicht und an der Projektkarte.
- Wird ein Topf geändert, ordnet Pulse vorhandene Buchungen neu zu.
- **Umsatz erbracht/offen** je Topf: Billable = abrechenbare Stunden × Satz; Festpreis = anteilig nach Kontingent (sonst nach Laufzeit), abgelaufen = voll; offen = Rest in nicht abgelaufenen Töpfen.
- **Soll/Auslastung**: Wochenstunden (Standard 40, je Benutzer überschreibbar) ÷ 5 × Arbeitstage bis heute.
- **Löschen**: Benutzer, Projekte, Kunden mit Buchungen werden nicht gelöscht, sondern deaktiviert/archiviert – Reports bleiben stimmig. SSO-Benutzer werden nur deaktiviert.
## Exporte
Serverseitig erzeugt, alle mit Kopf/Fuß aus *Einstellungen → Export* (Logo, Akzentfarbe, Titel, 3 Fußspalten, Unterschriftenfelder):
- **Stundennachweis je Monat** (PDF, Excel mit Summenformeln, CSV mit UTF-8-BOM/Semikolon): eigener Nachweis, eines Teammitglieds oder aller; filterbar nach Kunde/Projekt; optional mit Sätzen und Beträgen für die Rechnung.
- **Zeitreport** nach Kunden/Projekten/Tasks/Mitarbeitern oder als Detailliste.
## SSO / Active Directory
**Einrichtung** (nur Administrator, *Einstellungen → SSO / Entra ID*): Microsoft Entra ID per OpenID Connect (Authorization Code, Client Secret AES-verschlüsselt in der DB). App-Registrierung mit Redirect-URI `https://pulse.perinco.de/api/auth/sso/callback`, delegiert `openid profile email`, Anwendung `User.Read.All` mit Administratorzustimmung.
**SSO-Verwaltung** (Management und Administrator, *Einstellungen → SSO-Verwaltung*):
- Links alle zulässigen AD-Konten, rechts die Rollen Mitarbeiter / Vorgesetzter / Management. Konto auf eine Rolle **ziehen** = nach Pulse übernehmen bzw. Rolle ändern. Alternativ Mehrfachauswahl + Knopf (Handy, Tastatur). Optional gleich einen Vorgesetzten setzen.
- Zulässig sind nur Konten mit Mail **`vorname.nachname@perinco.de`** oder **`vorname.nachname@icon-experts.de`** (Bindestriche im Namen erlaubt). Ignoriert werden Adressen mit weiteren Teilen (`adm.sascha.wolf@…`), Funktionsadressen (`info@…`), deaktivierte Konten und Konten ohne Lizenz (freigegebene Postfächer) sowie Gäste. Regel: `server/src/lib/sso.js` → `isEligible`, Tests: `cd server && npm test`.
- Existiert schon ein lokales Konto mit derselben Mail, wird es verknüpft statt doppelt angelegt.
- **Anmelden per Microsoft kann sich nur, wer übernommen wurde.** Rollen kommen ausschließlich aus Pulse, AD-Gruppen spielen keine Rolle. Administratoren können hier nicht geändert und nicht vergeben werden.
- **Abgleich**: aktualisiert Name, Mail und Jobtitel der übernommenen Konten; wer im AD gelöscht, deaktiviert oder nicht mehr zulässig ist, wird in Pulse deaktiviert. Legt nie selbst Konten an. Die AD-Liste wird 5 Minuten zwischengespeichert („Neu laden“ holt sofort).
- Externe Benutzer bleiben lokale Konten.
## Entwicklung
```bash
npm install && (cd server && npm install)
cp server/.env.example server/.env # Werte setzen
# DB z. B. per Tunnel: plink -L 3307:127.0.0.1:3306 root@31.70.144.60 (DB_PORT=3307, DB_NAME=perinco_pulse_dev)
(cd server && npm run dev) # Backend :5010
npm run dev # Frontend :3110, proxyt /api
```
`perinco_pulse_dev` enthält Demodaten (Kunden, Projekte, ~1000 Buchungen, Testbenutzer mit Passwort `Test12345!`) und wird von der Produktion nicht benutzt.
## Deployment
```powershell
$env:PULSE_SSH_PASSWORD = '…'
powershell -File deploy/deploy.ps1 # baut, lädt hoch, tauscht dist atomar, startet Dienst neu
powershell -File deploy/deploy.ps1 -Deps # zusätzlich npm ci im Backend
```
Die Produktions-`.env` liegt nur auf dem Server (`/var/www/pulse/server/.env`, 600). `SETTINGS_KEY` nicht ändern, sonst ist das gespeicherte SSO-Secret unlesbar.
## Anforderungs-Checkliste
Aus Auftrag und Call-Mitschrift.
| Anforderung | Status | Wo |
| --- | --- | --- |
| Dashboard Mgmt/Admin: Billable/Non-billable-Grafik, umschaltbar Tag/Woche/Monat/Quartal/Jahr, animiert | ✅ | Dashboard |
| Letzte Buchungen | ✅ | Dashboard |
| Mitarbeiter-Übersicht auf-/absteigend, letzte Tage/Woche/Monat/Quartal/Jahr, Filter billable/non-billable | ✅ animierte Umsortierung | Dashboard |
| Vorgesetzte sehen nur ihr Team | ✅ rekursiv, auch im Backend | Dashboard, Reports, Buchung (Teamansicht, nur lesen) |
| Buchung: Wochenansicht Standard, umschaltbar Monat/Jahr | ✅ | Buchung |
| Grünes Plus → Projekt, Task, optionaler Freitext, Zeit rechts daneben, `0:00`, `1:23` und `1,5` | ✅ | Buchungsdialog |
| Nur zugewiesene Projekte, Task optional | ✅ | Buchungsdialog |
| Zeiten bearbeiten und löschen | ✅ | Klick auf Buchung |
| Monatsexport Excel/PDF/CSV als Kundenvorlage mit Logo und Footer | ✅ (Footer-Inhalt siehe Hinweis) | Buchung → Export |
| Menü je Rolle (Dashboard/Buchung, Projekte, Team, Kunden, Reports, Einstellungen) | ✅ | Seitenleiste / mobile Leiste |
| Projekte: User nur zugewiesene; andere alle inkl. Töpfe, bearbeiten/anlegen/entfernen | ✅ | Projekte |
| Projekt wählen → Tasks darunter | ✅ | Projekte |
| Projektanlage: Name, Code automatisch, Start/Ende, Budget, Tags, Notizen, Typ billable/non-billable/Festpreis, Stunde/Tag | ✅ | Neues Projekt |
| Budgettopf: Laufzeit, Budget €, Stundensatz, Bezeichnung; n Töpfe; Balken Stunden/Tage/Budget | ✅ | Projekte |
| Hinweis beim Buchen, wann Budget endet und wieviel offen ist | ✅ | Buchungsdialog, Buchung |
| Topf voll oder abgelaufen → nicht buchbar | ✅ serverseitig erzwungen | |
| Nur auf Topf buchen, dessen Laufzeit die Leistung enthält | ✅ | |
| Töpfe bearbeiten ab Vorgesetzter | ✅ | Projekte |
| Festpreis: virtueller Stundensatz intern | ✅ | Projekt, Projektreport |
| Report: Gesamt aller Töpfe und einzelne Töpfe | ✅ | Projektreport → Budgettöpfe |
| Projekte Usern zuweisen | ✅ | Projekt → Zuweisen / Formular |
| Kunden über Projekten | ✅ | Kunden, Projektformular (+ Kunde) |
| Team: Hierarchie Vorgesetzte/Mitarbeiter, lokale User anlegen/bearbeiten/löschen, Externe | ✅ Organigramm + Liste | Team |
| Kunden anlegen/bearbeiten, wer hat welche Projekte mit welchen Mitarbeitern, Umsatz erbracht/offen, animiert | ✅ | Kunden |
| Reports wie Harvest (Report.png), interaktiv, Export Excel/PDF/CSV | ✅ | Reports → Zeitreport |
| Report für einzelne Kunden / Projekt wie Report-Kunde.png | ✅ | Reports → Kunden-/Projektreport |
| Einstellungen (nur Admin): SSO-Credentials, Userverwaltung, Kopf/Fuß der Exporte | ✅ inkl. PDF-/Excel-Vorschau | Einstellungen |
| Arbeitsstunden pro Woche (40) | ✅ | Einstellungen → Allgemein |
| Projektbezeichnungen nach Muster | ✅ | Einstellungen → Allgemein |
| Themes wie CRM (perinco, Pixel Art, Outdoor, Cyberpunk, Rainy, Snow, Aura – jeweils hell/dunkel, am Benutzer gespeichert) | ✅ | Kopfleiste |
| Lokaler Admin mit vorgegebenem Passwort, später änderbar | ✅ | Mein Konto |
| Mobil: eigene Ansicht bei Handy | ✅ untere Navigation, Tagesstreifen, Bottom-Sheets, schwebender Plus-Knopf | |
| SSO, anfangs normaler Login; AD-User auslesen, Rollen intern | ✅ vorbereitet, noch nicht gegen echten Tenant getestet | Einstellungen → SSO |
### Offene Punkte
- **Wordvorlage** lag nicht im Ordner. Kopf nutzt das perinco-Logo; die Fußzeilen (Adresse, Registergericht, Bank) bitte in *Einstellungen → Export* eintragen.
- **SSO** ist implementiert, braucht aber eine App-Registrierung in Entra ID zum Test.
- Nicht umgesetzt, weil nicht gefordert: Rechnungsmodul („Uninvoiced amount“ aus Harvest), gespeicherte Reports.