generated from coulomb/repo-seed
Implementiert Subunternehmer-Katalog mit Suche/Filter, Zuordnung zu Losen via HTMX-Modal, Dienstleistertyp-CRUD und Präferenz-Badges. Bibliothek: Nachweis-Katalog mit Ablaufwarnung und Versionierung, Referenz-Katalog mit Ausschreibungszuordnung, Leistungsblatt-CRUD, Entscheidungsregel-CRUD mit Aktiv-Toggle. Migration für referenzen M2M auf Ausschreibung. 56 Tests grün. Tests-Discovery auf tests.py-Dateien ausgedehnt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
220 lines
7.8 KiB
Markdown
220 lines
7.8 KiB
Markdown
---
|
|
id: WP-0010
|
|
title: Subunternehmer, Partner und Bibliothek
|
|
status: done
|
|
phase: 10-of-12
|
|
created: "2026-05-08"
|
|
depends_on: WP-0009
|
|
---
|
|
|
|
# WP-0010 — Subunternehmer, Partner und Bibliothek
|
|
|
|
Subunternehmer-Katalog, Dienstleistertypen, Nachweis-/Referenz-/Leistungsblatt-/
|
|
Entscheidungsregel-Verwaltung. Referenz: UC-SU-01 bis UC-SU-04, UC-BIB-01 bis UC-BIB-05.
|
|
|
|
---
|
|
|
|
```task
|
|
id: WP-0010-T01
|
|
title: Subunternehmer-Katalog: Liste, Suche, Anlegen (UC-SU-01, UC-SU-03)
|
|
status: done
|
|
|
|
`partner/views.py` — subunternehmer_liste, subunternehmer_neu:
|
|
|
|
Liste: Filter nach Dienstleistertyp, Präferenz (bevorzugt/zugelassen/gesperrt), Freitext-Suche.
|
|
`Subunternehmer.objects.filter(name__icontains=q)` für Freitext.
|
|
Präferenz 'gesperrt': Rot-Badge, wird in Suchergebnissen mit Warnsymbol angezeigt.
|
|
|
|
`SubunternehmerForm(ModelForm)`: alle Felder, Präferenz als Radio-Buttons.
|
|
|
|
subunternehmer_detail: Stammdaten + verknüpfte Ausschreibungen (über SubunternehmerZuordnung):
|
|
```python
|
|
zuordnungen = SubunternehmerZuordnung.objects.filter(
|
|
subunternehmer=obj
|
|
).select_related('ausschreibung', 'los').order_by('-ausschreibung__erstellt_am')
|
|
```
|
|
Zeigt: Ausschreibung, Los, Leistung, Zusage/Nachweis/Preis-Status.
|
|
CustomAttribute-Panel.
|
|
```
|
|
|
|
```task
|
|
id: WP-0010-T02
|
|
title: Subunternehmer einer Ausschreibung/Los zuordnen (UC-SU-02)
|
|
status: done
|
|
|
|
`partner/views.py` — subunternehmer_zuordnen:
|
|
|
|
HTMX-Modal auf Los-Detail-Seite:
|
|
```python
|
|
def subunternehmer_suche_modal(request, ausschreibung_id, los_pk):
|
|
q = request.GET.get('q', '')
|
|
subunternehmer = Subunternehmer.objects.filter(name__icontains=q)
|
|
return render(request, 'partner/partials/subunternehmer_suche.html',
|
|
{'subunternehmer': subunternehmer, 'los_pk': los_pk,
|
|
'ausschreibung_id': ausschreibung_id})
|
|
|
|
def subunternehmer_zuordnen(request, ausschreibung_id, los_pk):
|
|
if request.method == 'POST':
|
|
sub_id = request.POST['subunternehmer_id']
|
|
sub = get_object_or_404(Subunternehmer, pk=sub_id)
|
|
if sub.praeferenz == 'gesperrt':
|
|
# Warnung anzeigen aber nicht blockieren
|
|
pass
|
|
los = get_object_or_404(Los, pk=los_pk)
|
|
zuordnung, created = SubunternehmerZuordnung.objects.get_or_create(
|
|
subunternehmer=sub,
|
|
ausschreibung_id=ausschreibung_id,
|
|
los=los,
|
|
defaults={'konkrete_leistung': request.POST.get('konkrete_leistung', '')}
|
|
)
|
|
return render(request, 'partner/partials/zuordnung_zeile.html',
|
|
{'zuordnung': zuordnung})
|
|
```
|
|
|
|
Auf der Los-Detail-Seite zeigt jede Zuordnung: Name, Dienstleistertyp, Leistung,
|
|
drei Checkboxen (Zusage, Nachweis, Preis) — HTMX-togglebar.
|
|
```
|
|
|
|
```task
|
|
id: WP-0010-T03
|
|
title: Dienstleistertyp-Katalog und Subunternehmer als gesperrt markieren (UC-SU-04)
|
|
status: done
|
|
|
|
`partner/views.py` — dienstleistertypen_liste, dienstleistertyp_neu/_bearbeiten:
|
|
Einfache CRUD-Views für Dienstleistertypen (Katalog-Daten).
|
|
|
|
`subunternehmer_praeferenz (POST)`:
|
|
```python
|
|
def subunternehmer_praeferenz(request, pk):
|
|
sub = get_object_or_404(Subunternehmer, pk=pk)
|
|
if request.method == 'POST':
|
|
sub.praeferenz = request.POST['praeferenz']
|
|
if sub.praeferenz == 'gesperrt':
|
|
sub.bewertung = request.POST.get('begruendung', sub.bewertung)
|
|
sub.save(update_fields=['praeferenz', 'bewertung'])
|
|
return render(request, 'partner/partials/praeferenz_badge.html', {'sub': sub})
|
|
```
|
|
|
|
Bei Präferenz 'gesperrt': Roter Warnhinweis wenn dieser Subunternehmer bei
|
|
Zuordnung gewählt wird (im Suchmodal).
|
|
|
|
`partner/urls.py`:
|
|
```python
|
|
app_name = 'partner'
|
|
urlpatterns = [
|
|
path('subunternehmer/', views.subunternehmer_liste, name='su_liste'),
|
|
path('subunternehmer/neu/', views.subunternehmer_neu, name='su_neu'),
|
|
path('subunternehmer/<int:pk>/', views.subunternehmer_detail, name='su_detail'),
|
|
path('subunternehmer/<int:pk>/bearbeiten/', views.subunternehmer_bearbeiten, name='su_bearbeiten'),
|
|
path('subunternehmer/<int:pk>/praeferenz/', views.subunternehmer_praeferenz, name='su_praeferenz'),
|
|
path('dienstleistertypen/', views.dienstleistertypen_liste, name='dt_liste'),
|
|
path('dienstleistertypen/neu/', views.dienstleistertyp_neu, name='dt_neu'),
|
|
]
|
|
```
|
|
```
|
|
|
|
```task
|
|
id: WP-0010-T04
|
|
title: Bibliothek: Nachweis-Katalog mit Ablaufwarnung (UC-BIB-01, UC-BIB-02)
|
|
status: done
|
|
|
|
`bibliothek/views.py` — nachweise_liste, nachweis_neu/_bearbeiten:
|
|
|
|
Liste mit Ablauffilter:
|
|
```python
|
|
from datetime import date, timedelta
|
|
heute = date.today()
|
|
in_60_tagen = heute + timedelta(days=60)
|
|
```
|
|
Tabs: "Alle", "Bald ablaufend" (`gueltig_bis__lte=in_60_tagen`), "Abgelaufen" (`gueltig_bis__lt=heute`).
|
|
Abgelaufene Nachweise: Roter Badge. Bald ablaufende: Oranger Badge.
|
|
|
|
`NachweisForm(ModelForm)`:
|
|
`datei` als FileInput. `gueltig_ab`/`gueltig_bis` als DateInput type="date".
|
|
Checkboxen für fuer_oeffentliche/fuer_privatwirtschaftliche.
|
|
|
|
nachweis_neue_version: Analog zu Dokument-Versionierung (WP-0007-T03).
|
|
Alten Nachweis auf status='ersetzt', neuen anlegen mit höherer Version.
|
|
|
|
`bibliothek/urls.py` (Auszug):
|
|
```python
|
|
path('nachweise/', views.nachweise_liste, name='nachweise_liste'),
|
|
path('nachweise/neu/', views.nachweis_neu, name='nachweis_neu'),
|
|
path('nachweise/<int:pk>/', views.nachweis_detail, name='nachweis_detail'),
|
|
path('nachweise/<int:pk>/version/', views.nachweis_neue_version, name='nachweis_version'),
|
|
```
|
|
```
|
|
|
|
```task
|
|
id: WP-0010-T05
|
|
title: Bibliothek: Referenz anlegen und zuordnen (UC-BIB-03, UC-BIB-04)
|
|
status: done
|
|
|
|
`bibliothek/views.py` — referenzen_liste, referenz_neu/_bearbeiten:
|
|
|
|
`ReferenzForm(ModelForm)`:
|
|
`whitepaper` als FileInput. `projektzeitraum` als CharField (freies Datum-Format: "2024-2025").
|
|
`leistungsblaetter` als CheckboxSelectMultiple.
|
|
|
|
referenz_detail: Zeigt alle Felder, Whitepaper-Download-Link, verknüpfte Leistungsblätter.
|
|
|
|
`referenz_zuordnen (POST)`:
|
|
Wird von Ausschreibungs-Abgabe-Seite aufgerufen.
|
|
Erstellt eine M2M-Verknüpfung zwischen Referenz und Ausschreibung.
|
|
(Ergänze `referenzen` M2M-Feld auf Ausschreibung-Modell + Migration)
|
|
|
|
HTMX-Suchmodal: Suche nach Branche, Leistungsbeschreibung, Titel.
|
|
Zeigt Freigabestatus und Nutzungseinschränkungen als Warnung.
|
|
```
|
|
|
|
```task
|
|
id: WP-0010-T06
|
|
title: Bibliothek: Leistungsblatt und Entscheidungsregel (UC-BIB-05)
|
|
status: done
|
|
|
|
`bibliothek/views.py` — leistungsblaetter_liste, leistungsblatt_neu/_bearbeiten:
|
|
Einfache CRUD-Views. `LeistungsblattForm(ModelForm)` mit allen Textfeldern.
|
|
|
|
`bibliothek/views.py` — entscheidungsregeln_liste, entscheidungsregel_neu/_bearbeiten:
|
|
|
|
`EntscheidungsregelForm(ModelForm)`:
|
|
`kategorie` als Select mit Choices:
|
|
[(ausschlusskriterium, 'Ausschlusskriterium'), (frist, 'Fristlage'),
|
|
(referenz, 'Referenzanforderung'), (wirtschaftlichkeit, 'Wirtschaftlichkeit'),
|
|
(ressourcen, 'Ressourcenverfügbarkeit'), (sonstiges, 'Sonstiges')]
|
|
|
|
`empfehlung` als Radio-Buttons (teilnehmen/nicht_teilnehmen/pruefen).
|
|
`aktiv`-Toggle in der Liste (HTMX POST).
|
|
|
|
Auf der Entscheidungsseite (Phase 2) werden nur `aktiv=True` Regeln angezeigt.
|
|
```
|
|
|
|
```task
|
|
id: WP-0010-T07
|
|
title: Bibliothek URL-Verkabelung und Tests
|
|
status: done
|
|
|
|
`bibliothek/urls.py` vollständig:
|
|
```python
|
|
app_name = 'bibliothek'
|
|
urlpatterns = [
|
|
path('nachweise/', ...),
|
|
path('referenzen/', ...),
|
|
path('leistungsblaetter/', ...),
|
|
path('entscheidungsregeln/', ...),
|
|
# Detail/Neu/Bearbeiten für jede Entität
|
|
]
|
|
```
|
|
|
|
Global in Haupt-URLs:
|
|
`path('bibliothek/', include('vergabe_teilnahme.apps.bibliothek.urls'))`
|
|
`path('partner/', include('vergabe_teilnahme.apps.partner.urls'))`
|
|
|
|
Tests:
|
|
- Test: Nachweis mit abgelaufenem gueltig_bis → `ist_abgelaufen` property True
|
|
- Test: Nachweis-Liste mit Filter "Abgelaufen" → nur abgelaufene sichtbar
|
|
- Test: Subunternehmer-Zuordnung zu Los → SubunternehmerZuordnung-Objekt in DB
|
|
- Test: Gesperrter Subunternehmer → Warnung im Modal sichtbar (Template enthält 'gesperrt')
|
|
- Test: Entscheidungsregel mit aktiv=False → erscheint nicht in Phase-2-Auswertung
|
|
```
|