_get_holiday_dates() fragte nur PublicHoliday für data.start_date.year ab.
Ein Antrag über den Jahreswechsel (z.B. 28.12.-01.01.) rechnete den
Neujahrsfeiertag im Folgejahr daher nicht heraus -> Mitarbeiter verlor
einen Urlaubstag, den er nicht verbraucht hat. Neuer year_to-Parameter
deckt den Jahres-Range ab, beide Aufrufstellen (create_absence,
update_absence) angepasst. Regressionstest ergänzt.
Gefunden während Coverage-Arbeit an absence_service.py/report_service.py.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Ahyx6D3r7G1EuAc42nezn
Optionale zweistufige Freigabe (Feature-Parität mit Urlaubsverwaltung,
Second-Stage-Authority), ohne SSO:
- Firmen-Opt-in companies.two_stage_approval_enabled + two_stage_min_days
(nur Anträge ab X Arbeitstagen brauchen Stufe 2; 0 = alle).
- Ablauf PENDING → FIRST_APPROVED → APPROVED: erste Stufe durch Manager-Rollen,
finale Stufe nur HR/Admin und zwingend eine ANDERE Person als Stufe 1.
- Urlaubs-/FZA-Abzug, CalDAV-Sync und Vertreter-Mail erst bei finaler Genehmigung.
Ablehnen in beiden Stufen möglich; Eigentümer darf FIRST_APPROVED noch stornieren.
- pending_days, Kalender und Reminder-Digest berücksichtigen FIRST_APPROVED.
- Neuer Status-Wert + absences.first_approved_by; System-Kommentar bei Stufe 1.
Frontend: CompanySettingsPage (Toggle + Schwellwert), AbsencesPage
("Endgültig genehmigen"/Ablehnen für HR/Admin ≠ Erstgenehmiger, Status-Badge).
Migration 0038. 190/190 Tests grün. Deployed 137 + 164.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Geplante Erinnerungen (Feature-Parität mit Urlaubsverwaltung):
- APScheduler (AsyncIOScheduler) in der FastAPI-Lifespan; tägliche Jobs ab
settings.reminder_hour. Redis-Tageslock gegen Doppelversand bei mehreren
Prozessen; jeder Job mit eigener Session + RLS-Bypass.
- Drei Jobs (auch einzeln aufrufbar): offene Anträge an Genehmiger,
Resturlaub-Verfall-Vorwarnung an Mitarbeiter, fehlende AU an HR.
- Pro-User notification_prefs (JSONB, opt-out); GET/PATCH /users/me/notification-prefs
+ ProfilePage-UI; Vertreter-Mail respektiert die Prefs.
- Manueller Trigger POST /companies/me/run-reminders (Admin) – gleiche Logik,
firmen-scoped (testbar ohne Warten).
- Bugfix: GET-/PATCH-Urlaubskonto (update_balance) nutzte nicht existente
Felder (base_days/carried_over_days/ip_address) → korrigiert auf
entitled_days/carried_over/ip + company_id; available_days ergänzt.
Migration 0037 (users.notification_prefs). 188/188 Tests grün. Deployed 137 + 164.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Abwesenheits-Modul abgerundet (Feature-Parität mit Urlaubsverwaltung):
- Vertretung: Overlap-Warnung beim Anlegen, E-Mail an Vertretung bei
Genehmigung, GET /absences/?as_substitute=true, neuer schlanker
GET /users/colleagues (alle Rollen, RLS-gefenced) für die Auswahl;
Vertreter-Dropdown + Anzeige in der Liste.
- Stornierung mit Re-Genehmigung: neuer Status CANCELLATION_REQUESTED,
POST /absences/{id}/request-cancellation; Manager genehmigt/lehnt über
bestehende approve/reject ab (Urlaub + FZA-Rückbuchung via _apply_cancellation).
- Kommentare: Model AbsenceComment (company_id-RLS), GET/POST comments,
System-Kommentare bei Statuswechsel, AbsenceCommentsModal.
- Fix: CalDAV fire-and-forget nutzte die Request-Session weiter (in Tests
geteilt -> "another operation in progress"); jetzt sync_*_bg mit eigener
Session + RLS-Bypass.
Migration 0035. 178/178 Tests grün. Deployed auf 137 + 164.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- old_value enthält den tatsächlichen Status vor der Stornierung (pending/approved)
- new_value enthält cancelled_by_name für bessere Lesbarkeit
- fza_hours_refunded: true wird gesetzt wenn ein genehmigter FZA storniert wurde
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>