Neue Bestellübersicht erstellt. /bestellungen/summary

This commit is contained in:
2026-06-10 10:43:53 +02:00
parent 5d6235679a
commit 0a6ecda7a2
5 changed files with 227 additions and 1 deletions
BIN
View File
Binary file not shown.
@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Bestell-Zusammenfassung</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<!-- PAGER & DATUM -->
<div class="card shadow mb-4">
<div class="card-body text-center">
<h2 class="mb-3">Bestell-Check für den <span class="text-success">{{ target_date|date:"d.m.Y" }}</span></h2>
<div class="btn-group">
<a href="?datum={{ prev_date }}" class="btn btn-outline-secondary {% if not prev_date %}disabled{% endif %}">&laquo; Vorheriger Tag</a>
<a href="?" class="btn btn-primary">Heute</a>
<a href="?datum={{ next_date }}" class="btn btn-outline-secondary {% if not next_date %}disabled{% endif %}">Nächster Tag &raquo;</a>
</div>
</div>
</div>
<!-- DASHBOARD CARDS -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card bg-white shadow border-start border-primary border-5">
<div class="card-body">
<h6 class="text-muted">Gesamtanzahl Bestellungen</h6>
<h3 class="mb-0">{{ total_bestellungen }}</h3>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card bg-white shadow border-start border-success border-5">
<div class="card-body">
<h6 class="text-muted">Erwarteter Umsatz</h6>
<h3 class="mb-0">{{ total_umsatz|floatformat:2 }} €</h3>
</div>
</div>
</div>
</div>
<!-- DETAILS TABELLE -->
<div class="card shadow">
<div class="card-header bg-dark text-white">Details pro Gericht</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Gericht</th>
<th>Kategorie</th>
<th class="text-center">Menge</th>
<th class="text-end">Umsatz</th>
</tr>
</thead>
<tbody>
{% for stat in summary_stats %}
<tr>
<td><strong>{{ stat.menue__gericht__name }}</strong></td>
<td><span class="badge bg-info text-dark">{{ stat.menue__gericht__kategorie__name }}</span></td>
<td class="text-center">{{ stat.anzahl }}</td>
<td class="text-end">{{ stat.umsatz|floatformat:2 }} €</td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center py-4 text-muted">Keine Bestellungen für diesen Tag vorhanden.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="mt-3 text-end">
<a href="{% url 'speisekarte' %}" class="btn btn-link text-decoration-none">&larr; Zurück zur Speisekarte</a>
</div>
</div>
</body>
</html>
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mensa Speiseplan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<!-- PAGER & DATUM ANZEIGE -->
<div class="card shadow mb-4">
<div class="card-body text-center">
<h2 class="mb-3">Speiseplan für den <br><span class="text-primary">{{ target_date|date:"d.m.Y" }}</span></h2>
<div class="btn-group" role="group" aria-label="Datum Navigation">
<!-- Button Zurück -->
{% if prev_date %}
<a href="?datum={{ prev_date }}" class="btn btn-outline-secondary">&laquo; Vorheriger Tag</a>
{% else %}
<button class="btn btn-outline-light text-muted" disabled>&laquo; Vorheriger</button>
{% endif %}
<!-- Button Heute (Reset) -->
<a href="?" class="btn btn-primary">Heute</a>
<!-- Button Weiter -->
{% if next_date %}
<a href="?datum={{ next_date }}" class="btn btn-outline-secondary">Nächster Tag &raquo;</a>
{% else %}
<button class="btn btn-outline-light text-muted" disabled>Nächster &raquo;</button>
{% endif %}
</div>
</div>
</div>
<!-- TABELLE DER TAGESMENÜS -->
<div class="card shadow mb-4">
<div class="card-header bg-dark text-white">Tagesmenüs</div>
<div class="card-body p-0">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>Gericht</th>
<th>Kategorie</th>
<th>Preis</th>
</tr>
</thead>
<tbody>
{% for menue in menues_day %}
<tr>
<td>{{ menue.gericht.name }}</td>
<td><span class="badge bg-info text-dark">{{ menue.gericht.kategorie.name }}</span></td>
<td>{{ menue.preis|floatformat:2 }} €</td>
</tr>
{% empty %}
<tr>
<td colspan="3" class="text-center py-3 text-muted">Keine Tagesmenüs für diesen Tag geplant.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- TABELLE DER DAUERANGEBOTE -->
<div class="card shadow">
<div class="card-header bg-success text-white">Immer verfügbar (Dauerangebote)</div>
<div class="card-body p-0">
<ul class="list-group list-group-flush">
{% for gericht in dauerangebote %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ gericht.name }}
<span class="badge bg-success rounded-pill">Verfügbar</span>
</li>
{% empty %}
<li class="list-group-item text-muted">Momentan keine Dauerangebote.</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
+3 -1
View File
@@ -1,8 +1,10 @@
from django.urls import path
from .views import GerichtListView
from .views import SpeiseplanView # Achte auf den neuen Klassennamen!
from .views import BestellSummaryView
urlpatterns = [
path('speisekarte/', GerichtListView.as_view(), name='gericht_list'),
path('speisekarte/', GerichtListView.as_view(), name='speisekarte'),
path('speiseplan/', SpeiseplanView.as_view(), name='speiseplan'),
path('bestellungen/summary/', BestellSummaryView.as_view(), name='bestell_summary'),
]
+51
View File
@@ -48,3 +48,54 @@ class SpeiseplanView(TemplateView):
context['next_date'] = next_tag.datum.strftime('%Y-%m-%d') if next_tag else None
return context
from django.views.generic import TemplateView
from django.utils import timezone
from django.db.models import Count, Sum
from datetime import datetime
from .models import Bestellung, SpeiseplanTag, Gericht, Menue
class BestellSummaryView(TemplateView):
template_name = 'mensa_app/bestell_summary.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 1. Datum aus der URL holen (wie beim Speiseplan)
date_str = self.request.GET. get('datum')
if date_str:
try:
target_date = datetime.strptime(date_str, '%Y-%m-%d').date()
except ValueError:
target_date = timezone.now().date()
else:
target_date = timezone.now().date()
context['target_date'] = target_date
# 2. Aggregation: Bestellungen nach Gericht gruppieren
# Wir suchen alle Bestellungen, deren Menü am target_date stattfindet
summary_stats = (
Bestellung.objects.filter(menue__tag__datum=target_date)
.values('menue__gericht__name', 'menue__gericht__kategorie__name') # Gruppierung nach Name & Kategorie
.annotate(
anzahl=Count('id'), # Wie viele wurden bestellt?
umsatz=Sum('menue__preis') # Was macht das für einen Umsatz?
)
.order_by('menue__gericht__name')
)
context['summary_stats'] = summary_stats
# 3. Pager-Logik (identisch mit dem Speiseplan-View)
prev_tag = SpeiseplanTag.objects.filter(datum__lt=target_date).order_by('-datum').first()
next_tag = SpeiseplanTag.objects.filter(datum__gt=target_date).order_by('datum').first()
context['prev_date'] = prev_tag.datum.strftime('%Y-%m-%d') if prev_tag else None
context['next_date'] = next_tag.datum.strftime('%Y-%m-%d') if next_tag else None
# 4. Gesamtzahlen für die Übersicht
context['total_bestellungen'] = Bestellung.objects.filter(menue__tag__datum=target_date).count()
context['total_umsatz'] = Bestellung.objects.filter(menue__tag__datum=target_date).aggregate(Sum('menue__preis'))['menue__preis__sum'] or 0
return context