Unterstützung für Lose
This commit is contained in:
parent
e4eb5bc368
commit
f6af101933
8 changed files with 105 additions and 1 deletions
|
|
@ -7,7 +7,7 @@ class AusschreibungForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ausschreibung
|
model = Ausschreibung
|
||||||
fields = [
|
fields = [
|
||||||
'titel', 'ausschreiber', 'vergabeplattform', 'vergabenummer', 'vergabeart', 'rechtsgrundlage',
|
'titel', 'ausschreiber', 'vergabeplattform', 'vergabenummer', 'vergabeart', 'rechtsgrundlage', 'rechtsgrundlage_details',
|
||||||
'fundstelle_url', 'bid_manager', 'leistungsbeschreibung',
|
'fundstelle_url', 'bid_manager', 'leistungsbeschreibung',
|
||||||
'branche', 'schlagwoerter', 'geschaetztes_volumen',
|
'branche', 'schlagwoerter', 'geschaetztes_volumen',
|
||||||
'veroeffentlichungsdatum', 'bieterfragen_bis', 'abgabe_bis', 'bindefrist', 'bindefrist_tage',
|
'veroeffentlichungsdatum', 'bieterfragen_bis', 'abgabe_bis', 'bindefrist', 'bindefrist_tage',
|
||||||
|
|
@ -21,6 +21,7 @@ class AusschreibungForm(forms.ModelForm):
|
||||||
'vergabenummer': forms.TextInput(attrs={'class': 'form-input'}),
|
'vergabenummer': forms.TextInput(attrs={'class': 'form-input'}),
|
||||||
'vergabeart': forms.Select(attrs={'class': 'form-input'}),
|
'vergabeart': forms.Select(attrs={'class': 'form-input'}),
|
||||||
'rechtsgrundlage': forms.Select(attrs={'class': 'form-input'}),
|
'rechtsgrundlage': forms.Select(attrs={'class': 'form-input'}),
|
||||||
|
'rechtsgrundlage_details': forms.Textarea(attrs={'class': 'form-input', 'rows': 2, 'placeholder': 'z.B. Verfahrensart, Schwellenwert, Besonderheiten…'}),
|
||||||
'fundstelle_url': forms.URLInput(attrs={'class': 'form-input'}),
|
'fundstelle_url': forms.URLInput(attrs={'class': 'form-input'}),
|
||||||
'bid_manager': forms.Select(attrs={'class': 'form-input'}),
|
'bid_manager': forms.Select(attrs={'class': 'form-input'}),
|
||||||
'leistungsbeschreibung': forms.Textarea(attrs={'class': 'form-input', 'rows': 4}),
|
'leistungsbeschreibung': forms.Textarea(attrs={'class': 'form-input', 'rows': 4}),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 6.0.5 on 2026-05-13 22:57
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ausschreibungen', '0005_bindefrist_tage'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ausschreibung',
|
||||||
|
name='rechtsgrundlage_details',
|
||||||
|
field=models.TextField(blank=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -49,6 +49,7 @@ class Ausschreibung(FlexibleModel):
|
||||||
vergabenummer = models.CharField(max_length=100, blank=True)
|
vergabenummer = models.CharField(max_length=100, blank=True)
|
||||||
vergabeart = models.CharField(max_length=30, choices=VERGABEART_CHOICES, blank=True)
|
vergabeart = models.CharField(max_length=30, choices=VERGABEART_CHOICES, blank=True)
|
||||||
rechtsgrundlage = models.CharField(max_length=20, choices=RECHTSGRUNDLAGE_CHOICES, blank=True)
|
rechtsgrundlage = models.CharField(max_length=20, choices=RECHTSGRUNDLAGE_CHOICES, blank=True)
|
||||||
|
rechtsgrundlage_details = models.TextField(blank=True)
|
||||||
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=1)
|
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=1)
|
||||||
teilnahmeentscheidung = models.CharField(
|
teilnahmeentscheidung = models.CharField(
|
||||||
max_length=20, choices=TEILNAHME_CHOICES, default='offen'
|
max_length=20, choices=TEILNAHME_CHOICES, default='offen'
|
||||||
|
|
|
||||||
|
|
@ -108,13 +108,25 @@ def ausschreibung_neu(request):
|
||||||
|
|
||||||
|
|
||||||
def ausschreibung_detail(request, pk):
|
def ausschreibung_detail(request, pk):
|
||||||
|
from django.db.models import Count, Q
|
||||||
|
|
||||||
from vergabe_teilnahme.apps.aufgaben.models import Aufgabe
|
from vergabe_teilnahme.apps.aufgaben.models import Aufgabe
|
||||||
from vergabe_teilnahme.apps.core.services import aufgaben_score, build_phase_nav, get_deadline_warnings
|
from vergabe_teilnahme.apps.core.services import aufgaben_score, build_phase_nav, get_deadline_warnings
|
||||||
|
from vergabe_teilnahme.apps.lose.models import Los
|
||||||
|
|
||||||
a = get_object_or_404(Ausschreibung, pk=pk)
|
a = get_object_or_404(Ausschreibung, pk=pk)
|
||||||
|
lose = Los.objects.filter(ausschreibung=a).annotate(
|
||||||
|
aufgaben_total=Count('aufgaben', distinct=True),
|
||||||
|
aufgaben_erledigt=Count(
|
||||||
|
'aufgaben',
|
||||||
|
filter=Q(aufgaben__status__in=['erledigt', 'verworfen']),
|
||||||
|
distinct=True,
|
||||||
|
),
|
||||||
|
).order_by('losnummer')
|
||||||
ctx = {
|
ctx = {
|
||||||
'ausschreibung': a,
|
'ausschreibung': a,
|
||||||
'ausschreibung_id': pk,
|
'ausschreibung_id': pk,
|
||||||
|
'lose': lose,
|
||||||
'phases': build_phase_nav(a),
|
'phases': build_phase_nav(a),
|
||||||
'warnungen': get_deadline_warnings(a),
|
'warnungen': get_deadline_warnings(a),
|
||||||
'aufgaben_score': aufgaben_score(Aufgabe.objects.filter(ausschreibung=a)),
|
'aufgaben_score': aufgaben_score(Aufgabe.objects.filter(ausschreibung=a)),
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,12 @@ def los_neu(request, ausschreibung_id):
|
||||||
return redirect('ausschreibungen:lose:liste', ausschreibung_id=ausschreibung_id)
|
return redirect('ausschreibungen:lose:liste', ausschreibung_id=ausschreibung_id)
|
||||||
else:
|
else:
|
||||||
form = LosForm(ausschreibung=ausschreibung)
|
form = LosForm(ausschreibung=ausschreibung)
|
||||||
|
|
||||||
|
if _is_htmx(request):
|
||||||
|
return render(request, 'lose/partials/los_form_inline.html', {
|
||||||
|
'form': form,
|
||||||
|
'ausschreibung': ausschreibung,
|
||||||
|
})
|
||||||
return render(request, 'lose/form.html', {
|
return render(request, 'lose/form.html', {
|
||||||
'form': form,
|
'form': form,
|
||||||
'ausschreibung': ausschreibung,
|
'ausschreibung': ausschreibung,
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,41 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Lose -->
|
||||||
|
<div class="card mt-6">
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<h2 class="text-sm font-semibold text-slate-700">Lose</h2>
|
||||||
|
<button class="btn-ghost text-xs"
|
||||||
|
hx-get="{% url 'ausschreibungen:lose:neu' ausschreibung.pk %}"
|
||||||
|
hx-target="#lose-form-container"
|
||||||
|
hx-swap="innerHTML">
|
||||||
|
+ Los hinzufügen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="lose-form-container"></div>
|
||||||
|
{% if lose %}
|
||||||
|
<table class="w-full text-sm mt-1">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-slate-200 text-left text-xs text-slate-500">
|
||||||
|
<th class="pb-2 pr-4">Nr.</th>
|
||||||
|
<th class="pb-2 pr-4">Titel</th>
|
||||||
|
<th class="pb-2 pr-4">Aufgaben</th>
|
||||||
|
<th class="pb-2 pr-4">Teilnahme</th>
|
||||||
|
<th class="pb-2"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="lose-tbody" class="divide-y divide-slate-100">
|
||||||
|
{% for los in lose %}
|
||||||
|
{% include "lose/partials/los_row.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div id="lose-tbody"></div>
|
||||||
|
<p class="text-sm text-slate-400 mt-1">Noch keine Lose angelegt.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-3 mt-6">
|
<div class="flex gap-3 mt-6">
|
||||||
<a href="{% url 'ausschreibungen:entscheidung' ausschreibung.pk %}" class="btn-ghost text-xs">
|
<a href="{% url 'ausschreibungen:entscheidung' ausschreibung.pk %}" class="btn-ghost text-xs">
|
||||||
Teilnahmeentscheidung
|
Teilnahmeentscheidung
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,10 @@
|
||||||
{{ form.rechtsgrundlage }}
|
{{ form.rechtsgrundlage }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Rechtsgrundlage — Details</label>
|
||||||
|
{{ form.rechtsgrundlage_details }}
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="form-label">Vergabeplattform</label>
|
<label class="form-label">Vergabeplattform</label>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<form hx-post="{% url 'ausschreibungen:lose:neu' ausschreibung.pk %}"
|
||||||
|
hx-target="#lose-tbody"
|
||||||
|
hx-swap="afterbegin"
|
||||||
|
hx-on::after-request="if(event.detail.successful) this.reset(); document.getElementById('lose-form-container').innerHTML = '';"
|
||||||
|
class="bg-slate-50 border border-slate-200 rounded p-3 mt-2">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="flex gap-2 items-end flex-wrap">
|
||||||
|
<div class="flex-none w-20">
|
||||||
|
<label class="form-label">Nr.</label>
|
||||||
|
{{ form.losnummer }}
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 min-w-40">
|
||||||
|
<label class="form-label">Titel *</label>
|
||||||
|
{{ form.lostitel }}
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 shrink-0">
|
||||||
|
<button type="submit" class="btn-primary text-xs">Speichern</button>
|
||||||
|
<button type="button" class="btn-ghost text-xs"
|
||||||
|
onclick="document.getElementById('lose-form-container').innerHTML = ''">Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if form.losnummer.errors or form.lostitel.errors %}
|
||||||
|
<p class="text-red-600 text-xs mt-1">
|
||||||
|
{{ form.losnummer.errors.0 }}{{ form.lostitel.errors.0 }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue