generated from coulomb/repo-seed
158 lines
5.8 KiB
Markdown
158 lines
5.8 KiB
Markdown
---
|
|
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 (`<input type="file" multiple>`) 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: <Nachweis-Titel>"
|
|
- `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('<int:pk>/', views.dokument_detail, name='detail'),
|
|
path('<int:pk>/version/', views.dokument_neue_version, name='neue_version'),
|
|
path('<int:pk>/status/', views.dokument_status, name='status'),
|
|
path('<int:pk>/final/', views.dokument_finale_version, name='finale_version'),
|
|
path('<int:pk>/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
|
|
```
|