"""Sondervertretungs-Faktoren: special_assignments Tabelle Revision ID: 0029 Revises: 0028 Create Date: 2026-05-25 Neue Tabelle special_assignments: - user_id + company_id (ForeignKeys mit CASCADE) - date_from / date_to - factor NUMERIC(5,3) – Multiplikator (z.B. 1.5) - mode ENUM(fza|payroll|both) - label / description (optional) - Overlap-Check per Constraint (date_from <= date_to) + App-seitige Prüfung """ from alembic import op revision = "0029" down_revision = "0028" branch_labels = None depends_on = None def upgrade() -> None: op.execute("CREATE EXTENSION IF NOT EXISTS btree_gist") # Enum erzeugen (nur falls nicht vorhanden – CREATE TYPE IF NOT EXISTS nicht überall unterstützt) op.execute(""" DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'assignment_mode') THEN CREATE TYPE assignment_mode AS ENUM ('fza', 'payroll', 'both'); END IF; END $$ """) # Tabelle nur anlegen falls noch nicht vorhanden (idempotent) op.execute(""" CREATE TABLE IF NOT EXISTS special_assignments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, date_from DATE NOT NULL, date_to DATE NOT NULL, factor NUMERIC(5,3) NOT NULL, mode assignment_mode NOT NULL DEFAULT 'both', label VARCHAR(100), description TEXT, CONSTRAINT ck_special_assignment_factor CHECK (factor > 0 AND factor <= 10), CONSTRAINT ck_special_assignment_dates CHECK (date_from <= date_to) ) """) op.execute("CREATE INDEX IF NOT EXISTS ix_special_assignments_user_id ON special_assignments(user_id)") op.execute("CREATE INDEX IF NOT EXISTS ix_special_assignments_company_id ON special_assignments(company_id)") # Exclusion Constraint (nur falls noch nicht vorhanden) op.execute(""" DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'special_assignments_no_overlap' ) THEN ALTER TABLE special_assignments ADD CONSTRAINT special_assignments_no_overlap EXCLUDE USING gist ( user_id WITH =, daterange(date_from, date_to, '[]') WITH && ); END IF; END $$ """) def downgrade() -> None: op.execute("DROP TABLE IF EXISTS special_assignments") op.execute("DROP TYPE IF EXISTS assignment_mode")