feat(overtime): Auszahlungs-Anträge durch Mitarbeiter (opt-in pro Firma)

Mitarbeiter beantragt Überstunden-Auszahlung, HR genehmigt/lehnt ab.
Firmen-Opt-in payout_request_enabled (Default aus - nur HR-Direktbuchung).
Saldo-Abzug erst bei Genehmigung; Reject/Cancel bucht nichts.

- Migration 0042: hours_payouts.status/decided_by/decided_at/rejection_reason
  + companies.payout_request_enabled (nur Spalten, RLS unveraendert)
- Router: POST /hr/payouts/request|{id}/approve|reject|cancel; list status-Filter;
  HR-Direktbuchung bleibt (status approved)
- Frontend: PayoutRequestCard (Selbstbedienung in AbsencesPage), HR-Page
  Status-Spalte + Genehmigen/Ablehnen, CompanySettings-Toggle
- 4 pytest-Cases

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 22:43:26 +02:00
co-authored by Claude Opus 4.8
parent 87806b4501
commit eb8dace0fd
15 changed files with 1134 additions and 96 deletions
+2
View File
@@ -93,6 +93,8 @@ class Company(Base):
overtime_expiry_month: Mapped[int] = mapped_column(Integer, nullable=False, default=3) # März
overtime_expiry_day: Mapped[int] = mapped_column(Integer, nullable=False, default=31) # 31.
overtime_max_carryover_hours: Mapped[int | None] = mapped_column(Integer, nullable=True) # None = alles
# Überstunden-Auszahlung: Selbstbedienung durch Mitarbeiter (Antrag → HR-Genehmigung)
payout_request_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
# Relationships
users: Mapped[list["User"]] = relationship(