generated from coulomb/repo-seed
Implements all 8 tasks of the final cross-cutting workplan: - T01: Generisches Freigabe-Modal (freigabe_modal, freigabe_erteilen views + templates) - T02: Freigaben-Übersicht pro Ausschreibung (freigaben_uebersicht view + template) - T03: EntityFieldConfig Admin-Interface (/felder/<entity_type>/ with HTMX toggle) - T04: CustomAttribute-Panel (full CRUD with sort, lazy HTMX load) - T05: Feedback-Backlog mit Statusverwaltung + feedback_success.html template - T06: End-to-End-Tests in vergabe_teilnahme/tests/test_e2e.py (8 tests) - T07: Globale Suche erweitert (Dokumente, Nachweise, Referenzen, Marktbegleiter) - T08: Alle Migrationen sauber, 68/68 Tests grün, Ruff-Fehler in neuem Code behoben Bugfix: URL-Namespace-Fehler in Abgabe-Templates (ausschreibungen:nachbetrachtung:abgabe → ausschreibungen:abgabe) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
122 lines
4.8 KiB
Python
122 lines
4.8 KiB
Python
import pytest
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
|
from vergabe_teilnahme.apps.accounts.models import Mitarbeiter
|
|
from vergabe_teilnahme.apps.ausschreibungen.models import Ausschreibung
|
|
from vergabe_teilnahme.apps.core.models import CustomAttribute, EntityFieldConfig, Freigabe
|
|
from vergabe_teilnahme.apps.lose.models import Los
|
|
from vergabe_teilnahme.apps.partner.models import Subunternehmer
|
|
|
|
|
|
@pytest.fixture
|
|
def mitarbeiter(db):
|
|
return Mitarbeiter.objects.create_user(username='testuser', password='x')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestVollstaendigerBieterprozess:
|
|
def test_ausschreibung_anlegen_bis_freigabe(self, client, mitarbeiter):
|
|
client.force_login(mitarbeiter)
|
|
|
|
# 1. Ausschreibung anlegen
|
|
r = client.post('/ausschreibungen/neu/', {
|
|
'titel': 'E2E Test Ausschreibung',
|
|
'ausschreiber': 'Testamt',
|
|
})
|
|
assert r.status_code == 302
|
|
a = Ausschreibung.objects.get(titel='E2E Test Ausschreibung')
|
|
|
|
# 2. Los anlegen
|
|
r = client.post(f'/ausschreibungen/{a.pk}/lose/neu/', {
|
|
'losnummer': '1',
|
|
'lostitel': 'Hauptlos',
|
|
})
|
|
assert r.status_code == 302
|
|
assert Los.objects.filter(ausschreibung=a).count() == 1
|
|
|
|
# 3. Freigabe erteilen via API
|
|
ct = ContentType.objects.get_for_model(a)
|
|
r = client.post('/freigaben/erteilen/', {
|
|
'content_type_id': ct.pk,
|
|
'object_id': a.pk,
|
|
'freigabe_typ': 'teilnahme',
|
|
})
|
|
assert r.status_code == 200
|
|
assert Freigabe.objects.filter(
|
|
content_type=ct, object_id=a.pk, freigabe_typ='teilnahme'
|
|
).exists()
|
|
|
|
# 4. Freigaben-Übersicht abrufbar
|
|
r = client.get(f'/ausschreibungen/{a.pk}/freigaben/')
|
|
assert r.status_code == 200
|
|
assert 'teilnahme' in r.content.decode().lower()
|
|
|
|
def test_freigabe_modal_liefert_fragment(self, client, mitarbeiter):
|
|
client.force_login(mitarbeiter)
|
|
a = Ausschreibung.objects.create(titel='Modal Test', ausschreiber='X', status=1)
|
|
ct = ContentType.objects.get_for_model(a)
|
|
r = client.get(f'/freigaben/modal/?ct={ct.pk}&oid={a.pk}&typ=preis')
|
|
assert r.status_code == 200
|
|
content = r.content.decode()
|
|
assert 'freigabe_typ' in content.lower() or 'freigabe' in content.lower()
|
|
|
|
def test_abgabe_dokumentieren(self, client, mitarbeiter):
|
|
client.force_login(mitarbeiter)
|
|
a = Ausschreibung.objects.create(titel='Abgabe Test', ausschreiber='X', status=6)
|
|
r = client.post(f'/ausschreibungen/{a.pk}/abgabe/dokumentieren/', {
|
|
'abgabe_zeitpunkt': '2026-05-11T12:00',
|
|
'abgabe_plattform': 'Vergabeplattform',
|
|
})
|
|
assert r.status_code == 302
|
|
a.refresh_from_db()
|
|
assert a.status == 9
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestFlexibleFelder:
|
|
def test_feld_ausblenden_wirkt_im_template(self, client, mitarbeiter):
|
|
sub = Subunternehmer.objects.create(name='Test GmbH')
|
|
EntityFieldConfig.objects.create(
|
|
entity_type='subunternehmer', field_name='webseite', is_hidden=True
|
|
)
|
|
client.force_login(mitarbeiter)
|
|
hidden_fields = sub.get_hidden_fields()
|
|
assert 'webseite' in hidden_fields
|
|
|
|
def test_custom_attribute_hinzufuegen(self, client, mitarbeiter):
|
|
sub = Subunternehmer.objects.create(name='Test GmbH')
|
|
ct = ContentType.objects.get_for_model(sub)
|
|
client.force_login(mitarbeiter)
|
|
r = client.post(f'/core/attrs/{ct.pk}/{sub.pk}/neu/', {
|
|
'label': 'Vertragsnummer',
|
|
'value': 'VN-2026-001',
|
|
'data_type': 'text',
|
|
})
|
|
assert r.status_code == 200
|
|
assert CustomAttribute.objects.filter(
|
|
content_type=ct, object_id=sub.pk, label='Vertragsnummer'
|
|
).exists()
|
|
|
|
def test_custom_attribute_panel_zeigt_attribute(self, client, mitarbeiter):
|
|
sub = Subunternehmer.objects.create(name='Panel Test GmbH')
|
|
ct = ContentType.objects.get_for_model(sub)
|
|
CustomAttribute.objects.create(
|
|
content_type=ct, object_id=sub.pk,
|
|
key='kundennr', label='Kundennummer', value='KD-999', data_type='text'
|
|
)
|
|
client.force_login(mitarbeiter)
|
|
r = client.get(f'/core/attrs/{ct.pk}/{sub.pk}/')
|
|
assert r.status_code == 200
|
|
assert b'Kundennummer' in r.content
|
|
|
|
def test_feld_konfiguration_seite_erreichbar(self, client, mitarbeiter):
|
|
client.force_login(mitarbeiter)
|
|
r = client.get('/felder/ausschreibung/')
|
|
assert r.status_code == 200
|
|
assert b'titel' in r.content
|
|
|
|
def test_feedback_backlog_erreichbar(self, client, mitarbeiter):
|
|
client.force_login(mitarbeiter)
|
|
r = client.get('/feedback/backlog/')
|
|
assert r.status_code == 200
|