vergabe-teilnahme/vergabe_teilnahme/apps/core/views.py

226 lines
8.8 KiB
Python
Raw Normal View History

from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, render
from django.views.decorators.http import require_GET, require_POST
from .models import CustomAttribute, EntityFieldConfig, Freigabe
ENTITY_TYPES = None # lazy-loaded to avoid circular imports
def _entity_types():
global ENTITY_TYPES
if ENTITY_TYPES is None:
from vergabe_teilnahme.apps.aufgaben.models import Aufgabe
from vergabe_teilnahme.apps.ausschreibungen.models import Ausschreibung
from vergabe_teilnahme.apps.bibliothek.models import Nachweis, Referenz
from vergabe_teilnahme.apps.lose.models import Anforderung, Los
from vergabe_teilnahme.apps.partner.models import Subunternehmer
ENTITY_TYPES = {
'ausschreibung': Ausschreibung,
'los': Los,
'anforderung': Anforderung,
'aufgabe': Aufgabe,
'subunternehmer': Subunternehmer,
'nachweis': Nachweis,
'referenz': Referenz,
}
return ENTITY_TYPES
2026-05-08 14:26:48 +02:00
def custom_404(request, exception=None):
return render(request, 'errors/404.html', status=404)
def custom_500(request):
return render(request, 'errors/500.html', status=500)
# ── Freigaben ─────────────────────────────────────────────────────────────────
@require_GET
def freigabe_modal(request):
ctx = {
'content_type_id': request.GET.get('ct', ''),
'object_id': request.GET.get('oid', ''),
'freigabe_typ': request.GET.get('typ', ''),
'freigabe_typ_choices': Freigabe.TYP_CHOICES,
}
return render(request, 'partials/freigabe_modal.html', ctx)
@require_POST
def freigabe_erteilen(request):
ct_id = request.POST.get('content_type_id')
object_id = request.POST.get('object_id')
freigabe_typ = request.POST.get('freigabe_typ')
if not (ct_id and object_id and freigabe_typ):
return HttpResponseBadRequest()
ct = get_object_or_404(ContentType, pk=ct_id)
Freigabe.objects.create(
content_type=ct,
object_id=object_id,
freigabe_typ=freigabe_typ,
freigebende_person=request.user,
status='erteilt',
kommentar=request.POST.get('kommentar', ''),
)
return render(request, 'partials/freigabe_success.html', {})
# ── EntityFieldConfig ─────────────────────────────────────────────────────────
def feld_konfiguration_liste(request, entity_type):
types = _entity_types()
if entity_type not in types:
from django.http import Http404
raise Http404
model = types[entity_type]
raw_felder = [f for f in model._meta.get_fields()
if hasattr(f, 'column') and not f.name.startswith('_')]
konfigurationen = {
cfg.field_name: cfg
for cfg in EntityFieldConfig.objects.filter(entity_type=entity_type)
}
felder = [
{'feld': f, 'cfg': konfigurationen.get(f.name)}
for f in raw_felder
]
return render(request, 'core/feld_konfiguration.html', {
'entity_type': entity_type,
'entity_types': list(_entity_types().keys()),
'felder': felder,
})
@require_POST
def feld_konfiguration_toggle(request, entity_type, field_name):
cfg, _ = EntityFieldConfig.objects.get_or_create(
entity_type=entity_type, field_name=field_name
)
cfg.is_hidden = request.POST.get('is_hidden') == 'true'
cfg.display_label = request.POST.get('display_label', '')
cfg.save()
return render(request, 'core/partials/feld_zeile.html', {
'cfg': cfg,
'field_name': field_name,
'entity_type': entity_type,
})
# ── CustomAttributes ──────────────────────────────────────────────────────────
def custom_attributes_panel(request, content_type_id, object_id):
ct = get_object_or_404(ContentType, pk=content_type_id)
attrs = CustomAttribute.objects.filter(content_type=ct, object_id=object_id)
return render(request, 'core/partials/custom_attributes.html', {
'attrs': attrs,
'ct_id': content_type_id,
'oid': object_id,
})
@require_POST
def custom_attribute_neu(request, content_type_id, object_id):
from django.utils.text import slugify
ct = get_object_or_404(ContentType, pk=content_type_id)
label = request.POST.get('label', '').strip()
if label:
CustomAttribute.objects.create(
content_type=ct,
object_id=object_id,
key=slugify(label),
label=label,
value=request.POST.get('value', ''),
data_type=request.POST.get('data_type', 'text'),
)
attrs = CustomAttribute.objects.filter(content_type=ct, object_id=object_id)
return render(request, 'core/partials/custom_attributes.html', {
'attrs': attrs,
'ct_id': content_type_id,
'oid': object_id,
})
@require_POST
def custom_attribute_bearbeiten(request, content_type_id, object_id, attr_pk):
attr = get_object_or_404(CustomAttribute, pk=attr_pk, object_id=object_id)
attr.label = request.POST.get('label', attr.label).strip() or attr.label
attr.value = request.POST.get('value', attr.value)
attr.data_type = request.POST.get('data_type', attr.data_type)
attr.save()
ct = ContentType.objects.get(pk=content_type_id)
attrs = CustomAttribute.objects.filter(content_type=ct, object_id=object_id)
return render(request, 'core/partials/custom_attributes.html', {
'attrs': attrs,
'ct_id': content_type_id,
'oid': object_id,
})
@require_POST
def custom_attribute_loeschen(request, content_type_id, object_id, attr_pk):
attr = get_object_or_404(CustomAttribute, pk=attr_pk, object_id=object_id)
attr.delete()
ct = ContentType.objects.get(pk=content_type_id)
attrs = CustomAttribute.objects.filter(content_type=ct, object_id=object_id)
return render(request, 'core/partials/custom_attributes.html', {
'attrs': attrs,
'ct_id': content_type_id,
'oid': object_id,
})
@require_POST
def custom_attribute_sort(request, content_type_id, object_id, attr_pk):
direction = request.POST.get('direction', 'down')
attr = get_object_or_404(CustomAttribute, pk=attr_pk, object_id=object_id)
ct = ContentType.objects.get(pk=content_type_id)
attrs = list(CustomAttribute.objects.filter(content_type=ct, object_id=object_id))
idx = next((i for i, a in enumerate(attrs) if a.pk == attr.pk), None)
if idx is not None:
swap = idx - 1 if direction == 'up' else idx + 1
if 0 <= swap < len(attrs):
old, new = attrs[swap].sort_order, attrs[idx].sort_order
attrs[idx].sort_order, attrs[swap].sort_order = old, new
attrs[idx].save(update_fields=['sort_order'])
attrs[swap].save(update_fields=['sort_order'])
attrs = CustomAttribute.objects.filter(content_type=ct, object_id=object_id)
return render(request, 'core/partials/custom_attributes.html', {
'attrs': attrs,
'ct_id': content_type_id,
'oid': object_id,
})
# ── Globale Suche ─────────────────────────────────────────────────────────────
2026-05-08 14:26:48 +02:00
@require_GET
def suche(request):
q = request.GET.get('q', '').strip()
if not q or len(q) < 2:
return HttpResponse('')
from vergabe_teilnahme.apps.ausschreibungen.models import Ausschreibung
from vergabe_teilnahme.apps.bibliothek.models import Nachweis, Referenz
from vergabe_teilnahme.apps.dokumente.models import Dokument
from vergabe_teilnahme.apps.marktbegleiter.models import Marktbegleiter
2026-05-08 14:26:48 +02:00
ausschreibungen = Ausschreibung.objects.filter(titel__icontains=q)[:5]
dokumente = Dokument.objects.filter(dateiname__icontains=q).select_related('ausschreibung')[:5]
nachweise = Nachweis.objects.filter(titel__icontains=q)[:5]
referenzen = Referenz.objects.filter(referenztitel__icontains=q)[:5]
marktbegleiter = Marktbegleiter.objects.filter(name__icontains=q)[:5]
has_results = any([ausschreibungen, dokumente, nachweise, referenzen, marktbegleiter])
if not has_results:
return render(request, 'partials/search_results.html', {'q': q, 'empty': True})
return render(request, 'partials/search_results.html', {
'q': q,
'ausschreibungen': ausschreibungen,
'dokumente': dokumente,
'nachweise': nachweise,
'referenzen': referenzen,
'marktbegleiter': marktbegleiter,
})