docs: Story 5.2 — .env.example, CHANGELOG, README + CLAUDE.md auf 3-Varianten + Mehrbenutzer aktualisiert

This commit is contained in:
Kenearos 2026-07-07 23:18:55 +02:00
parent d18bd30adf
commit 4f0f143d68
4 changed files with 82 additions and 30 deletions

33
.env.example Normal file
View file

@ -0,0 +1,33 @@
# Dienstplan-Pro — Team-Release v1.0 Konfiguration
# Diese Datei ist eine VORLAGE. Echte Secrets niemals committen.
# ── Admin (Pflicht) ──
# Fehlt/ungültig → Server startet NICHT (Fail-Fast). Bestehende Daten werden diesem Admin zugeordnet.
ADMIN_EMAIL=admin@deine-domain.de
# ── App ──
APP_BASE_URL=https://bonus.pixel-by-design.de
PORT=3000
DATA_DIR=/data
# ── Sitzungen / Tokens ──
SESSION_TTL_DAYS=30
SESSION_IDLE_HOURS=8
TOKEN_TTL_MIN=30
# ── Rate-Limit (E-Mail primär; IP großzügig wegen Klinik-NAT) ──
RATE_LIMIT_EMAIL=5
RATE_LIMIT_WINDOW_MIN=15
RATE_LIMIT_IP=50
# ── SMTP (Magic-Link-Versand). Ohne SMTP_HOST → Link erscheint nur in der Server-Konsole. ──
# WICHTIG: SPF + DKIM beim Provider einrichten, sonst landen Links im Spam.
SMTP_HOST=smtp.deine-domain.de
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=dienstplan@deine-domain.de
SMTP_PASS=
SMTP_FROM=Dienstplan-Pro <dienstplan@deine-domain.de>
# ── Nur lokaler HTTP-Dev: Session-Cookie ohne Secure-Flag. In Produktion NICHT setzen. ──
# COOKIE_INSECURE=true

22
CHANGELOG.md Normal file
View file

@ -0,0 +1,22 @@
# Changelog
## v1.0.0 — Team-Release (2026-07-07)
### Neu — Mehrbenutzer
- **Magic-Link-Login** (passwortlos): Kollegen melden sich mit freigeschalteter Arbeits-E-Mail an; Login-Link scanner-sicher (Bestätigungsseite gegen Mail-Prefetch), Sitzung mit absoluter + Inaktivitäts-Frist, Logout.
- **Getrennte Datenbasis pro Nutzer** (server-erzwungen, `user_id` nur aus der Session — Anti-IDOR). Auch der Admin sieht nur seine eigenen Daten.
- **Admin-Nutzerverwaltung**: E-Mails freischalten / auflisten / entfernen mit Last-Admin-Schutz; Notzugangs-Link (audit-geloggt).
- **Migration** der bisherigen globalen Daten auf den Admin — atomar, idempotent, Fail-Fast bei fehlender `ADMIN_EMAIL`.
### Sicherheit
- Login-Token & Session-IDs nur als SHA-256-Hash gespeichert; Rohwert nur im Link/Cookie.
- `foreign_keys=ON` + `ON DELETE CASCADE`; httpOnly/Secure/SameSite=Lax-Cookies (secure-by-default).
- Rate-Limit (E-Mail primär, IP großzügig), neutrale Auth-Antworten, atomarer Token-Claim (TOCTOU-frei).
- Minimales, PII-freies Audit-Log (Login/Logout/Admin-Aktionen/Fehlversuche) mit pseudonymer `user_id`.
### Unverändert übernommen
- Server-Persistenz (SQLite/WAL) + tägliches Online-Backup + Offline-fähiger Sync.
- Foto-Import (Vision-LLM) und 3-Varianten-Bonusberechnung (NRW Psychiatrie 2011).
### Bewusst nicht in v1
- DSGVO-Rechtstexte/Löschkonzept (internes Team, Risiko akzeptiert), Passwort-Login, gemeinsame Datenbasis.

View file

@ -54,34 +54,28 @@ Script loading order in `index.html` is critical:
## Business Logic
### Qualifying Days (WE/Feiertag)
### Tag-Klassifizierung (Slot pro Dienst)
A day is "qualifying" (eligible for higher bonus rate) if ANY of:
- **Weekend**: Friday (5), Saturday (6), or Sunday (0)
- **Public Holiday**: Any NRW state holiday
- **Day Before Holiday**: The calendar day preceding a public holiday
Jeder Dienst bekommt genau einen Slot (`variants.js``classify`):
- **fr**: echter Freitag · oder Tag vor einem MoDo-Feiertag
- **sa**: echter Samstag · oder Sandwich-Tag (Feiertag UND Tag davor)
- **so**: echter Sonntag · oder MoDo-Feiertag (ohne Sandwich)
- **weekday**: MoDo ohne Feiertagsbezug
### Bonus Calculation Rules
Echte Fr/Sa/So gewinnen immer. Sätze: `fr/sa/so` = 450 €, `weekday` = 250 €; Anteile 0,5 oder 1,0.
```
Constants:
- RATE_NORMAL = 250€ (normal weekday rate)
- RATE_WEEKEND = 450€ (qualifying day rate)
- MIN_QUALIFYING_DAYS = 2.0 (threshold)
- DEDUCTION_AMOUNT = 2.0 (deducted from qualifying days)
### Bonus-Berechnung (3 Varianten — `variants.js`, NICHT mehr das alte 2.0-Schwellen-Modell)
Algorithm:
1. Count qualifying days (Friday, Sat, Sun, holidays, day-before-holiday)
2. Count normal days (Mon-Thu, not holiday-related)
3. If qualifyingDays < 2.0: NO BONUS (total = 0€)
4. If qualifyingDays >= 2.0:
- Deduct 2.0 from qualifying days (Friday priority)
- Bonus = (normalDays × 250€) + (remainingQualifyingDays × 450€)
```
`calculator.js` läuft alle drei Varianten und nimmt die mit dem höchsten Betrag (Gleichstand → niedrigste Variantennummer). Werte normal (Urlaub = halbiert):
- **V1** — greift wenn `fr+so ≥ 1` UND `weekday ≥ 3`; Abzug 1 aus fr+so (Fr-Priorität) + 3 weekday; `sa` wird voll bezahlt.
- **V2** — greift wenn `sa ≥ 1` UND `weekday ≥ 2`; Abzug 1 sa + 2 weekday; `fr`/`so` werden voll bezahlt.
- **V3** — greift wenn `fr+sa+so ≥ 2`; Abzug 2 aus dem Pool (Reihenfolge fr → so → sa); `weekday` wird voll bezahlt.
### Friday Priority Deduction
**Urlaubsmodus** (Flag pro Mitarbeiter/Monat) halbiert alle Schwellen und Abzüge. Maßgeblich ist `variants.js`; die In-App-Doku (Einstellungen → Berechnungsregeln) ist aktuell.
When deducting the 2.0 qualifying days, Fridays are deducted first before Saturday/Sunday/holidays. This is tracked via `qualifyingDaysFriday` and `qualifyingDaysOther` in the calculator.
### Mehrbenutzer (v1.0)
Seit dem Team-Release ist die App hinter Magic-Link-Login; Daten sind pro Nutzer (`user_id`) getrennt. Backend in `server/` (auth.js, index.js, mailer.js, ratelimit.js, audit.js). Konfiguration via Env-Variablen — siehe `.env.example` (u.a. `ADMIN_EMAIL` Pflicht/Fail-Fast, SMTP, Session-Fristen).
### Duty Shares

View file

@ -8,10 +8,18 @@ Eine Web-Anwendung zur Berechnung von Bonuszahlungen für Wochenend- und Feierta
- ✅ **Dienstplanung**: Dienste für beliebige Monate eintragen (ganze und halbe Dienste)
- ✅ **Automatische Feiertagserkennung**: NRW-Feiertage 2025-2030
- ✅ **Bonusberechnung**: Automatische Berechnung nach festgelegten Regeln
- ✅ **Team-Login (v1.0)**: Passwortlose Anmeldung per Magic-Link; jeder Nutzer hat eine eigene, getrennte Datenbasis
- ✅ **Server-Persistenz + Offline-Sync**: Daten liegen serverseitig (SQLite) und synchronisieren; LocalStorage als Offline-Cache
- ✅ **Datenexport/Import**: JSON-Export für Backup und Migration
- ✅ **LocalStorage**: Alle Daten werden lokal im Browser gespeichert
- ✅ **Responsive Design**: Funktioniert auf Desktop und Mobilgeräten
## Team-Betrieb (v1.0)
- **Anmelden:** freigeschaltete Arbeits-E-Mail eingeben → Login-Link per Mail → bestätigen. Kein Passwort. Sitzung ~30 Tage (bzw. 8 h Inaktivität).
- **Admin** (per `ADMIN_EMAIL` beim ersten Start angelegt) verwaltet unter **Einstellungen → Konto & Team**, wer teilnehmen darf, und behält den Team-Blick; **reguläre Nutzer** erfassen nur ihre eigene Person.
- **Datentrennung** ist serverseitig erzwungen — niemand sieht fremde Daten.
- Konfiguration: siehe `.env.example`. SMTP mit SPF/DKIM einrichten, sonst landen Login-Links im Spam.
## Berechnungsregeln
### Qualifizierende Tage (WE/Feiertag)
@ -19,13 +27,8 @@ Eine Web-Anwendung zur Berechnung von Bonuszahlungen für Wochenend- und Feierta
- **Feiertage**: Alle gesetzlichen Feiertage in NRW
- **Tag vor Feiertag**: Der Tag vor einem gesetzlichen Feiertag
### Bonusberechnung
1. **Schwellenwert**: Mindestens **2.0 qualifizierende Tage** im Monat erforderlich
2. **Abzug**: Bei Erreichen des Schwellenwerts werden **2.0 qualifizierende Tage** abgezogen (Freitag-Priorität)
3. **Vergütung**:
- Normale Tage: **250€** pro Tag
- Qualifizierende Tage (WE/Feiertag): **450€** pro Tag
- Halbe Dienste: Jeweils die Hälfte
### Bonusberechnung (NRW Psychiatrie 2011 — 3 Varianten)
Jeder Dienst wird in einen Slot klassifiziert (`fr`/`sa`/`so` = **450 €**, `weekday` = **250 €**; inkl. Feiertags-Verschiebung). Der Bonus wird über **drei Varianten** (V1/V2/V3) berechnet — es gewinnt die mit dem höchsten Betrag; bei Gleichstand die niedrigste Variantennummer. Der **Urlaubsmodus** halbiert Schwellen und Abzüge. Die vollständigen Regeln (Schwellen/Abzüge je Variante) stehen in der App unter **Einstellungen → Berechnungsregeln**.
### Beispiel
Mitarbeiter hat im Monat: