{% endif %}
```
URL: `path('suche/', core_views.global_search, name='global_search')`
Topbar-Formular (aus WP-0003-T02) zeigt Ergebnisse in `#search-results`.
## Ausschreibungen-URL-Verkabelung und App-Namespace
```task
id: VT-WP-0004-T10
status: done
priority: medium
```
`vergabe_teilnahme/apps/ausschreibungen/urls.py`:
```python
from django.urls import path
from . import views
app_name = 'ausschreibungen'
urlpatterns = [
path('', views.ausschreibung_liste, name='liste'),
path('neu/', views.ausschreibung_neu, name='neu'),
path('/', views.ausschreibung_detail, name='detail'),
path('/bearbeiten/', views.ausschreibung_bearbeiten, name='bearbeiten'),
path('/status/', views.ausschreibung_status, name='status'),
path('/entscheidung/', views.ausschreibung_entscheidung, name='entscheidung'),
path('/archivieren/', views.ausschreibung_archivieren, name='archivieren'),
# Unterseiten-URLs (Platzhalter für spätere Workplans):
path('/lose/', include('vergabe_teilnahme.apps.lose.urls')),
path('/aufgaben/', include('vergabe_teilnahme.apps.aufgaben.urls')),
path('/bieterfragen/', include('vergabe_teilnahme.apps.aufgaben.bieterfragen_urls')),
path('/dokumente/', include('vergabe_teilnahme.apps.dokumente.urls')),
path('/preise/', include('vergabe_teilnahme.apps.preise.urls')),
path('/abgabe/', include('vergabe_teilnahme.apps.nachbetrachtung.abgabe_urls')),
path('/nachbetrachtung/', include('vergabe_teilnahme.apps.nachbetrachtung.urls')),
path('/marktbegleiter/', include('vergabe_teilnahme.apps.marktbegleiter.passagen_urls')),
]
```
Jede referenzierte App-URL-Datei wird hier als leere Stub-Datei angelegt
(`urlpatterns = []`) damit die includes nicht zu ImportErrors führen.
Prüfe: `uv run manage.py check --deploy` → keine URL-Fehler.
Smoke-Test: alle Hauptseiten (/ausschreibungen/, /ausschreibungen/neu/) laden ohne 500.
## Ausschreibungs-Tests (Models und Views)
```task
id: VT-WP-0004-T11
status: done
priority: medium
```
Erstelle `vergabe_teilnahme/apps/ausschreibungen/tests/`:
`test_models.py`:
- Test: Ausschreibung `__str__` gibt Titel zurück
- Test: `ist_aktiv` property für Status 1-9 (True) und 10-13 (False)
- Test: `naechste_frist` gibt das frühere von bieterfragen_bis/abgabe_bis zurück
`test_views.py` (nutze `pytest-django` + `client` fixture):
- Test: GET /ausschreibungen/ → 200
- Test: GET /ausschreibungen/neu/ → 200
- Test: POST /ausschreibungen/neu/ mit validen Daten → Redirect zur Detailseite
- Test: GET /ausschreibungen// → 200
- Test: POST /ausschreibungen//status/ mit status=4 → 200, Ausschreibung hat status=4
- Test: Status-Wechsel mit HTMX-Header → partial template response
Nutze `factory_boy` für Factories:
```python
import factory
class AusschreibungFactory(factory.django.DjangoModelFactory):
class Meta:
model = Ausschreibung
titel = factory.Sequence(lambda n: f"Ausschreibung {n}")
ausschreiber = "Testausschreiber GmbH"
status = 1
```
## Seed-Daten prüfen und Dashboard-Kacheln verifizieren
```task
id: VT-WP-0004-T12
status: done
priority: medium
```
Führe die gesamte Integrations-Smoke-Test-Sequenz durch:
1. `make db` → PostgreSQL läuft
2. `uv run manage.py migrate` → alle Migrationen sauber
3. `uv run manage.py seed_dev` → Seed-Daten angelegt
4. `make dev` → Server läuft
5. Browser öffnen: `http://localhost:8000/`
→ Dashboard zeigt Kacheln (auch wenn leer)
→ Sidebar zeigt alle globalen Navpunkte
→ Topbar mit Suchleiste sichtbar
6. `http://localhost:8000/ausschreibungen/`
→ Liste zeigt die Seed-Ausschreibung
7. Ausschreibung öffnen → Detail-Seite rendert mit Stammdaten
8. Status-Dropdown wechseln → HTMX aktualisiert Status inline
9. `http://localhost:8000/ausschreibungen/neu/` → Formular funktioniert
10. `uv run pytest vergabe_teilnahme/apps/ausschreibungen/` → alle Tests grün
Erst wenn alle 10 Punkte erfüllt sind: Task als done markieren.
## Completion review — 2026-09-27
The finished plan retained twelve stale todo blocks with nested Markdown fences.
Reconciled the existing task IDs into one valid task block per section; no new
tasks were created. T03–T07 and T10–T11 already had application implementations
and model/view acceptance. T01 now includes the missing 60-day evidence-expiry
card (including expired evidence). T02 now searches title and issuing authority
alongside status/manager/archive filters with HTMX. T08 now validates and saves
a historical result atomically in the existing Nachbetrachtung model and sets
the corresponding terminal tender status. T09 now includes issuing authority,
tasks and partners alongside the already implemented search categories.
The old illustrative fields are superseded by the current model: bid_manager
replaces hauptverantwortung, and ergebnis belongs to Nachbetrachtung. Historical
entry uses those models without a schema change. Existing approval, decision,
archive, detail and URL implementations remain in place.
Validation: all 131 application tests pass on disposable SQLite; all 20 tender
model/view tests also pass on disposable PostgreSQL 16. Vite build, Django system
check and migration-drift check pass. Changed Python passes Ruff excluding the
pre-existing E501 long lines. Local tests retain the pre-existing naive-datetime
fixture and uncollected-static-directory warnings. T12 uses freshly migrated/seeded disposable PostgreSQL and local
Chromium for dashboard/navigation, tender list/detail, HTMX search/status and
historical creation. This is local source acceptance, not pilot-user evidence.
The final T12 server-log review exposed a missing CSRF header on inline status
POSTs. T05 now inherits the rendered Django CSRF token through base.html's
HTMX headers. A CSRF-enforcing regression proves missing-token rejection and
valid-token mutation. The Chromium check now changes to a different status,
requires HTTP 200 for the POST, and checks persistence after reload; an unchanged
seeded value is not accepted as proof. This supersedes the initial browser
script's false-positive status assertion.