vergabe-teilnahme/workplans/VT-WP-0009-abgabe-nachbetrachtung.md
codex 8838725e70
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
fix(workplans): bring vergabe-teilnahme onto the work-record convention
This repository predates the convention wholesale: no file declared a type at
all, identifiers were bare WP-NNNN with no repository prefix, and no file
carried a hub identifier. Seventeen real workplans were therefore invisible to
every projection while the hub separately held sixteen records for them — so a
forge-derived reset read all sixteen as no longer deriving.

Adds type: workplan, moves identifiers to VT-WP-NNNN, qualifies 135 task
identifiers with their workplan, and derives projection UUIDs. Frontmatter
status done maps to finished; task-block statuses are a different vocabulary and
are untouched.

Known remaining divergence: task fences contain prose after the YAML, so only 15
of 135 tasks parse. Recorded rather than rewritten — reflowing someone's
workplan content is not a mechanical change.

Refs STATE-WP-0083-T05

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 20:22:29 +02:00

7.2 KiB

id type title status phase created depends_on domain repo state_hub_workstream_id
VT-WP-0009 workplan Abgabe und Nachbetrachtung finished 9-of-12 2026-05-08 WP-0008 communication vergabe-teilnahme c59a0bb1-b332-5777-b916-38b7a3ac83ff

WP-0009 — Abgabe (Phase 6/7) und Nachbetrachtung (Phase 8)

Abgabe-Checkliste, Vollständigkeitsprüfung, Abgabe-Dokumentation mit Nachweis, Ergebnis erfassen, Kickoff-Aufgabe erstellen, Verlustanalyse, Lessons Learned. Referenz: UC-AB-01 bis UC-AB-03, UC-NB-01 bis UC-NB-03.


id: VT-WP-0009-T01
title: Abgabe-Checkliste mit Vollständigkeitsstatus (UC-AB-01)
status: done

`nachbetrachtung/abgabe_views.py` — abgabe_checkliste:

Vollständigkeitsprüfung-Service `abgabe_vollstaendigkeit(ausschreibung)`:
```python
def abgabe_vollstaendigkeit(ausschreibung):
    from vergabe_teilnahme.apps.dokumente.models import Dokument
    from vergabe_teilnahme.apps.core.models import Freigabe
    from django.contrib.contenttypes.models import ContentType

    ct = ContentType.objects.get_for_model(ausschreibung)
    freigaben = Freigabe.objects.filter(content_type=ct, object_id=ausschreibung.pk)

    def hat_freigabe(typ):
        return freigaben.filter(freigabe_typ=typ, status='erteilt').exists()

    return {
        'dokumente_gesamt': Dokument.objects.filter(ausschreibung=ausschreibung).count(),
        'dokumente_freigegeben': Dokument.objects.filter(
            ausschreibung=ausschreibung, status__in=['freigegeben', 'final_abgegeben']).count(),
        'teilnahme_freigabe': hat_freigabe('teilnahme'),
        'preis_freigabe': hat_freigabe('preis'),
        'recht_freigabe': hat_freigabe('recht'),
        'abgabe_freigabe': hat_freigabe('abgabe'),
        'entscheidung_getroffen': ausschreibung.teilnahmeentscheidung == 'teilnahme',
    }

Template nachbetrachtung/abgabe.html:

  • Fortschrittsbalken oben (Anzahl erfüllter Checkpunkte / Gesamt)
  • Checkliste mit Grün-/Rot-Badges für jeden Punkt
  • Dokumente-Sektion: Liste aller Dokumente mit Status
  • Freigaben-Sektion: Welche Freigaben vorliegen / fehlen
  • Frist-Banner: "Abgabe bis: " prominent oben

```task
id: VT-WP-0009-T02
title: Abgabe dokumentieren mit Nachweis-Upload (UC-AB-02)
status: done

`nachbetrachtung/abgabe_views.py` — abgabe_dokumentieren:

`AbgabeForm(Form)`:
- `abgabe_zeitpunkt` DateTimeInput(type='datetime-local')
- `abgabe_plattform` CharField
- `verantwortlicher` ModelChoiceField(Mitarbeiter)
- `abgabenachweis` FileField (Eingangsbestätigung, Screenshot etc.)
- `kommentar` Textarea

```python
def abgabe_dokumentieren(request, ausschreibung_id):
    ausschreibung = get_object_or_404(Ausschreibung, pk=ausschreibung_id)
    if request.method == 'POST':
        form = AbgabeForm(request.POST, request.FILES)
        if form.is_valid():
            # Abgabenachweis als Dokument speichern
            if form.cleaned_data.get('abgabenachweis'):
                Dokument.objects.create(
                    ausschreibung=ausschreibung,
                    datei=form.cleaned_data['abgabenachweis'],
                    kategorie='abgabenachweis',
                    status='final_abgegeben',
                    finale_abgabeversion=True,
                )
            # Ausschreibungsstatus auf Abgegeben setzen
            ausschreibung.status = 9
            ausschreibung.save(update_fields=['status', 'geaendert_am'])
            # Alle Dokumente mit finale_abgabeversion=True: gesperrt (kein weiterer Upload)
            return redirect('ausschreibungen:detail', pk=ausschreibung_id)
    else:
        form = AbgabeForm()
    return render(request, 'nachbetrachtung/abgabe_formular.html',
                  {'form': form, 'ausschreibung': ausschreibung})

```task
id: VT-WP-0009-T03
title: Nachbetrachtung-View — Ergebnis und Kickoff (UC-NB-01)
status: done

`nachbetrachtung/views.py` — nachbetrachtung_detail:

Erstellt oder lädt die OneToOne `Nachbetrachtung` für die Ausschreibung.

`NachbetrachtungForm(ModelForm)`:
Felder: ergebnis (Radio), zuschlagsdatum, projektverantwortlicher (Select Mitarbeiter).

Bei Ergebnis 'gewonnen' (POST):
1. Setze Ausschreibungsstatus auf 10
2. Erstelle automatisch Aufgabe:
```python
Aufgabe.objects.get_or_create(
    ausschreibung=ausschreibung,
    titel="Kickoff vorbereiten",
    defaults={
        'typ': 'fachlich',
        'prioritaet': 1,
        'verantwortlicher': form.cleaned_data.get('projektverantwortlicher'),
        'beschreibung': f"Kickoff für {ausschreibung.titel}. Angebotsumfang und Annahmen übergeben.",
    }
)
  1. Flash-Meldung: "Kickoff-Aufgabe erstellt für "

Template nachbetrachtung/detail.html:

  • Ergebnis-Formular oben
  • Bei Ergebnis 'gewonnen': Übergabe-Abschnitt (Projektverantwortlicher, Kickoff-Aufgabe-Link)
  • Bei Ergebnis 'verloren': Verlustanalyse-Abschnitt (aus UC-NB-02)

```task
id: VT-WP-0009-T04
title: Verlustanalyse und Lessons Learned (UC-NB-02, UC-NB-03)
status: done

**Verlustgründe** — dynamisches JSONField-Formular:
Alpine.js-gesteuertes Array:
```html
<div x-data="{ gruende: {{ nachbetrachtung.verlustgruende|json_script:'gruende'|safe }} }">
  <template x-for="(g, i) in gruende" :key="i">
    <div class="flex gap-2 mb-2">
      <input x-model="g.grund" class="form-input flex-1" placeholder="Verlustgrund">
      <select x-model="g.kategorie" class="form-input w-32">
        <option value="preis">Preis</option>
        <option value="referenz">Referenz</option>
        <option value="anforderung">Anforderung</option>
        <option value="subunternehmer">Subunternehmer</option>
        <option value="sonstiges">Sonstiges</option>
      </select>
      <input type="number" x-model.number="g.verlaesslichkeit" min="1" max="5"
             class="form-input w-20" placeholder="1-5">
      <button @click="gruende.splice(i, 1)" class="btn-ghost text-red-500">✕</button>
    </div>
  </template>
  <button @click="gruende.push({grund:'', kategorie:'sonstiges', verlaesslichkeit:3})"
          class="btn-secondary">+ Verlustgrund</button>
  <input type="hidden" name="verlustgruende" :value="JSON.stringify(gruende)">
</div>

Ausschlaggebende Merkmale, Lessons Learned, Empfehlungen: einfache Textareas. Checkbox "Wiederverwendbare Erkenntnisse markieren".

Bei Speichern: Aktualisiere Nachbetrachtung-Objekt, Redirect zur Nachbetrachtungsseite.


```task
id: VT-WP-0009-T05
title: URL-Verkabelung Abgabe/Nachbetrachtung und Tests
status: done

`nachbetrachtung/abgabe_urls.py`:
```python
urlpatterns = [
    path('', abgabe_views.abgabe_checkliste, name='checkliste'),
    path('dokumentieren/', abgabe_views.abgabe_dokumentieren, name='dokumentieren'),
    path('problem/', abgabe_views.abgabe_problem, name='problem'),
]

nachbetrachtung/urls.py:

app_name = 'nachbetrachtung'
urlpatterns = [
    path('', views.nachbetrachtung_detail, name='detail'),
]

abgabe_problem (POST): Setzt ausschreibung.status auf internen Marker "Problem bei Abgabe" (eigenes Status-Choice ergänzen wenn nötig, oder Kommentarfeld).

Tests:

  • Test: abgabe_vollstaendigkeit ohne Freigaben → alle False
  • Test: Freigabe erteilen → entsprechendes Feld True
  • Test: Ergebnis 'gewonnen' → Kickoff-Aufgabe wird erstellt, Status 10
  • Test: Ergebnis 'verloren' → Status 11
  • Test: Verlustgründe JSONField gespeichert mit korrekter Struktur