From b00cf62d870b751dbff6994aafb3128f5633df8f Mon Sep 17 00:00:00 2001 From: Jan Remmer Siebels Date: Wed, 24 Sep 2025 22:13:27 +0200 Subject: [PATCH] Add IBAN and Verwendungszweck columns to support list and fix chart growing bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enhanced 'Alle Unterstützungen' view with IBAN and Verwendungszweck columns for better payment tracking - Updated export functions to handle both legacy 'selected_fields' and new 'fields' parameters - Added IBAN and Verwendungszweck to default export field selections - Improved destinataer list UI by adding Status column and removing obsolete study proof field - Fixed infinite growing animation bug in 'Größen der Grundstücke (Top 30)' chart by replacing Chart.js with CSS-based implementation - Removed Bootstrap h-100 class conflicts that caused chart resize loops --- app/stiftung/views.py | 51 ++++++++---- app/templates/stiftung/destinataer_list.html | 8 +- app/templates/stiftung/land_list.html | 78 +++++++++++-------- .../stiftung/unterstuetzungen_all.html | 22 +++++- 4 files changed, 106 insertions(+), 53 deletions(-) diff --git a/app/stiftung/views.py b/app/stiftung/views.py index 074415e..da2356e 100644 --- a/app/stiftung/views.py +++ b/app/stiftung/views.py @@ -4474,12 +4474,23 @@ def export_unterstuetzungen_csv(request, queryset, selected_ids=None): if selected_ids: queryset = queryset.filter(id__in=selected_ids) - # Get selected fields from request (default to all if none specified) - selected_fields_param = ( - request.POST.get("selected_fields", "") - if request.method == "POST" - else request.GET.get("selected_fields", "") - ) + # Get selected fields from request (handle both 'fields' and 'selected_fields' parameter names) + selected_fields_param = "" + if request.method == "POST": + # Try 'fields' first (new format), then 'selected_fields' (legacy) + fields_list = request.POST.getlist("fields") + if fields_list: + selected_fields_param = ",".join(fields_list) + else: + selected_fields_param = request.POST.get("selected_fields", "") + else: + # Try 'fields' first (new format), then 'selected_fields' (legacy) + fields_list = request.GET.getlist("fields") + if fields_list: + selected_fields_param = ",".join(fields_list) + else: + selected_fields_param = request.GET.get("selected_fields", "") + selected_fields = selected_fields_param.split(",") if selected_fields_param else [] if not selected_fields: @@ -4488,8 +4499,9 @@ def export_unterstuetzungen_csv(request, queryset, selected_ids=None): "destinataer_name", "betrag", "faellig_am", - "status", "empfaenger_iban", + "verwendungszweck", + "status", "empfaenger_name", "beschreibung", ] @@ -4671,12 +4683,23 @@ def export_unterstuetzungen_pdf(request, queryset, selected_ids=None): if selected_ids: queryset = queryset.filter(id__in=selected_ids) - # Get selected fields from request (default to key fields if none specified) - selected_fields_param = ( - request.POST.get("selected_fields", "") - if request.method == "POST" - else request.GET.get("selected_fields", "") - ) + # Get selected fields from request (handle both 'fields' and 'selected_fields' parameter names) + selected_fields_param = "" + if request.method == "POST": + # Try 'fields' first (new format), then 'selected_fields' (legacy) + fields_list = request.POST.getlist("fields") + if fields_list: + selected_fields_param = ",".join(fields_list) + else: + selected_fields_param = request.POST.get("selected_fields", "") + else: + # Try 'fields' first (new format), then 'selected_fields' (legacy) + fields_list = request.GET.getlist("fields") + if fields_list: + selected_fields_param = ",".join(fields_list) + else: + selected_fields_param = request.GET.get("selected_fields", "") + selected_fields = selected_fields_param.split(",") if selected_fields_param else [] if not selected_fields: @@ -4685,6 +4708,8 @@ def export_unterstuetzungen_pdf(request, queryset, selected_ids=None): "destinataer_name", "betrag", "faellig_am", + "empfaenger_iban", + "verwendungszweck", "status", "beschreibung", "ausgezahlt_am", diff --git a/app/templates/stiftung/destinataer_list.html b/app/templates/stiftung/destinataer_list.html index d496094..ef27fd0 100644 --- a/app/templates/stiftung/destinataer_list.html +++ b/app/templates/stiftung/destinataer_list.html @@ -100,7 +100,7 @@ Vierteljährlicher Betrag - Letzter Studiennachweis + Status Unterstützung bestätigt @@ -135,10 +135,10 @@ {% endif %} - {% if destinataer.letzter_studiennachweis %} - {{ destinataer.letzter_studiennachweis|date:"d.m.Y" }} + {% if destinataer.aktiv %} + Aktiv {% else %} - - + Inaktiv {% endif %} diff --git a/app/templates/stiftung/land_list.html b/app/templates/stiftung/land_list.html index 3b6c549..952748b 100644 --- a/app/templates/stiftung/land_list.html +++ b/app/templates/stiftung/land_list.html @@ -99,14 +99,18 @@
-
+
Größen der Grundstücke (Top 30)
- +
+
+ +
+
@@ -395,42 +399,50 @@ console.log('usageChart canvas not found'); } - // Bar chart for sizes - const ctx = document.getElementById('sizesChart'); - if (ctx) { - console.log('Found sizesChart canvas'); + // Simple CSS bar chart for sizes (no Chart.js) + const chartContainer = document.getElementById('sizesChart'); + if (chartContainer) { + console.log('Found sizesChart container'); const labels = JSON.parse('{{ size_chart_labels_json|escapejs }}'); const dataVals = JSON.parse('{{ size_chart_values_json|escapejs }}'); console.log('Bar chart data:', {labels: labels.length, values: dataVals.length}); - new Chart(ctx, { - type: 'bar', - data: { - labels: labels, - datasets: [{ - label: 'Größe (qm)', - data: dataVals, - backgroundColor: 'rgba(0, 104, 55, 0.6)', - borderColor: '#006837', - borderWidth: 2 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - scales: { - x: { ticks: { autoSkip: true, maxTicksLimit: 10 } }, - y: { beginAtZero: true } - }, - plugins: { - legend: { display: false }, - tooltip: { callbacks: { label: function(ctx) { return ctx.parsed.y.toLocaleString() + ' qm'; } } } - } - } - }); - console.log('Bar chart created successfully'); + if (dataVals.length > 0) { + const maxValue = Math.max(...dataVals); + + chartContainer.innerHTML = ''; // Clear container + + // Create bars + dataVals.forEach((value, index) => { + const barHeight = (value / maxValue) * 120; // Max height 120px + const bar = document.createElement('div'); + bar.style.cssText = ` + background-color: rgba(0, 104, 55, 0.8); + border: 1px solid #006837; + width: ${Math.max(100 / dataVals.length - 1, 8)}%; + height: ${barHeight}px; + min-height: 2px; + display: flex; + align-items: end; + justify-content: center; + font-size: 10px; + color: white; + text-shadow: 1px 1px 1px rgba(0,0,0,0.5); + cursor: pointer; + `; + + // Add tooltip on hover + bar.title = `${labels[index] || 'N/A'}: ${value.toLocaleString()} qm`; + + chartContainer.appendChild(bar); + }); + + console.log('CSS bar chart created successfully'); + } else { + chartContainer.innerHTML = '
Keine Daten verfügbar
'; + } } else { - console.log('sizesChart canvas not found'); + console.log('sizesChart container not found'); } } catch (e) { console.error('Chart initialization error:', e); diff --git a/app/templates/stiftung/unterstuetzungen_all.html b/app/templates/stiftung/unterstuetzungen_all.html index dd9eae0..5d470d2 100644 --- a/app/templates/stiftung/unterstuetzungen_all.html +++ b/app/templates/stiftung/unterstuetzungen_all.html @@ -117,7 +117,7 @@
- +
@@ -275,6 +275,8 @@ Destinatär Betrag Fällig am + IBAN + Verwendungszweck Konto Status Beschreibung @@ -296,6 +298,20 @@ €{{ unterstuetzung.betrag|floatformat:2 }} {{ unterstuetzung.faellig_am|date:"d.m.Y" }} + + {% if unterstuetzung.empfaenger_iban %} + {{ unterstuetzung.empfaenger_iban }} + {% else %} + - + {% endif %} + + + {% if unterstuetzung.verwendungszweck %} + {{ unterstuetzung.verwendungszweck|truncatechars:30 }} + {% else %} + - + {% endif %} + {{ unterstuetzung.konto }} @@ -353,8 +369,8 @@ function selectDefaultFields() { // Check default fields const defaultFields = [ - 'destinataer_name', 'betrag', 'faellig_am', 'status', - 'empfaenger_iban', 'empfaenger_name', 'beschreibung' + 'destinataer_name', 'betrag', 'faellig_am', 'empfaenger_iban', 'verwendungszweck', + 'status', 'empfaenger_name', 'beschreibung' ]; defaultFields.forEach(fieldName => {