Files
timemaster/backend/migrations/versions/0035_absence_comments.py
T
patrickandClaude Opus 4.8 3b2df1c978
Security Audit / Python Dependency Audit (push) Has been cancelled
Security Audit / Node.js Dependency Audit (push) Has been cancelled
feat: agent-11 PR1 – Vertretung, Storno-Re-Genehmigung, Kommentare
Abwesenheits-Modul abgerundet (Feature-Parität mit Urlaubsverwaltung):

- Vertretung: Overlap-Warnung beim Anlegen, E-Mail an Vertretung bei
  Genehmigung, GET /absences/?as_substitute=true, neuer schlanker
  GET /users/colleagues (alle Rollen, RLS-gefenced) für die Auswahl;
  Vertreter-Dropdown + Anzeige in der Liste.
- Stornierung mit Re-Genehmigung: neuer Status CANCELLATION_REQUESTED,
  POST /absences/{id}/request-cancellation; Manager genehmigt/lehnt über
  bestehende approve/reject ab (Urlaub + FZA-Rückbuchung via _apply_cancellation).
- Kommentare: Model AbsenceComment (company_id-RLS), GET/POST comments,
  System-Kommentare bei Statuswechsel, AbsenceCommentsModal.
- Fix: CalDAV fire-and-forget nutzte die Request-Session weiter (in Tests
  geteilt -> "another operation in progress"); jetzt sync_*_bg mit eigener
  Session + RLS-Bypass.

Migration 0035. 178/178 Tests grün. Deployed auf 137 + 164.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:47:47 +02:00

62 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Absence comments + cancellation-request status (agent-11 PR1)
Revision ID: 0035
Revises: 0034
Create Date: 2026-06-23
- Neuer Enum-Wert AbsenceStatus.CANCELLATION_REQUESTED (Mitarbeiter-Stornoantrag
für genehmigte Anträge → Manager genehmigt/lehnt ab)
- Neue Tabelle absence_comments (Kommentar-Thread + System-Kommentare bei Statuswechsel)
mit company_id-Spalte → reguläre company_id-RLS-Policy (analog 0024).
"""
from alembic import op
from sqlalchemy import text
revision = "0035"
down_revision = "0034"
branch_labels = None
depends_on = None
_BYPASS = "COALESCE(current_setting('app.bypass_rls', true), 'off') = 'on'"
_CID = "company_id = NULLIF(current_setting('app.company_id', true), '')::uuid"
_USING = f"({_BYPASS} OR {_CID})"
def _exec(sql: str) -> None:
op.execute(text(sql))
def upgrade() -> None:
# 1) Enum-Wert ergänzen (idempotent)
_exec("ALTER TYPE absencestatus ADD VALUE IF NOT EXISTS 'cancellation_requested'")
# 2) Tabelle anlegen
_exec("""
CREATE TABLE IF NOT EXISTS absence_comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
absence_id UUID NOT NULL REFERENCES absences(id) ON DELETE CASCADE,
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
author_id UUID REFERENCES users(id) ON DELETE SET NULL,
body TEXT NOT NULL,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
)
""")
_exec("CREATE INDEX IF NOT EXISTS ix_absence_comments_absence_id ON absence_comments(absence_id)")
_exec("CREATE INDEX IF NOT EXISTS ix_absence_comments_company_id ON absence_comments(company_id)")
# 3) RLS (company_id-gefenced, analog 0024)
_exec("ALTER TABLE absence_comments ENABLE ROW LEVEL SECURITY")
_exec("ALTER TABLE absence_comments FORCE ROW LEVEL SECURITY")
for cmd in ("select", "insert", "update", "delete"):
_exec(f"DROP POLICY IF EXISTS rls_absence_comments_{cmd} ON absence_comments")
_exec(f"CREATE POLICY rls_absence_comments_select ON absence_comments FOR SELECT USING {_USING}")
_exec(f"CREATE POLICY rls_absence_comments_insert ON absence_comments FOR INSERT WITH CHECK {_USING}")
_exec(f"CREATE POLICY rls_absence_comments_update ON absence_comments FOR UPDATE USING {_USING} WITH CHECK {_USING}")
_exec(f"CREATE POLICY rls_absence_comments_delete ON absence_comments FOR DELETE USING {_USING}")
def downgrade() -> None:
# Enum-Wert kann in PostgreSQL nicht entfernt werden Tabelle wird gedroppt.
_exec("DROP TABLE IF EXISTS absence_comments")