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,70 @@
|
||||
"""time entries and work schedules
|
||||
|
||||
Revision ID: 0002_time_entries
|
||||
Revises: 0001_initial
|
||||
Create Date: 2026-03-27
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
revision = "0002_time_entries"
|
||||
down_revision = "0001_initial"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
# Enum-Typen explizit definieren (create_type=False → sa.Enum erstellt sie selbst via op.create_table)
|
||||
entrystatus = sa.Enum("pending", "approved", "rejected", name="entrystatus")
|
||||
entrysource = sa.Enum("web", "kiosk", "api", "manual", name="entrysource")
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ── work_schedules ─────────────────────────────────────────────────────────
|
||||
op.create_table(
|
||||
"work_schedules",
|
||||
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("mon_h", sa.Numeric(4, 2), server_default="8.00"),
|
||||
sa.Column("tue_h", sa.Numeric(4, 2), server_default="8.00"),
|
||||
sa.Column("wed_h", sa.Numeric(4, 2), server_default="8.00"),
|
||||
sa.Column("thu_h", sa.Numeric(4, 2), server_default="8.00"),
|
||||
sa.Column("fri_h", sa.Numeric(4, 2), server_default="8.00"),
|
||||
sa.Column("sat_h", sa.Numeric(4, 2), server_default="0.00"),
|
||||
sa.Column("sun_h", sa.Numeric(4, 2), server_default="0.00"),
|
||||
sa.Column("valid_from", sa.Date, nullable=False),
|
||||
)
|
||||
op.create_index("ix_work_schedules_company_id", "work_schedules", ["company_id"])
|
||||
|
||||
# ── time_entries ───────────────────────────────────────────────────────────
|
||||
op.create_table(
|
||||
"time_entries",
|
||||
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("date", sa.Date, nullable=False),
|
||||
sa.Column("start_time", sa.Time(timezone=False), nullable=False),
|
||||
sa.Column("end_time", sa.Time(timezone=False)),
|
||||
sa.Column("break_minutes", sa.Integer, server_default="0"),
|
||||
sa.Column("break_start", sa.Time(timezone=False)),
|
||||
sa.Column("project_id", postgresql.UUID(as_uuid=True)),
|
||||
sa.Column("note", sa.Text),
|
||||
sa.Column("status", entrystatus, nullable=False, server_default="pending"),
|
||||
sa.Column("source", entrysource, nullable=False, server_default="web"),
|
||||
sa.Column("approved_by", postgresql.UUID(as_uuid=True),
|
||||
sa.ForeignKey("users.id", ondelete="SET NULL")),
|
||||
sa.Column("correction_note", sa.Text),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
op.create_index("ix_time_entries_user_id", "time_entries", ["user_id"])
|
||||
op.create_index("ix_time_entries_date", "time_entries", ["date"])
|
||||
op.create_index("ix_time_entries_user_date", "time_entries", ["user_id", "date"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("time_entries")
|
||||
op.drop_table("work_schedules")
|
||||
entrystatus.drop(op.get_bind(), checkfirst=True)
|
||||
entrysource.drop(op.get_bind(), checkfirst=True)
|
||||
Reference in New Issue
Block a user