generated from coulomb/repo-seed
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>
This commit is contained in:
@@ -1,3 +1,102 @@
|
||||
from django.shortcuts import render
|
||||
import json
|
||||
|
||||
# Create your views here.
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user