vergabe-teilnahme/vergabe_teilnahme/apps/nachbetrachtung/views.py
tegwick a1cc317b3b feat(nachbetrachtung): Abgabe-Checkliste, Dokumentation und Nachbetrachtung (WP-0009)
Vollständigkeitsprüfung mit Freigaben-Check, Abgabe dokumentieren mit
Nachweis-Upload, Nachbetrachtung mit Kickoff-Aufgabe (gewonnen) und
Alpine.js-gesteuerter Verlustanalyse (verloren). 5 Tests grün.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:09:38 +02:00

102 lines
4.3 KiB
Python

import json
from django import forms
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from vergabe_teilnahme.apps.accounts.models import Mitarbeiter
from vergabe_teilnahme.apps.aufgaben.models import Aufgabe
from vergabe_teilnahme.apps.ausschreibungen.models import Ausschreibung
from .models import Nachbetrachtung
class NachbetrachtungForm(forms.ModelForm):
class Meta:
model = Nachbetrachtung
fields = [
'ergebnis', 'zuschlagsdatum', 'projektverantwortlicher',
'abgegebene_unterlagen', 'abgegebene_preise',
'ausschlaggebende_zuschlagsmerkmale', 'lessons_learned',
'empfehlungen', 'wiederverwendbare_erkenntnisse_markiert',
]
widgets = {
'ergebnis': forms.RadioSelect(attrs={'class': 'mr-2'}),
'zuschlagsdatum': forms.DateInput(attrs={'type': 'date', 'class': 'form-input'}),
'projektverantwortlicher': forms.Select(attrs={'class': 'form-select'}),
'abgegebene_unterlagen': forms.Textarea(attrs={'class': 'form-input', 'rows': 3}),
'abgegebene_preise': forms.Textarea(attrs={'class': 'form-input', 'rows': 3}),
'ausschlaggebende_zuschlagsmerkmale': forms.Textarea(attrs={'class': 'form-input', 'rows': 3}),
'lessons_learned': forms.Textarea(attrs={'class': 'form-input', 'rows': 4}),
'empfehlungen': forms.Textarea(attrs={'class': 'form-input', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['projektverantwortlicher'].queryset = Mitarbeiter.objects.all()
self.fields['projektverantwortlicher'].required = False
self.fields['zuschlagsdatum'].required = False
def nachbetrachtung_detail(request, ausschreibung_id):
ausschreibung = get_object_or_404(Ausschreibung, pk=ausschreibung_id)
nb, _ = Nachbetrachtung.objects.get_or_create(ausschreibung=ausschreibung)
if request.method == 'POST':
form = NachbetrachtungForm(request.POST, instance=nb)
verlustgruende_raw = request.POST.get('verlustgruende', '[]')
try:
verlustgruende = json.loads(verlustgruende_raw)
except (json.JSONDecodeError, ValueError):
verlustgruende = []
if form.is_valid():
nb = form.save(commit=False)
nb.verlustgruende = verlustgruende
nb.save()
ergebnis = form.cleaned_data['ergebnis']
if ergebnis == 'gewonnen':
ausschreibung.status = 10
ausschreibung.save(update_fields=['status', 'geaendert_am'])
aufgabe, created = 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.'
),
},
)
if created:
pv = form.cleaned_data.get('projektverantwortlicher')
pv_name = str(pv) if pv else ''
messages.success(request, f'Kickoff-Aufgabe erstellt für {pv_name}.')
elif ergebnis == 'verloren':
ausschreibung.status = 11
ausschreibung.save(update_fields=['status', 'geaendert_am'])
messages.success(request, 'Nachbetrachtung gespeichert.')
return redirect('ausschreibungen:nachbetrachtung:detail',
ausschreibung_id=ausschreibung_id)
else:
form = NachbetrachtungForm(instance=nb)
kickoff_aufgabe = None
if nb.ergebnis == 'gewonnen':
kickoff_aufgabe = Aufgabe.objects.filter(
ausschreibung=ausschreibung, titel='Kickoff vorbereiten'
).first()
ctx = {
'ausschreibung': ausschreibung,
'nachbetrachtung': nb,
'form': form,
'kickoff_aufgabe': kickoff_aufgabe,
'verlustgruende_json': json.dumps(nb.verlustgruende),
}
return render(request, 'nachbetrachtung/detail.html', ctx)