fix(WP-0013): Feedback-Bugs — alle 8 Einträge aus Backlog behoben
- Fristen-Widget-Format: DateInput/DateTimeInput mit ISO-Format-Attribut, damit Browser date/datetime-local korrekt vorausfüllen (Feedback #4) - Phase 2 Teilnahmeentscheidung: URL in build_phase_nav von /teilnahmeentscheidung/ → /entscheidung/ korrigiert (Feedback #6) - Phase 3 Detaillierte Durchsicht: URL in build_phase_nav von /anforderungen/ → /lose/anforderungen/ korrigiert (Feedback #7) - Phase 7 Abgabe: order_by('bezeichnung') → order_by('beschreibung') in abgabe_views.py (Dokument hat kein Feld 'bezeichnung') (Feedback #8) - Ausschreibungen-Liste: Ausschreiber zuerst, Titel zweite Spalte, neues geschätztes Volumen (Feedback #5) - Feedback-Backlog Leerstand: bereits durch vorherigen URL-Fix abgedeckt (Feedback #1) - Rechtsgrundlage (VgV/UVgO/VOB/A/SektVO/GWB) als neues Formularfeld incl. Migration (Feedback #2) - Bindefrist in Tagen + berechnetes Enddatum als Modell-Property bindefrist_berechnet, Formular und Detailansicht erweitert (Feedback #3) 68/68 Tests grün. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d6873d7b88
commit
7903f59f85
10 changed files with 240 additions and 14 deletions
|
|
@ -7,10 +7,10 @@ class AusschreibungForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Ausschreibung
|
||||
fields = [
|
||||
'titel', 'ausschreiber', 'vergabeplattform', 'vergabenummer', 'vergabeart',
|
||||
'titel', 'ausschreiber', 'vergabeplattform', 'vergabenummer', 'vergabeart', 'rechtsgrundlage',
|
||||
'fundstelle_url', 'bid_manager', 'leistungsbeschreibung',
|
||||
'branche', 'schlagwoerter', 'geschaetztes_volumen',
|
||||
'veroeffentlichungsdatum', 'bieterfragen_bis', 'abgabe_bis', 'bindefrist',
|
||||
'veroeffentlichungsdatum', 'bieterfragen_bis', 'abgabe_bis', 'bindefrist', 'bindefrist_tage',
|
||||
'unterlagen_erhalten', 'unterlagen_erhalten_am',
|
||||
'teilnahmeentscheidung', 'entscheidungsbegruendung',
|
||||
]
|
||||
|
|
@ -20,17 +20,19 @@ class AusschreibungForm(forms.ModelForm):
|
|||
'vergabeplattform': forms.TextInput(attrs={'class': 'form-input'}),
|
||||
'vergabenummer': forms.TextInput(attrs={'class': 'form-input'}),
|
||||
'vergabeart': forms.Select(attrs={'class': 'form-input'}),
|
||||
'rechtsgrundlage': forms.Select(attrs={'class': 'form-input'}),
|
||||
'fundstelle_url': forms.URLInput(attrs={'class': 'form-input'}),
|
||||
'bid_manager': forms.Select(attrs={'class': 'form-input'}),
|
||||
'leistungsbeschreibung': forms.Textarea(attrs={'class': 'form-input', 'rows': 4}),
|
||||
'branche': forms.TextInput(attrs={'class': 'form-input'}),
|
||||
'schlagwoerter': forms.TextInput(attrs={'class': 'form-input'}),
|
||||
'geschaetztes_volumen': forms.NumberInput(attrs={'class': 'form-input'}),
|
||||
'veroeffentlichungsdatum': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}),
|
||||
'bieterfragen_bis': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}),
|
||||
'abgabe_bis': forms.DateTimeInput(attrs={'class': 'form-input', 'type': 'datetime-local'}),
|
||||
'bindefrist': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}),
|
||||
'unterlagen_erhalten_am': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}),
|
||||
'veroeffentlichungsdatum': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}, format='%Y-%m-%d'),
|
||||
'bieterfragen_bis': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}, format='%Y-%m-%d'),
|
||||
'abgabe_bis': forms.DateTimeInput(attrs={'class': 'form-input', 'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'),
|
||||
'bindefrist': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}, format='%Y-%m-%d'),
|
||||
'bindefrist_tage': forms.NumberInput(attrs={'class': 'form-input', 'min': '1', 'placeholder': 'Tage'}),
|
||||
'unterlagen_erhalten_am': forms.DateInput(attrs={'class': 'form-input', 'type': 'date'}, format='%Y-%m-%d'),
|
||||
'teilnahmeentscheidung': forms.Select(attrs={'class': 'form-input'}),
|
||||
'entscheidungsbegruendung': forms.Textarea(attrs={'class': 'form-input', 'rows': 3}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 6.0.5 on 2026-05-13 22:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ausschreibungen', '0003_referenzen_m2m'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ausschreibung',
|
||||
name='rechtsgrundlage',
|
||||
field=models.CharField(blank=True, choices=[('vgv', 'VgV'), ('uvgo', 'UVgO'), ('vob_a', 'VOB/A'), ('sektvo', 'SektVO'), ('gwb', 'GWB'), ('sonstige', 'Sonstige')], max_length=20),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 6.0.5 on 2026-05-13 22:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ausschreibungen', '0004_rechtsgrundlage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ausschreibung',
|
||||
name='bindefrist_tage',
|
||||
field=models.PositiveSmallIntegerField(blank=True, help_text='Bindefrist in Tagen ab Abgabe', null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import date
|
||||
from datetime import date, timedelta
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
|
@ -34,12 +34,21 @@ class Ausschreibung(FlexibleModel):
|
|||
('rahmenvertrag', 'Rahmenvertrag'),
|
||||
('sonstige', 'Sonstige'),
|
||||
]
|
||||
RECHTSGRUNDLAGE_CHOICES = [
|
||||
('vgv', 'VgV'),
|
||||
('uvgo', 'UVgO'),
|
||||
('vob_a', 'VOB/A'),
|
||||
('sektvo', 'SektVO'),
|
||||
('gwb', 'GWB'),
|
||||
('sonstige', 'Sonstige'),
|
||||
]
|
||||
|
||||
titel = models.CharField(max_length=400)
|
||||
ausschreiber = models.CharField(max_length=300)
|
||||
vergabeplattform = models.CharField(max_length=200, blank=True)
|
||||
vergabenummer = models.CharField(max_length=100, blank=True)
|
||||
vergabeart = models.CharField(max_length=30, choices=VERGABEART_CHOICES, blank=True)
|
||||
rechtsgrundlage = models.CharField(max_length=20, choices=RECHTSGRUNDLAGE_CHOICES, blank=True)
|
||||
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=1)
|
||||
teilnahmeentscheidung = models.CharField(
|
||||
max_length=20, choices=TEILNAHME_CHOICES, default='offen'
|
||||
|
|
@ -51,6 +60,7 @@ class Ausschreibung(FlexibleModel):
|
|||
bieterfragen_bis = models.DateField(null=True, blank=True)
|
||||
abgabe_bis = models.DateTimeField(null=True, blank=True)
|
||||
bindefrist = models.DateField(null=True, blank=True)
|
||||
bindefrist_tage = models.PositiveSmallIntegerField(null=True, blank=True, help_text='Bindefrist in Tagen ab Abgabe')
|
||||
|
||||
# Verantwortlichkeiten
|
||||
bid_manager = models.ForeignKey(
|
||||
|
|
@ -92,6 +102,15 @@ class Ausschreibung(FlexibleModel):
|
|||
def __str__(self):
|
||||
return self.titel
|
||||
|
||||
@property
|
||||
def bindefrist_berechnet(self):
|
||||
"""Enddatum aus abgabe_bis + bindefrist_tage, falls kein festes bindefrist-Datum."""
|
||||
if self.bindefrist:
|
||||
return self.bindefrist
|
||||
if self.abgabe_bis and self.bindefrist_tage:
|
||||
return (self.abgabe_bis + timedelta(days=self.bindefrist_tage)).date()
|
||||
return None
|
||||
|
||||
@property
|
||||
def ist_aktiv(self):
|
||||
return 1 <= self.status <= 9
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ def build_phase_nav(ausschreibung, current_url=''):
|
|||
base = f'/ausschreibungen/{ausschreibung.pk}'
|
||||
phase_urls = {
|
||||
1: f'{base}/',
|
||||
2: f'{base}/teilnahmeentscheidung/',
|
||||
3: f'{base}/anforderungen/',
|
||||
2: f'{base}/entscheidung/',
|
||||
3: f'{base}/lose/anforderungen/',
|
||||
4: f'{base}/bieterfragen/',
|
||||
5: f'{base}/preise/',
|
||||
6: f'{base}/dokumente/',
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ def abgabe_vollstaendigkeit(ausschreibung):
|
|||
def abgabe_checkliste(request, ausschreibung_id):
|
||||
ausschreibung = get_object_or_404(Ausschreibung, pk=ausschreibung_id)
|
||||
vollstaendigkeit = abgabe_vollstaendigkeit(ausschreibung)
|
||||
dokumente = Dokument.objects.filter(ausschreibung=ausschreibung).order_by('kategorie', 'bezeichnung')
|
||||
dokumente = Dokument.objects.filter(ausschreibung=ausschreibung).order_by('kategorie', 'beschreibung')
|
||||
|
||||
punkte = [
|
||||
('entscheidung_getroffen', 'Teilnahmeentscheidung getroffen'),
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
<dl class="space-y-1">
|
||||
{% render_field ausschreibung "ausschreiber" "Ausschreiber" %}
|
||||
{% render_field ausschreibung "vergabeart" "Vergabeart" %}
|
||||
{% render_field ausschreibung "rechtsgrundlage" "Rechtsgrundlage" %}
|
||||
{% render_field ausschreibung "vergabenummer" "Vergabenummer" %}
|
||||
{% render_field ausschreibung "vergabeplattform" "Plattform" %}
|
||||
{% render_field ausschreibung "branche" "Branche" %}
|
||||
|
|
@ -73,7 +74,19 @@
|
|||
{% render_field ausschreibung "veroeffentlichungsdatum" "Veröffentlicht" %}
|
||||
{% render_field ausschreibung "bieterfragen_bis" "Bieterfragen bis" %}
|
||||
{% render_field ausschreibung "abgabe_bis" "Abgabe bis" %}
|
||||
{% if ausschreibung.bindefrist_tage %}
|
||||
<div class="flex justify-between text-sm py-0.5">
|
||||
<dt class="text-slate-500">Bindefrist</dt>
|
||||
<dd class="text-slate-800">
|
||||
{{ ausschreibung.bindefrist_tage }} Tage
|
||||
{% if ausschreibung.bindefrist_berechnet %}
|
||||
<span class="text-slate-400">(bis {{ ausschreibung.bindefrist_berechnet|date:"d.m.Y" }})</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
{% else %}
|
||||
{% render_field ausschreibung "bindefrist" "Bindefrist" %}
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@
|
|||
<label class="form-label">Vergabeart</label>
|
||||
{{ form.vergabeart }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Rechtsgrundlage</label>
|
||||
{{ form.rechtsgrundlage }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
|
|
@ -83,9 +87,14 @@
|
|||
{{ form.abgabe_bis }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Bindefrist</label>
|
||||
<label class="form-label">Bindefrist (Datum)</label>
|
||||
{{ form.bindefrist }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Bindefrist (Tage ab Abgabe)</label>
|
||||
{{ form.bindefrist_tage }}
|
||||
<p class="text-xs text-slate-400 mt-0.5">Alternativ zum Datum — Enddatum wird automatisch berechnet</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
<table class="w-full text-sm">
|
||||
<thead class="bg-slate-50 border-b border-slate-200">
|
||||
<tr>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Titel</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Ausschreiber</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Titel</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Volumen (gesch.)</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Status</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Abgabe</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-slate-600">Bid Manager</th>
|
||||
|
|
@ -14,12 +15,15 @@
|
|||
<tbody class="divide-y divide-slate-100">
|
||||
{% for a in ausschreibungen %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-4 py-2 text-slate-600 font-medium">{{ a.ausschreiber|default:"—" }}</td>
|
||||
<td class="px-4 py-2">
|
||||
<a href="{% url 'ausschreibungen:detail' a.pk %}" class="text-brand-700 hover:underline font-medium">
|
||||
{{ a.titel }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="px-4 py-2 text-slate-600">{{ a.ausschreiber }}</td>
|
||||
<td class="px-4 py-2 text-slate-600 text-right whitespace-nowrap">
|
||||
{% if a.geschaetztes_volumen %}{{ a.geschaetztes_volumen|floatformat:0 }} €{% else %}—{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-2">
|
||||
{% status_badge a.status a.get_status_display %}
|
||||
</td>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue