feat: Sondervertretungs-Faktoren (special_assignments)
- Neues Model SpecialAssignment mit AssignmentMode (fza|payroll|both)
- CRUD-Endpunkte unter /users/{id}/special-assignments
- Payroll-Report: GET /reports/special-assignments/payroll?year=&month=
- Migration 0029: special_assignments Tabelle + btree_gist Overlap-Constraint
- _recalculate_overtime_balance berücksichtigt FZA-Faktoren
- Frontend: Sondervertretungs-Zeiträume im UsersPage Edit-Modal
- Frontend: ReportsPage neuer Tab 'Sondervertretungen' mit Payroll-Tabelle + CSV-Export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ from app.models.public_holiday import PublicHoliday
|
||||
from app.models.smtp_config import SmtpConfig
|
||||
from app.models.caldav_config import CaldavCompanyConfig, CaldavUserConfig
|
||||
from app.models.kiosk_device import KioskDevice, KioskAuthMethod
|
||||
from app.models.special_assignment import SpecialAssignment, AssignmentMode
|
||||
|
||||
__all__ = [
|
||||
"Company",
|
||||
@@ -35,4 +36,6 @@ __all__ = [
|
||||
"PublicHoliday",
|
||||
"KioskDevice",
|
||||
"KioskAuthMethod",
|
||||
"SpecialAssignment",
|
||||
"AssignmentMode",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Sondervertretungs-Faktoren: Per-User Zeitraum-Zuweisung mit Multiplikator."""
|
||||
import enum
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlalchemy import CheckConstraint, Date, Enum, ForeignKey, Numeric, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class AssignmentMode(str, enum.Enum):
|
||||
fza = "fza" # Nur FZA-Stunden-Anrechnung
|
||||
payroll = "payroll" # Nur Gehaltsabrechnung (Bericht)
|
||||
both = "both" # Beides
|
||||
|
||||
|
||||
class SpecialAssignment(Base):
|
||||
"""Sondervertretungs-Zeitraum mit Faktor für einen Mitarbeiter."""
|
||||
__tablename__ = "special_assignments"
|
||||
__table_args__ = (
|
||||
CheckConstraint("factor > 0 AND factor <= 10", name="ck_special_assignment_factor"),
|
||||
CheckConstraint("date_from <= date_to", name="ck_special_assignment_dates"),
|
||||
)
|
||||
|
||||
id: Mapped[UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid4)
|
||||
user_id: Mapped[UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
company_id: Mapped[UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("companies.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
|
||||
date_from: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
date_to: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
|
||||
factor: Mapped[Decimal] = mapped_column(Numeric(5, 3), nullable=False)
|
||||
mode: Mapped[AssignmentMode] = mapped_column(Enum(AssignmentMode, name="assignment_mode"), nullable=False, default=AssignmentMode.both)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
label: Mapped[str | None] = mapped_column(String(100), nullable=True) # z.B. "Schichtleiter Vertretung"
|
||||
Reference in New Issue
Block a user