Initial commit – TimeMaster Zeiterfassung & HR-Tool
Stand: agent-06 (Audit-Log), agent-05 (Krankmeldung), agent-07 Phase 1 (Personalnummer), Busylight-Pull-Integration, TOTP/2FA, Abwesenheiten, Zeiterfassung, Kiosk-Grundgerüst. Migrations 0001–0023 deployed auf 192.168.1.137 + .164. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""absence management
|
||||
|
||||
Revision ID: 0003_absences
|
||||
Revises: 0002_time_entries
|
||||
Create Date: 2026-03-27
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
revision = "0003_absences"
|
||||
down_revision = "0002_time_entries"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
absencestatus = sa.Enum("pending", "approved", "rejected", "cancelled", name="absencestatus")
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ── absence_types ──────────────────────────────────────────────────────────
|
||||
op.create_table(
|
||||
"absence_types",
|
||||
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("company_id", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("companies.id", ondelete="CASCADE"), nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("color", sa.String(7), server_default="#3B82F6"),
|
||||
sa.Column("requires_approval", sa.Boolean, server_default="true"),
|
||||
sa.Column("deducts_vacation", sa.Boolean, server_default="false"),
|
||||
sa.Column("is_paid", sa.Boolean, server_default="true"),
|
||||
sa.Column("max_days_per_year", sa.Integer),
|
||||
sa.Column("is_active", sa.Boolean, server_default="true"),
|
||||
)
|
||||
op.create_index("ix_absence_types_company_id", "absence_types", ["company_id"])
|
||||
|
||||
# ── absences ───────────────────────────────────────────────────────────────
|
||||
op.create_table(
|
||||
"absences",
|
||||
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("user_id", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
|
||||
sa.Column("type_id", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("absence_types.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("start_date", sa.Date, nullable=False),
|
||||
sa.Column("end_date", sa.Date, nullable=False),
|
||||
sa.Column("half_day_start", sa.Boolean, server_default="false"),
|
||||
sa.Column("half_day_end", sa.Boolean, server_default="false"),
|
||||
sa.Column("working_days", sa.Numeric(5, 1), server_default="0"),
|
||||
sa.Column("status", absencestatus, nullable=False, server_default="pending"),
|
||||
sa.Column("approved_by", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("users.id", ondelete="SET NULL")),
|
||||
sa.Column("substitute_id", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("users.id", ondelete="SET NULL")),
|
||||
sa.Column("note", sa.Text),
|
||||
sa.Column("rejection_reason", sa.Text),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
op.create_index("ix_absences_user_id", "absences", ["user_id"])
|
||||
op.create_index("ix_absences_start_date", "absences", ["start_date"])
|
||||
|
||||
# ── vacation_balances ──────────────────────────────────────────────────────
|
||||
op.create_table(
|
||||
"vacation_balances",
|
||||
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("user_id", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
|
||||
sa.Column("year", sa.Integer, nullable=False),
|
||||
sa.Column("entitled_days", sa.Integer, server_default="30"),
|
||||
sa.Column("carried_over", sa.Integer, server_default="0"),
|
||||
sa.Column("used_days", sa.Integer, server_default="0"),
|
||||
sa.UniqueConstraint("user_id", "year", name="uq_vacation_balance_user_year"),
|
||||
)
|
||||
op.create_index("ix_vacation_balances_user_id", "vacation_balances", ["user_id"])
|
||||
|
||||
# ── public_holidays ────────────────────────────────────────────────────────
|
||||
op.create_table(
|
||||
"public_holidays",
|
||||
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("country", sa.String(10), nullable=False),
|
||||
sa.Column("state", sa.String(10)),
|
||||
sa.Column("date", sa.Date, nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("year", sa.Integer, nullable=False),
|
||||
sa.UniqueConstraint("country", "state", "date", name="uq_public_holiday"),
|
||||
)
|
||||
op.create_index("ix_public_holidays_date", "public_holidays", ["date"])
|
||||
op.create_index("ix_public_holidays_year", "public_holidays", ["year"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("public_holidays")
|
||||
op.drop_table("vacation_balances")
|
||||
op.drop_table("absences")
|
||||
op.drop_table("absence_types")
|
||||
absencestatus.drop(op.get_bind(), checkfirst=True)
|
||||
Reference in New Issue
Block a user