--- id: WP-0007 title: Dokumentenmanagement status: done phase: 7-of-12 created: "2026-05-08" depends_on: WP-0006 --- # WP-0007 — Dokumentenmanagement Upload, Kategorisierung, Versionierung, Statusworkflow und Standarddokument-Zuordnung für alle Dokumente. Referenz: UC-DO-01 bis UC-DO-05. --- ```task id: WP-0007-T01 title: Dokument-Upload und Kategorisierung (UC-DO-01) status: done `dokumente/views.py` — dokument_upload: `DokumentForm(ModelForm)`: Felder: datei (FileInput), kategorie (Select), version (Text, default='1.0'), quelle (Text, blank), verantwortlicher (Select), pruefer (Select, blank), los (Select, blank). `clean_datei()` prüft Dateiendung (.pdf, .docx, .xlsx, .zip, .png, .jpg, .jpeg) und Dateigröße ≤ settings.MAX_UPLOAD_SIZE. Fehler: ValidationError mit klarer Meldung. Nach erfolgreichem Upload: - `dateiname` wird aus `datei.name` befüllt (`os.path.basename(form.instance.datei.name)`) - Redirect zur Dokumentenliste der Ausschreibung Multi-Upload: Zeige Dropzone (``) mit Alpine.js-Preview der gewählten Dateien (Dateinamen-Liste). Für jede Datei eigenes Formular-Submit (vereinfacht: ein File at a time in v1). ``` ```task id: WP-0007-T02 title: Dokumentenliste und Dokumentdetail status: done `dokumente/views.py` — dokumente_liste: Zeigt alle Dokumente einer Ausschreibung, gruppiert nach Kategorie. Filter: Status, Kategorie, Verantwortlicher. Template `dokumente/liste.html`: - Akkordeon nach Kategorie (Alpine.js) - Tabelle: Dateiname (Download-Link), Version, Status-Badge, Verantwortlicher, Prüfer, Datum - Rote Markierung für `finale_abgabeversion=True` `dokument_detail`: - Alle Felder via render_field - Download-Button für Datei - Versionshistorie (alle Dokumente gleicher Kategorie + Name, geordnet nach Version) - Freigaben-Liste via GenericRelation - CustomAttribute-Panel ``` ```task id: WP-0007-T03 title: Neue Dokumentversion hochladen (UC-DO-02) status: done `dokument_neue_version (POST)`: ```python def dokument_neue_version(request, ausschreibung_id, pk): altes_dokument = get_object_or_404(Dokument, pk=pk, ausschreibung_id=ausschreibung_id) form = DokumentVersionForm(request.POST, request.FILES) if form.is_valid(): neues_dok = form.save(commit=False) neues_dok.ausschreibung = altes_dokument.ausschreibung neues_dok.los = altes_dokument.los neues_dok.kategorie = altes_dokument.kategorie neues_dok.verantwortlicher = altes_dokument.verantwortlicher neues_dok.save() altes_dokument.status = 'ersetzt' altes_dokument.save(update_fields=['status']) return redirect('dokumente:detail', ausschreibung_id=ausschreibung_id, pk=neues_dok.pk) return render(request, 'dokumente/neue_version.html', {'form': form, 'dokument': altes_dokument}) ``` `DokumentVersionForm`: Nur datei + version (vorausgefüllt mit inkrementierter Versionsnummer). Logik `naechste_version(alte_version_str)`: "1.0" → "2.0", "2.3" → "3.0" (Major-Inkrement für neue Versionen). ``` ```task id: WP-0007-T04 title: Dokumentstatus-Workflow und finale Abgabeversion (UC-DO-03, UC-DO-04) status: done **Status-Workflow** — HTMX-Widget analog zum Aufgaben-Status. Statusübergänge: hochgeladen → zu_pruefen → in_bearbeitung → geprueft → freigegeben → final_abgegeben. `dokument_status (POST)`: Bei Übergang auf 'final_abgegeben': Setze automatisch `finale_abgabeversion=True`. Bei Übergang auf 'final_abgegeben': Sperre weitere Status-Änderungen (Widget rendert dann nur readonly Status-Badge ohne Dropdown). **Finale Abgabeversion kennzeichnen** (UC-DO-04): Zusätzlicher Button "Als finale Abgabeversion kennzeichnen" (außerhalb des normalen Workflows): `dokument_finale_version (POST)`: ```python def dokument_finale_version(request, ausschreibung_id, pk): dok = get_object_or_404(Dokument, pk=pk) dok.finale_abgabeversion = True dok.status = 'final_abgegeben' dok.save(update_fields=['finale_abgabeversion', 'status']) return render(request, 'dokumente/partials/finaler_status_badge.html', {'dokument': dok}) ``` Nach Kennzeichnung erscheint grüner "Final" Badge; weitere Uploads zu dieser Version gesperrt. ``` ```task id: WP-0007-T05 title: Standarddokument aus Bibliothek zuordnen (UC-DO-05) status: done `dokument_bibliothek_zuordnen`: HTMX-Modal mit Suchfeld. Suche in `bibliothek.Nachweis` und Bibliothek-Dokumente. Jeder Treffer zeigt: Titel, Kategorie, Version, Ablaufdatum, Freigabestatus. Zuordnung erstellt **keinen** neuen Upload, sondern einen Dokument-Datensatz mit: - `datei` = leer (null) - `quelle` = "Bibliothek: " - `dateiname` = Nachweis-Titel - Referenz auf Nachweis via FK (`bibliothek_nachweis` FK(Nachweis, null=True, SET_NULL) — ergänze Feld im Dokument-Modell + Migration) Ablaufende/abgelaufene Nachweise: Zeige Warnung in orange/rot. ``` ```task id: WP-0007-T06 title: Dokument-URL-Verkabelung und Tests status: done `dokumente/urls.py`: ```python app_name = 'dokumente' urlpatterns = [ path('', views.dokumente_liste, name='liste'), path('hochladen/', views.dokument_upload, name='upload'), path('/', views.dokument_detail, name='detail'), path('/version/', views.dokument_neue_version, name='neue_version'), path('/status/', views.dokument_status, name='status'), path('/final/', views.dokument_finale_version, name='finale_version'), path('/bibliothek/', views.dokument_bibliothek_zuordnen, name='bibliothek_zuordnen'), ] ``` Tests: - Test: Upload mit gültigem PDF → Dokument in DB, Datei im Dateisystem - Test: Upload mit ungültiger Dateierweiterung → ValidationError - Test: Upload zu groß → ValidationError - Test: Neue Version hochladen → altes Dokument hat status='ersetzt' - Test: Finale Abgabeversion → finale_abgabeversion=True, Status gesperrt ```