import enum import uuid from datetime import datetime from sqlalchemy import ForeignKey, Integer, String from sqlalchemy.dialects.postgresql import ENUM as PgEnum, TIMESTAMP, UUID from sqlalchemy.orm import Mapped, mapped_column from app.db.base import Base class DokumentTyp(str, enum.Enum): """DOC-002: feste Kategorie statt Freitext - macht Dokumente filter-/ wiederfindbar (Nutzer-Vorgabe Modul 10: Prüfprotokolle/Wartungsberichte/ Bedienungsanleitungen/Rechnungen/Zulassungsdokumente).""" pruefprotokoll = "pruefprotokoll" wartungsbericht = "wartungsbericht" bedienungsanleitung = "bedienungsanleitung" rechnung = "rechnung" zulassungsdokument = "zulassungsdokument" sonstiges = "sonstiges" dokument_typ_pg = PgEnum(DokumentTyp, name="dokument_typ", create_type=False) # DOC-005: Rechnungen sind finanziell sensibel - nur Materialverantwortliche/ # Leitungsverantwortliche/Administration dürfen sie sehen/herunterladen, # Mitarbeiter nicht (dürfen sie aber weiterhin hochladen, z.B. direkt beim # Wareneingang scannen). Andere Dokumenttypen (Prüfprotokolle, Anleitungen, # ...) bleiben für alle mit Zugriff auf die Akte sichtbar - die werden im # Feld gebraucht. EINGESCHRAENKTE_DOKUMENTTYPEN = {DokumentTyp.rechnung} class Dokument(Base): """Roadmap Phase 5 (Modul Dokumente): polymorpher Datei-Anhang an beliebige Ressource (Objekt, Objektposition, Geräteinstanz, Mangel, Fahrzeugdetails, Benutzer, ...) - gleiches entitaet_typ/entitaet_id-Muster wie Historie/ Mangel. Datei liegt lokal auf Platte (self-hosted, kein Cloud-Zwang), speicherpfad ist relativ zu settings.upload_dir.""" __tablename__ = "dokument" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) entitaet_typ: Mapped[str] = mapped_column(String, nullable=False) entitaet_id: Mapped[str] = mapped_column(String, nullable=False) dateiname: Mapped[str] = mapped_column(String, nullable=False) speicherpfad: Mapped[str] = mapped_column(String, nullable=False) mime_type: Mapped[str] = mapped_column(String, nullable=False) groesse_bytes: Mapped[int] = mapped_column(Integer, nullable=False) # SHA-256 des Dateiinhalts (DOC-001: Duplikat-Erkennung) - hex-codiert, 64 # Zeichen. Vergleich nur innerhalb derselben entitaet_typ/entitaet_id, nicht # global (gleiche Datei an zwei verschiedenen Objekten ist kein Duplikat). # Nullable wegen Altbestand vor dieser Migration (Hash nachträglich ohne # Dateizugriff nicht sinnvoll befüllbar); jeder neue Upload setzt ihn. dateihash: Mapped[str | None] = mapped_column(String(64)) dokumenttyp: Mapped[DokumentTyp] = mapped_column( dokument_typ_pg, nullable=False, default=DokumentTyp.sonstiges ) beschreibung: Mapped[str | None] = mapped_column(String) hochgeladen_von: Mapped[int] = mapped_column(ForeignKey("benutzer.id"), nullable=False) hochgeladen_am: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), nullable=False)